Java different timezone offset in same timezone - java

I'm using "Asia/Bangkok" zone id.
That offset is from GMT UTC+07:00.
but when I did followings, then it is not +7:00 when set "01/01/1900 7:00:00.000"
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS");
Date date = dateFormat.parse("01/01/1900 7:00:00.000");
System.out.println(date);
System.out.println(date.getTimezoneOffset());
Date date2 = dateFormat.parse("01/01/1900 6:00:00.000");
System.out.println(date2);
System.out.println(date2.getTimezoneOffset());
The result is
Mon Jan 01 07:00:00 ICT 1900
-402
Mon Jan 01 06:00:00 ICT 1900
-420
I wondered if the offset had changed around 7:00 a.m. on January 1, 1900, so I looked it up on Wikipedia.
https://en.wikipedia.org/wiki/Time_in_Thailand
It was UTC+6:42, but from 1880 to 1920.
I have 3 questions.
Why it happen different time offset between "01/01/1900 7:00:00.000" and "01/01/1900 6:00:00.000"
Where can I see time zone history in Java.
How can I ignore different time offset in same Timezone.
-- additional question --
I understand that I should use LocalDateTime.
What is the best way to ignore offset and convert Date to LocalDateTime?
For example, in the following case, the value of convertedDate2 was converted based on an offset of -402.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy H:mm:ss.SSS");
LocalDateTime originalLdate = LocalDateTime.parse("01/01/1900 7:00:00.000", dateFormatter);
LocalDateTime originalLdate2 = LocalDateTime.parse("01/01/1900 6:00:00.000", dateFormatter);
System.out.println(originalLdate);
System.out.println(originalLdate2);
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS");
Date date = dateFormat.parse("01/01/1900 7:00:00.000");
Date date2 = dateFormat.parse("01/01/1900 6:00:00.000");
LocalDateTime convertedDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime convertedDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(convertedDate);
System.out.println(convertedDate2);
LocalDateTime convertedDate3 = LocalDateTime.parse(dateFormat.format(date), dateFormatter);
LocalDateTime convertedDate4 = LocalDateTime.parse(dateFormat.format(date2), dateFormatter);
System.out.println(convertedDate3);
System.out.println(convertedDate4);
The result is
1900-01-01T07:00
1900-01-01T06:00
1900-01-01T07:00
1900-01-01T05:42:04
1900-01-01T07:00
1900-01-01T06:00
If I convert it once to String and then to LocalDateTime, as in convertedDate3 and convertedDate4,
then I could convert as my expectation, but I wonder this is the most efficient way or not?

Java runtime timezone information for each version is available here
https://www.oracle.com/java/technologies/tzdata-versions.html
Inside the linked file (for a specific version) you can find links to the actual data used
https://www.iana.org/time-zones/repository/releases/tzcode2021a.tar.gz
https://www.iana.org/time-zones/repository/releases/tzdata2021a.tar.gz
https://www.iana.org/time-zones/repository/releases/tzdb-2021a.tar.lz
Inside the tzdata*.tar.gz you can find a file called asia which contains the data for Bangkok as well.
It contains these entries
# Thailand
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Bangkok 6:42:04 - LMT 1880
6:42:04 - BMT 1920 Apr # Bangkok Mean Time
7:00 - +07
Link Asia/Bangkok Asia/Phnom_Penh # Cambodia
Link Asia/Bangkok Asia/Vientiane # Laos
So the -402 timezone should be used for all dates before 1/4/1920, but it seems the implementation is using the -402 offset only from 1/1/1900 0:00:00.000 UTC (from 1/1/1900 6:42:04.000 in your timezone) and until 1/4/1920 in your timezone and -420 otherwise. I am not sure, if that is intended or a bug.
How can I ignore different time offset in same Timezone.
If you are actually using timezones in your application, then you should not ignore them.
However, if you are making an application that is intended to be used just in your local timezone, then you can use a DateTime class without timezone information, such as java.time.LocalDateTime.
Also worth noting: even if these timezones would be correct, the historical dates might still be inaccurate, due to modern time rules being applied for all time (see below). So in the end it depends on what your use case is.
A date-time without a time-zone in the ISO-8601 calendar system. The ISO-8601 calendar system is the modern civil calendar system used today in most of the world. It is equivalent to the proleptic Gregorian calendar system, in which today's rules for leap years are applied for all time. For most applications written today, the ISO-8601 rules are entirely suitable. However, any application that makes use of historical dates, and requires them to be accurate will find the ISO-8601 approach unsuitable.

java.util.Date and java.text.SimpleDateFormat are very old classes. Although they mostly work, they are difficult to use properly, especially where timezones are concerned.
Date.getTimezoneOffset is deprecated. Do not use deprecated methods.
The proper way to work with timezone rules is using the java.time, java.time.zone, and java.time.format packages:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter dateFormatter =
DateTimeFormatter.ofPattern("MM/dd/yyyy H:mm:ss.SSS");
LocalDateTime date =
LocalDateTime.parse("01/01/1900 7:00:00.000", dateFormatter);
System.out.println(date);
System.out.println(zone.getRules().getOffset(date));
LocalDateTime date2 =
LocalDateTime.parse("01/01/1900 6:00:00.000", dateFormatter);
System.out.println(date2);
System.out.println(zone.getRules().getOffset(date2));
The entire history of a timezone is in the ZoneRules:
System.out.println();
zone.getRules().getTransitions().forEach(System.out::println);
System.out.println();
zone.getRules().getTransitionRules().forEach(System.out::println);
You also asked:
What is the best way to ignore offset and convert Date to LocalDateTime?
You can’t. It is not possible to convert a Date to a LocalDateTime without assuming a timezone.
A Date is a wrapper for the number of milliseconds since 1970-01-01 00:00:00 UTC. You cannot generate a LocalDateTime from that without knowing which timezone to apply to that millisecond count. For example, noon Eastern Time in the US is a different number of milliseconds since 1970 than noon Greenwich time.
You may not realize it, but when you use SimpleDateFormat, you are specifying a timezone. Every SimpleDateFormat has a timezone property. Since your code never set that timezone explicitly, your date format used the system’s default timezone.
That is one reason to avoid DateFormat and SimpleDateFormat: the implicit use of the default timezone leads to errors and confusing behavior (though it is predictable behavior). When you use the java.time package and its subpackages, there is no ambiguity, and far less chance of confusion.

Related

How to set Z as timezone in SimpleDateFormat

Code sample:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
System.out.println(dateFormat.getTimeZone());
System.out.println(dateFormat.parse(time));
// dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
I don't want to use the commented section.
Zone should be set based on IST that I am giving in the input string:
String time ="2018-04-06 16:13:00 IST";
Current machine zone is: America/New_York. How should I get zone changes to IST based on z?
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
String time ="2018-04-06 18:40:00 IST";
dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Above is running correctly but I don't want to set zone explicitly. It should be choosen based on IST I am giving in input time string.
"I don't want to set zone explicitly"
Sorry to disappoint you, but that's not possible with SimpleDateFormat. Timezone abbreviations like IST are ambiguous - as already said in the comments, IST is used in many places (AFAIK, in India, Ireland and Israel).
Some of those abbreviations might work sometimes, in specific cases, but usually in arbitrary and undocumented ways, and you can't really rely on that. Quoting the javadoc:
For compatibility with JDK 1.1.x, some other three-letter time zone IDs (such as "PST", "CTT", "AST") are also supported. However, their use is deprecated because the same abbreviation is often used for multiple time zones (for example, "CST" could be U.S. "Central Standard Time" and "China Standard Time"), and the Java platform can then only recognize one of them.
Due to the ambiguous and non-standard characteristics of timezones abbreviations, the only way to solve it with SimpleDateFormat is to set a specific timezone on it.
"It should be set to based on Z"
I'm not really sure what this means, but anyway...
Z is the UTC designator. But if the input contains a timezone short-name such as IST, well, it means that it's not in UTC, so you can't parse it as if it was in UTC.
If you want to output the date with Z, then you need another formatter set to UTC:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
String time = "2018-04-06 18:40:00 IST";
dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
// parse the input
Date date = dateFormat.parse(time);
// output format, use UTC
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX");
outputFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(outputFormat.format(date)); // 2018-04-06 13:10:00Z
Perhaps if you specify exactly the output you're getting (with actual values, some examples of outputs) and what's the expected output, we can help you more.
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss z", Locale.ENGLISH);
String time ="2018-04-06 16:13:00 IST";
ZonedDateTime dateTime = ZonedDateTime.parse(time, formatter);
System.out.println(dateTime.getZone());
On my Java 8 this printed
Asia/Jerusalem
So apparently IST was interpreted as Israel Standard Time. On other computers with other settings you will instead get for instance Europe/Dublin for Irish Summer Time or Asia/Kolkata for India Standard Time. In any case the time zone comes from the abbreviation matching the pattern letter (lowercase) z in the format pattern string, which I suppose was what you meant(?)
If you want to control the choice of time zone in the all too frequent case of ambiguity, you may build your formatter in this way (idea stolen from this answer):
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-dd HH:mm:ss ")
.appendZoneText(TextStyle.SHORT,
Collections.singleton(ZoneId.of("Asia/Kolkata")))
.toFormatter(Locale.ENGLISH);
Now the output is
Asia/Kolkata
I am using and recommending java.time over the long outdated and notoriously troublesome SimpleDateFormat class.
Link: Oracle tutorial: Date Time explaining how to use java.time.

