简体   繁体   中英

ZonedDateTime & LocalDateTime are different for 100 years ago

The behaviour that I see is very strange - sometimes LocalDateTime would be equal to ZonedDateTime , other times it will differ by 1 hour or 2 and sometimes it's 30 minutes. All these strange differences depend on the year that I subtract. Can someone explain what's happening? Tried jdk1.8.0_65 and jdk1.8.0_91 , MacOS 10.11.5 . I work with UTC:

ZoneOffset offset = ZoneOffset.UTC;

Here are some experiments. For 1919 values may differ by nano or milliseconds, which is expected:

assertEquals(                     
  LocalDateTime.now(offset).minusYears(85).toInstant(offset),                   
  ZonedDateTime.now().minusYears(85).withZoneSameInstant(offset).toInstant());

For 1919 it's 1 hour difference:

assertEquals(                 
  LocalDateTime.now(offset).minusYears(86).toInstant(offset),                    
  ZonedDateTime.now().minusYears(86).withZoneSameInstant(offset).toInstant());

Expected :<1930-05-28T20:19:10.383Z> 
Actual   :<1930-05-28T21:19:10.383Z>

For 1920 it's 2 hours difference:

assertEquals(                    
  LocalDateTime.now(offset).minusYears(95).toInstant(offset),                      
  ZonedDateTime.now().minusYears(95).withZoneSameInstant(offset).toInstant());

Expected :<1921-05-28T20:21:45.094Z> 
Actual   :<1921-05-28T18:21:45.094Z>

For 1921 again milli or nano seconds difference :

assertEquals(                      
  LocalDateTime.now(offset).minusYears(96).toInstant(offset),                    
  ZonedDateTime.now().minusYears(96).withZoneSameInstant(offset).toInstant());

And the weirdest of all - 1930 year with 30 mins difference :

assertEquals(                      
  LocalDateTime.now(offset).minusYears(97).toInstant(offset),                  
  ZonedDateTime.now().minusYears(97).withZoneSameInstant(offset).toInstant());

Expected :<1919-05-28T20:24:27.345Z> 
Actual   :<1919-05-28T19:53:08.346Z>

Update

As @Tunaki pointed I had to specify the offset for the ZonedDateTime :

assertEquals(                   
  LocalDateTime.now(offset).minusYears(95).toInstant(offset),                    
  ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant());

The problem is that this doesn't know about Time Zones: LocalDateTime.now(offset).minusYears(97).toInstant(offset) . There is only offset present. But this knows about the time zone: ZonedDateTime.now().minusYears(97).toInstant()

  • ZoneId contains information about the place and the time difference at that place. It knows that N years ago in that particular time zone the offset was 2 hours, not 3 as it is now.
  • ZoneOffset keeps track only about the hours/minutes shift. It doesn't know the history of time changes in a particular country. It just "adds hours".

The suggested (and a little bit incorrect) solution is to let ZonedDateTime to forget about zones and use offset instead: ZonedDateTime.now(offset).minusYears(97) . This now would agree with LocalDateTime with the same offset - both would show same incorrect information. But they would agree since both "just add hours" instead of understanding the time difference at that point of history for that place:

ZoneOffset offset = ZoneId.of("Europe/Moscow").getRules().getOffset(Instant.now());
assertEquals(
    LocalDateTime.now(offset).minusYears(97).toInstant(offset),           
    ZonedDateTime.now(offset).minusYears(97).toInstant());

Alternatively we can set the LocalDateTime to show the correct value for that time for that place:

ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Europe/Moscow")).minusYears(97);
ZoneOffset offset = zoned.getOffset();//agrees with history
assertEquals(
        LocalDateTime.now().minusYears(97).toInstant(offset),
        zoned.toInstant());

PS: This all shows that ZonedDateTime can work differently in different situations - sometimes it knows about time zones, other times it just "adds hours" as you would do with LocalDateTime by setting the offset manually. To me this is a weird implementation. JodaTime probably is still the best Java implementation. At least you don't have to learn it for several days to understand it.

Since no one is answering this...

You have to send in offset when creating the ZonedDateTime as well. Otherwise it will use Clock.systemDefaultZone() and you get a difference in timezones.

ZonedDateTime.now(offset).minusYears(95).withZoneSameInstant(offset).toInstant()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM