Why do ZonedDateTime and Calendar disagree on the hour in year 2050? [duplicate]
Asked Answered
L

1

9

Consider the following code:

ZoneId zoneId = ZoneId.of("America/Los_Angeles");
long currMillis = 2530778400000L;
Instant curr = Instant.ofEpochMilli(currMillis);
LocalDateTime dt = LocalDateTime.ofInstant(curr, zoneId); //the local one just for completeness
ZonedDateTime zdt = ZonedDateTime.ofInstant(curr, zoneId);
Calendar calendar = GregorianCalendar.from(zdt);

System.out.println(String.format("%-30s %s", "java-8 LocalDateTime hour:", dt.toLocalTime().getHour()));
System.out.println(String.format("%-30s %s", "java-8 ZonedDateTime hour:", zdt.toLocalTime().getHour()));
System.out.println(String.format("%-30s %s", "Calendar hour:", calendar.get(Calendar.HOUR_OF_DAY)));

Printed:

java-8 LocalDateTime hour:     3
java-8 ZonedDateTime hour:     3
Calendar hour:                 2

It seems that around this hour Calendar jumps from hour 2 to hour 4 (not necessarily a problem in general if it corresponds to DST change).

I am using AdoptOpenJDK 1.8.0_242, but I've also checked on HotSpot 1.8.0_181 - the same issue.

Why does Calendar report a different hour from ZonedDateTime?
Is this mismatch a known issue?
Whom should I trust more - ZonedDateTime or Calendar in this case?

Lemur answered 7/2, 2020 at 8:20 Comment(0)
P
8

Assuming that the rules (transition into DST happens on the first Sunday on or after 8 March at 02:00) don't change in 2050, that instant is one where a gap transition occurs (13 March), where the clocks jump from 01:59 to 03:00, so 02:00 doesn't actually exist. Calendar is quite wrong here.

You can further see how wrong Calendar is by checking what each of the timezone classes say about the instant in question. ZonedDateTime uses ZoneId, whereas Calendar uses TimeZone. I compared the outputs of various methods on ZoneId with that of the TimeZone counterparts, using this code:

ZoneId zoneId = ZoneId.of("America/Los_Angeles");
long currMillis = 2530778400000L;
Instant curr = Instant.ofEpochMilli(currMillis);
TimeZone tz = TimeZone.getTimeZone(zoneId);

// what's the actual offset at that instant?
System.out.println(zoneId.getRules().getOffset(curr).getTotalSeconds());
System.out.println(tz.getOffset(currMillis) / 1000);

// is DST observed at that instant?
System.out.println(zoneId.getRules().isDaylightSavings(curr));
System.out.println(tz.inDaylightTime(new Date(currMillis)));

// what's the standard offset at that instant?      
System.out.println(zoneId.getRules().getStandardOffset(curr).getTotalSeconds());
System.out.println(tz.getRawOffset() / 1000);

// how many seconds does DST add to the standard offset at that instant?
System.out.println(zoneId.getRules().getDaylightSavings(curr).getSeconds());
Calendar calendar = GregorianCalendar.from(ZonedDateTime.ofInstant(curr, zoneId));
System.out.println(calendar.get(Calendar.DST_OFFSET) / 1000);

The results are as follows:

-25200
-28800
true
true
-28800
-28800
3600
0

As you can see, both of them think that DST is observed, but TimeZone thinks DST adds 0 seconds to the standard offset, which makes it think that the actual offset is still -8 hours.

But who knows what happens in 30 years? Let's hope everyone gets rid of DST :)

Preventer answered 7/2, 2020 at 9:30 Comment(7)
Great Answer. But regarding your comment about getting rid of DST: as long as there are politicians, there will be changes to time zones. DST is only one reason for zone changes. History has shown other political, military, diplomatic, and cultural reasons for politicians to change their time zones. I strongly recommend all programmers learn to handle time zones in their code, and defensively program to always expect zone changes regardless of whether DST is currently an issue in their juridictions.Illeetvilaine
@BasilBourque Yeah, I know, right? Non-DST changes are much more messy than DST changes. That comment was only meant as a joke...Preventer
I am surprised, though. From 2038 ZoneId says that DST begins at 2 on the 2nd Sunday in March, whereas TimeZone says at 3 AM. I thought that they both relied on the same time zone database. @BasilBourque is correct, of course, it doesn’t matter since no one knows what the politicians end up deciding for 2038 and later anyway.Ordonnance
@OleV.V. I just looked at the tzdata2019c file for northamerica. I see no occurrence of 2038. That you found a different that happens to start in the year 2038 makes me wonder if it might be a programming issue related to the 32-bit Year 2038 problem.Illeetvilaine
@BasilBourque I did notice the coincidence. Meno Hochschield’s comment here seems to tell us that it is more than a coincidence.Ordonnance
@OleV.V. So, yet another reason to avoid the legacy date-time classes?Illeetvilaine
@BasilBourque As if we didn’t have plenty already! Yes it is!Ordonnance

© 2022 - 2025 — McMap. All rights reserved.