I found a weird java discrepancy while testing some timezones with the use of ZonedDateTime. I was trying to parse a date before 1970 and saw that the result changes between java versions. The offset for Netherlands in 1932 is +00:19. Does anyone know why this happens? I feel this might be related with the bundling of european timezones in the time zone database project (https://github.com/eggert/tz), but i'm not sure. Is there a way to get the old behavior in java? Like with a setting?
ZonedDateTime zdt = LocalDateTime.parse("1932-10-20T10:19:32.000").atZone(ZoneId.of("Europe/Amsterdam"));
System.out.println(zdt);
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(NANO_OF_SECOND, 3, 3, true)
.appendOffset("+HH:MM", "Z")
.toFormatter();
System.out.println(formatter.format(zdt));
System.out.println(
java.time.zone.ZoneRulesProvider
.getVersions("UTC")
.lastEntry()
.getKey()
);
Result in Temurin (java jdk) 11.0.16 (expected output), last line showing timezone database version:
1932-10-20T10:19:32+00:19:32[Europe/Amsterdam]
1932-10-20T10:19:32.000+00:19
2022a
Result in Temurin 11.0.17:
1932-10-20T10:19:32Z[Europe/Amsterdam]
1932-10-20T10:19:32.000Z
2022c
Edit: Also an issue in JDK 17 starting from 17.0.5:
Temurin 17.0.4:
1932-10-20T10:19:32+00:19:32[Europe/Amsterdam]
1932-10-20T10:19:32.000+00:19
2022a
Temurin 17.0.5:
1932-10-20T10:19:32Z[Europe/Amsterdam]
1932-10-20T10:19:32.000Z
2022c
Disclaimer: I'm a colleague of OP. This issue has half the office baffled.
Helped along by comments by #Sweeper below the question, I think we've found the cause.
Background
The venerable Timezone Database did some housecleaning in 2022, and archived a lot of pre-1970 timezone data in their backzone file for version 2022b. This allowed them to merge, for example, Europe/Brussels and Europe/Amsterdam in the leaner post-1970 database (because after 1970, these are in fact equal).
They also introduced a special option (PACKRATLIST) for building the timezone database with the historic timezones intact.
OpenJDK updated to the new version of the Timezone Database, but chose to use the version without historical data, and subsequently, some developers noticed things breaking.
The relevant OpenJDK bug however currently lists supporting the backzone as won't fix.
Affected JDK's
Java 11 from 11.0.17 onwards.
Java 17 from 17.0.5 onwards.
Possible solutions
Perhaps newer JDK's will fix this after all? Unknown at this moment.
Alternatively, use the newer JDK's, but apply this workaround to install an older version (2022a) of the Timezone Database instead.
Or, rewrite all code that depends on this historical data, possibly by hardcoding the relevant timezone offsets if it is a limited set.
Related
On my local configuration (Eclipse 4.11 (2019.03), java runtime 1.8.0, SDK 1.8.0), when converting a local date & time (provided through separate strings) to a CET ZonedDateTime (using an intermediary LocalDateTime built from those strings), I get the following outputs :
input : "2001-10-26" and "21:32:52" => output : 2001-10-26T21:32:52+02:00[CET]
input : "-2001-10-26" and "21:32:52" => output :-2001-10-26T21:32:52+01:00[CET]
So, we see that the UTC offset is not the same.
Of course, we refer to a moment in time where "UTC" and "UTC offset" had not yet been defined...
However, I guess that java designers have implemented some rules for those cases as java process them anyway.
Could someone give me some enlighten about this ?
I have already seen this interesting post Java 8 - tz database time zones but it stays rather vague.
Thanks for helping me with this !
The timezone rules are provided through the abstract ZoneRulesProvider class. The docs mention the default implementation in the ZoneRulesProvider's class description.
The Java virtual machine has a default provider that provides zone rules for the time-zones defined by IANA Time Zone Database (TZDB)
So all rules are originating from the time zone database maintained by IANA, and a copy of it is shipped with the JVM.
For all timezones, the tz database has rules defined, the transitions of when the time on the clock has changed or will change. This way, one could determine what date and time it was or will be on an arbitrary moment on the timeline.
So on 26 October −20011, the UTC offset was apparently +01:00.
I have to add two things. First, I would believe the data from the tzdb, because its maintainers probably have a better understanding of how timezones and their rules work. Second, as Joachim already mentioned in the comments, timezones are a concept that was invented a century ago, so combining timezoned with years like −2001 makes a little sense.
1 Note that the calendar used by most of the people in the world is the Gregorian calendar. In the year −2001, that calendar wasn't invented yet. The java.time package uses the proleptic Gregorian calendar.
In my application while converting to date object i am always getting one hour behind time . this issue in happening only with moscow time zone.
below is code :
MutableDateTime mdt = new MutableDateTime(time);
mdt.setSecondOfMinute(0);
mdt.setMinuteOfDay(0);
mdt.toDate()
in above code mdt.todate() returning 5/30/2021 23:00 instead of 5/31/2021 00:00.
jdk version : "1.8.0_191"
Edit: Why does "June 6 00:00" after conversion mdt.toDate() become "May 31 23:00"?
Your surprising observation probably comes from an old Joda-Time version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. If Joda-Time “believes” that this is still the case, it sets your MutableDateTime to something like 2021-06-01T00:00:00.000+04:00 with offset +04:00 instead of +03:00. This corresponds to 2021-05-31T20:00Z UTC where the correct point in time would have been 2021-05-31T21:00Z UTC. In other words, it’s an hour too early. Therefore you get a Date that is an hour too early too. Your Java 1.8 “knows” that Moscow is at offset +03:00 these days and therefore prints the time as Mon May 31 23:00:00 MSK 2021.
Solutions include:
Upgrade to a newer version of Joda-Time that has an up-to-date time zone database.
Build your Joda-Time from sources for the version that you are using only with a newer bundled time zone database. This is explained on the Joda-Time home page, see the second link below.
Original answer
Your surprising observation probably comes from an old Java version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. I have reproduced your result on my Java 1.7.0_67 and verified that my Java installation “believes” that Moscow is at offset +04:00 and does not use summer time (DST), as was the case in the mentioned period.
Your Joda-Time seems to be new enough to know that Europe/Moscow is at +03:00 so correctly converts your MutableDateTime to a Date at 00:00 hours on the date in question. Only when you print this Date, Java uses its default time zone, still Europe/Moscow, but its own time zone data, and therefore incorrectly prints the time as 01:00 hours instead of 00:00.
Possible solutions include:
Upgrade to a newer Java version that has up-to-date time zone data.
Fix your current Java installation by upgrading only its time zone database. See Timezone Updater Tool in the second link below.
Setting the time to the start of the day
Edit: you added:
Here MutableDateTime time =new MutableDateTime(new Date().getTime());
To get a Date representing the start of today’s date using Joda-Time:
Date oldfashionedDateObject = LocalDate.now(DateTimeZone.getDefault()).toDate();
System.out.println(oldfashionedDateObject);
Output just now:
Mon May 31 00:00:00 MSK 2021
Original aside: As an aside, the simpler and safer way to set the time to the start of the day is:
mdt = mdt.toDateTime().withTimeAtStartOfDay().toMutableDateTime();
If you need to keep the same MutableDateTime object, instead do:
mdt.setMillis(mdt.toDateTime().withTimeAtStartOfDay().toInstant());
First of all I would be worried that your code may run in a time zone and on a day that in that time zone has a transition at 00:00 so that the first moment of the day is 01:00 or something else. In this case I b believe that your code would throw a surprising exception. Also I find setting individual fields low-level and prefer to set everything in one method call even if it requires further operations to determine the argument to pass to that method.
Links
Time Zone in Moscow, Russia (Moskva).
Joda-Time Updating the time zone data.
Timezone Updater Tool on Oracle’s web site.
Following is a small code snippet in java trying to convert a time in millis to a readable date time format,
Long timeInMillis=1615806808301l; //2021-03-15T16:43:28.301+05:30 IST
String timeZone="Europe/Istanbul";
MutableDateTime mdateTime = new MutableDateTime(timeInMills);
mdateTime.setZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone(timeZone)));
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timeInMills), ZoneId.of(timeZone));
Following are the results given by the Joda[mdateTime] and Java.time[zdt] for the same timeInmillis and timezone,
Europe/Istanbul
2021-03-15T13:13:28.301+02:00[Europe/Istanbul]
2021-03-15T14:13:28.301+03:00[Europe/Istanbul]
Turkey
2021-03-15T13:13:28.301+02:00
2021-03-15T14:13:28.301+03:00[Turkey]
Europe/Moscow
2021-03-15T15:13:28.301+04:00
2021-03-15T14:13:28.301+03:00[Europe/Moscow]
Europe/Minsk
2021-03-15T14:13:28.301+03:00
2021-03-15T14:13:28.301+03:00[Europe/Minsk]
As you can see, For Some Timezones, the results are different,
PS: My actual Intention is not to convert the timeInmillis to a readable date time format, but to understand why the results are different.
PS: The System Timezone was IST[+05:30]
Based on the results you are showing for Joda-Time, you likely are using a very old version.
Time zones change at the whim of governments. It's very important to always use the latest version and to stay on top of updates.
Upgrade to the current version of Joda-Time (2.10.10 at the time of writing this) and the discrepancy you reported should go away.
I am working with timezones in a Java application using JodaTime. I encounter a problem when trying to build a DateTimeZone (JodaTime) object from the id of a java timezone. Joda throws a
java.lang.IllegalArgumentException: The datetime zone id 'SystemV/HST10' is not recognised
for the folowing list of timezones:
SystemV/HST10
SystemV/YST9
SystemV/YST9YDT
SystemV/PST8
SystemV/PST8PDT
SystemV/MST7
SystemV/MST7MDT
SystemV/CST6
SystemV/CST6CDT
SystemV/EST5
SystemV/EST5EDT
SystemV/AST4
SystemV/AST4ADT
What are these timezones used for? Are they relevant to non-programmers? Should an application designed for general uses support these timezones?
Thanks.
The SystemV time-zone IDs are old and deprecated. However, you can make Joda-Time understand them by re-compiling the joda-time jar file with the systemv time-zone data file included. See the commented out lines in the systemv data file. (ie. uncomment the lines and rebuild the jar file).
I'll add this as a new post, as it provides the answers to my question. SystemV timezones were used in an old UNIX OS, that was named, you guessed it, UNIX SYSTEM V. After discussing with my team, we decided that they are of no importance to non-programers and even to programmers nowadays. So we decided not to use them in our application.
Some references about the SystemV timezones:
http://en.wikipedia.org/wiki/IANA_time_zone_database#Files_in_tzdata
http://en.wikipedia.org/wiki/System_V
You can simply convert java TimeZone to DateTimeZone, using method DateTimeZone#forTimeZone
TimeZone tz = //...
DateTimeZone dtz = DateTimeZone.forTimeZone(tz);
Some of this zones can be parsed without "SystemV/"
So you can use
String tzId = "SystemV/MST7MDT";
DateTimeZone tz = DateTimeZone.forID(tzId.replaceAll("SystemV/", ""));
Also you can make next
TimeZone tz = TimeZone.getTimeZone("SystemV/MST7MDT");
DateTimeZone jodaTz = DateTimeZone.forTimeZone(tz);
I am trying to parse a date and I am getting different results when I run the code locally/BST compare to a server in Paris/CEST.
I've reproduced the issue in a the following sample. This is trying to parse the start date for the Australian Grand Prix.
TimeZone tz = TimeZone.getTimeZone("AET");
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH mm");
dateFormat.setTimeZone(tz);
long time = dateFormat.parse("28/03/2010 17 00").getTime();
System.out.println("Time "+time);
It seems like I am setting the timezone correctly on the date format and the current timezone shouldn't be affecting the code. But locally it prints 1269756000000 and in Paris 1269759600000. Any idea?
Answer:
It seems like I was testing with an edge case: the Timezone definition is different on my mac compare to the linux server. If I change the timezone to be: "America/Los_Angeles" I am getting a consistent result. The linux box giving me the wrong result is running java 1.6.0-b105 which might be outdated. I'll try an upgrade
Interesting. According to the TimeZone documentation:
Three-letter time zone IDs 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.
It would be interesting to see the results if you use "Australia/Melbourne" instead of "AET", but just from a quick experiment that I did, it doesn't seem like it makes a difference.
It's curious that the results are an hour apart, like Daylight Savings Time isn't being taken into account in one of the cases. Stupid question; if you're running on two separate computers, are you sure the times are set correctly on each?
On my system here, the result is "1269756000000" (like on your local system). I would try to check the server in Paris, especially the settings that concern the timezone:
System.out.println(System.getProperty("user.timezone"));
System.out.println(System.getProperty("user.country"));
Maybe this brings up some differences that helps you to solve this issue.