问题
I am migrating an application from jdk 8 to 11 and I can see ZonedDateTime change is behavior about daylight saving time. JDK8
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
output: 2037-05-10T19:15+02:00[Europe/Paris]
JDK11/12
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
2037-05-10T20:15+02:00[Europe/Paris]
Can someone explain to me why did they change this behavior ?
Best regards,
回答1:
It’s a known bug in Java 8: JDK-8066982
I believe that what you are experiencing in Java 8 is really this bug: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition. The bug title doesn’t tell the whole story. The real issue is that in Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME
(which is implicitly used by the one-arg ZonedDateTime.parse
that you use) ignores the offset if a time zone ID is included in the parsed string. This in combination with a time zone database that disagrees with your string about the offset used in Paris in October 2037 causes a moment in time to be parsed that conflicts with the offset in the string.
The bug is fixed in Java 9. So in Java 9, 10 and 11, since the same disagreement about offset is still there, the moment parsed is based on the offset of the string. It is then converted to the time zone from the string using the rules from the time zone database. This causes the offset to be changed from +01:00 to +02:00 and the hour of day correspondingly from 19:15 to 20:15. I agree with Java 9+ that this is the correct behaviour.
Don’t use ZonedDateTime for far-future dates
Your problem is also partly caused by using ZonedDateTime
for a future date. This is only recommended for the very near future where we assume that no zone rules are changed. For a date and time in 2037, you should either use an Instant
if you know the moment in time, or a LocalDateTime
if you know just the date and time of day. Only when the time draws near and you trust that your Java installation has got the last time zone updates, convert to a ZonedDateTime
.
As has been discussed in the comments, we probably don’t know the correct UTC offset for Paris in October 2037 yet. It seems that EU is likely to abandon summer time (DST) from 2021, and as far as I know, the French politicians have not yet decided what the time will be in France after that.
What if we wanted the time of day from the string?
To get the time from the string (19:15), parse into a LocalDateTime
:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
Output is (run on Java 11):
Date and time from string: 2037-05-10T19:15
In case you wanted the full Java 8 behaviour on a later Java version — as I mentioned, it’s not recommended, you shouldn’t use ZonedDateTime
here:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
Time from string in zone from string: 2037-05-10T19:15+02:00[Europe/Paris]
来源:https://stackoverflow.com/questions/56255020/zoneddatetime-change-behavior-jdk-8-11