DateTime to date conversion, not the correct value

I try to convert a string into a datetime:
String dateString = "2015-01-14T00:00:00-04:00";
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ");
DateTime dt = df.parseDateTime(dateString);
If I display dt.toDate()
I get: Tue Jan 13 23:00:00 EST 2015
So there is a time problem.
Without the DateTimeFormatter, I get the same issue.
It's getting the correct value - basically 4am UTC, which is midnight in a UTC offset of -04:00 (as per the original text), or 11pm on the previous day for EST (as per the displayed result).
The problem is that you're using java.util.Date.toString(), which always returns the date in the system time zone. Note that a java.util.Date only represents an instant in time - it has no notion of a time zone itself, so its toString() method just uses the system default.
If you want to retain the time zone information (or in this case, the offset from UTC information - you don't have a full time zone) then stick to DateTime instead of converting to Date. Ideally, avoid java.util.Date/java.util.Calendar entirely. Stick to Joda Time and/or java.time.*.

java Date conversion confusion regarding timezones

I'm using Java 6. Our servers are on Eastern Standard Time (GMT-5), and I'm trying to store a value that should be interpreted as such but I'm confused about how to convert it. I have
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("GMT-5"));
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
System.out.println(dateObj.toString());
but right now this prints out "Sat May 18 19:00:00 CDT 2013" (since my local machine is on CDT) but I want the result to be "Sat May 18 24:00:00 CDT 2013". How can i interpret the date string "1368921600000" as an EST date? Thanks, - Dave
1368921600000 is an instant in time, the same instant, everywhere in the world. To convert this to a date and time, you have to specify where in the world you want to know the date/time at that instant. It just so happens that the timestamp is relative to UTC, and is Sun, 19 May 2013 00:00:00 GMT.
If you want the time of this instant (the same instant) somewhere else in the world, you can use the Calendar as you did and extract the individual field values (e.g. HOUR_OF_DAY). If all you care about is getting a text string, you use a DateFormat instance such as SimpleDateFormat:
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String timeOnUSEastCoast = df.format(new Date(1368921600000L));
// will be GMT-5 or GMT-4 depending on DST
In this example, the output would be GMT-4: Sat, 18 May 2013 20:00:00 EDT (not only has the hour changed, but it's still the previous day on the US east coast).
If you want to output the UTC time but just want to pretend it's EST then it's just easier to tell the DateFormat to leave off the time zone field (remove the "z") in the text output and call it whatever you want, but understand that the timestamp value is always UTC.
It is often not necessary to use the Joda Time library to get historically accurate time-zone- and daylight-savings-aware local time mappings, although this is the common go to response for many.
If you have a database of timestamps that require local time conversion, then here are some principles that have served me well:
Store date/times (Instants, in the parlance of Joda; Dates, in the parlance of the Java Calendar API) in UTC time. UTC does not care about DST. It does not care about time zones. UTC simply represents a universally representable moment in time. This step alone can save a lot of date/time headaches.
Database records should contain TimeZone and/or Locale fields so that mapping from UTC can be performed. Think about your data. Every timestamp in your database does not need time zone information associated with it. Instead, associate the time zone data with a part of your data model that provides appropriate granularity. If your app will only ever be localized in one time zone, then you needn't store this infor at all. In my recent project, I created a Locale table that contains the TZ ID for timestamps in my Encounters table. All other timestamps are subordinate to these records, so it made sense to associate it there.
Use the Java API GregorianCalendar to map UTC Dates to local time. And that's all I ever use it for. I almost never use GregorianCalendars to do date arithmetic or other date operations. Here is the paradigm that I've been working with:
public static void main(String[] args) {
m_GregorianCalendar = new GregorianCalendar(TimeZone.getTimeZone(
"America/Chicago"));
Date d = new Date();
String fmt = "dd-MMM-yyyy # HH:mm";
:
:
String myDate = mapToLocalTime(d, fmt, gc);
:
:
}
public String mapToLocalTime(Date utc, String format, GregorianCalendar gc) {
gc.setTime(utc); // this calendar is already timezone aware (constructed
// with time zone id (DST too))
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setCalendar(gc); // formatter uses conventions of calendar
sdf.applyPattern(fmt); // pattern for formatter
return sdf.format(utc);
}
Consider representing timestamps internally in a numeric format (longs, doubles). This greatly simplifies date comparisons and date arithmetic. The only downside is that conversions must be done to format the data into a human recognizable form, but if you use functions for these conversions it need not be a big deal at all.
Use either a new java.util.SimpleDateFormat(format) or java.util.DateFormat.getDateTimeInstance(int,int), then use #setTimeZone(timezone).
When you're printing Date.toString(), the Calendar's time zone is not taken into consideration at all. After you do a cal.getTime(), whatever was set in Calendar is no longer relevant.
What is is TimeZone's default time zone.
So right before printing the date, set the default time zone to the one you want to print in, e.g.:
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
TimeZone gmtZero = TimeZone.getTimeZone("GMT");
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
TimeZone.setDefault(gmtZero);
System.out.println(dateObj.toString());
This will print the date in GMT regardless of your system time zone.
Just remember to bring back the original default TimeZone afterwards!

Why does "12:00" converted to UTC become "11:00"?

I thought 2011-10-23 12:00:00 would remain the same as UTC and that the Converted date would be 2011-10-23 17:00:00.
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dt = formatter.parse("2011-10-23 12:00:00");
LocalDateTime ldt = new DateTime(dt).withZone(DateTimeZone.UTC).toLocalDateTime();
LOGGER.warn("Original date: " + ldt.toDateTime().toDate().toString());
DateTime cvtldt = ldt.toDateTime(DateTimeZone.forID("-05:00"));
LOGGER.warn("Converted date: " + cvtldt.toLocalDateTime().toDateTime().toDate().toString());
I don't understand why the output is minus one hour?
Original date: Sun Oct 23 11:00:00 BST 2011
Converted date: Sun Oct 23 11:00:00 BST 2011
You're using Date.toString() which always uses the local time zone. See how your string contains "BST"?
Ideally, stick to just Joda Time for as much of the time as you can:
Parse with the Joda Time formatters
Don't convert back to Date unless you need to
Don't use Date.toString() if you can possibly avoid it; you have no control over its format.
It's not clear what you're really trying to achieve, but you almost certainly don't want to do this many conversions. For example, you're calling toLocalDateTime() followed by toDateTime() again - which means it's using the system default time zone, after you'd carefully specified UTC in the previous conversion...
Your code contains the following conversions (in this order):
String to Date
Date to DateTime
DateTime to DateTime in UTC
DateTime to LocalDateTime (*)
LocalDateTime to DateTime
DateTime to Date
Date to String
(From the results at *) LocalDateTime to DateTime
DateTime to LocalDateTime
LocalDateTime to DateTime
DateTime to Date
Date to String
What do you think the chances of all those conversions being both necessary and correctly specified are? ;)
Original has changed because conversion to the UTC is done with respect to HOST time zone, so it should change.
Converted had changed, in fact it's the problem of accessing method.
You are getting base time and the modifier is stored in other field.
Try going into debugging mode and you will see that after conversion cvtldt
has toString with modifier.
Regards,
Grzesiek

How to set time to a date object in java

I created a Date object in Java. When I do so, it shows something like: date=Tue Aug 09 00:00:00 IST 2011. As a result, it appears that my Excel file is lesser by one day (27 feb becomes 26 feb and so on) I think it must be because of time. How can I set it to something like 5:30 pm?
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY,17);
cal.set(Calendar.MINUTE,30);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
Date d = cal.getTime();
Also See
Joda time
Calendar doc
Can you show code which you use for setting date object? Anyway< you can use this code for intialisation of date:
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse("2011-01-01 00:00:00")
I should like to contribute the modern answer. This involves using java.time, the modern Java date and time API, and not the old Date nor Calendar except where there’s no way to avoid it.
Your issue is very likely really a timezone issue. When it is Tue Aug 09 00:00:00 IST 2011, in time zones west of IST midnight has not yet been reached. It is still Aug 8. If for example your API for putting the date into Excel expects UTC, the date will be the day before the one you intended. I believe the real and good solution is to produce a date-time of 00:00 UTC (or whatever time zone or offset is expected and used at the other end).
LocalDate yourDate = LocalDate.of(2018, Month.FEBRUARY, 27);
ZonedDateTime utcDateDime = yourDate.atStartOfDay(ZoneOffset.UTC);
System.out.println(utcDateDime);
This prints
2018-02-27T00:00Z
Z means UTC (think of it as offset zero from UTC or Zulu time zone). Better still, of course, if you could pass the LocalDate from the first code line to Excel. It doesn’t include time-of-day, so there is no confusion possible. On the other hand, if you need an old-fashioned Date object for that, convert just before handing the Date on:
Date oldfashionedDate = Date.from(utcDateDime.toInstant());
System.out.println(oldfashionedDate);
On my computer this prints
Tue Feb 27 01:00:00 CET 2018
Don’t be fooled, it is correct. My time zone (Central European Time) is at offset +01:00 from UTC in February (standard time), so 01:00:00 here is equal to 00:00:00 UTC. It’s just Date.toString() grabbing the JVMs time zone and using it for producing the string.
How can I set it to something like 5:30 pm?
To answer your direct question directly, if you have a ZonedDateTime, OffsetDateTime or LocalDateTime, in all of these cases the following will accomplish what you asked for:
yourDateTime = yourDateTime.with(LocalTime.of(17, 30));
If yourDateTime was a LocalDateTime of 2018-02-27T00:00, it will now be 2018-02-27T17:30. Similarly for the other types, only they include offset and time zone too as appropriate.
If you only had a date, as in the first snippet above, you can also add time-of-day information to it:
LocalDate yourDate = LocalDate.of(2018, Month.FEBRUARY, 27);
LocalDateTime dateTime = yourDate.atTime(LocalTime.of(17, 30));
For most purposes you should prefer to add the time-of-day in a specific time zone, though, for example
ZonedDateTime dateTime = yourDate.atTime(LocalTime.of(17, 30))
.atZone(ZoneId.of("Asia/Kolkata"));
This yields 2018-02-27T17:30+05:30[Asia/Kolkata].
Date and Calendar vs java.time
The Date class that you use as well as Calendar and SimpleDateFormat used in the other answers are long outdated, and SimpleDateFormat in particular has proven troublesome. In all cases the modern Java date and time API is so much nicer to work with. Which is why I wanted to provide this answer to an old question that is still being visited.
Link: Oracle Tutorial Date Time, explaining how to use java.time.
If you don't have access to java 8 and the API java.time, here is my simple function to copy the time of one date to another date using the old java.util.Calendar (inspire by Jigar Joshi) :
/**
* Copy only the time of one date to the date of another date.
*/
public static Date copyTimeToDate(Date date, Date time) {
Calendar t = Calendar.getInstance();
t.setTime(time);
Calendar c = Calendar.getInstance();
c.setTime(date);
c.set(Calendar.HOUR_OF_DAY, t.get(Calendar.HOUR_OF_DAY));
c.set(Calendar.MINUTE, t.get(Calendar.MINUTE));
c.set(Calendar.SECOND, t.get(Calendar.SECOND));
c.set(Calendar.MILLISECOND, t.get(Calendar.MILLISECOND));
return c.getTime();
}
Calendar calendar = new Calendar.Builder()
.setDate(2022, Calendar.JUNE, 1)
.setTimeOfDay(0, 0, 0)
.build();
System.out.println(calendar.getTimeInMillis());

Categories

Resources