Why is subtracting these two times (in 1927) giving a strange result?

前端 未结 10 941
既然无缘
既然无缘 2020-11-21 08:10

If I run the following program, which parses two date strings referencing times 1 second apart and compares them:

public static void main(String[] args) throw         


        
相关标签:
10条回答
  • As explained by others, there's a time discontinuity there. There are two possible timezone offsets for 1927-12-31 23:54:08 at Asia/Shanghai, but only one offset for 1927-12-31 23:54:07. So, depending on which offset is used, there's either a one second difference or a 5 minutes and 53 seconds difference.

    This slight shift of offsets, instead of the usual one-hour daylight savings (summer time) we are used to, obscures the problem a bit.

    Note that the 2013a update of the timezone database moved this discontinuity a few seconds earlier, but the effect would still be observable.

    The new java.time package on Java 8 let use see this more clearly, and provide tools to handle it. Given:

    DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
    dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
    dtfb.appendLiteral(' ');
    dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
    DateTimeFormatter dtf = dtfb.toFormatter();
    ZoneId shanghai = ZoneId.of("Asia/Shanghai");
    
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    
    ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
    ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);
    
    Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());
    
    Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());
    

    Then durationAtEarlierOffset will be one second, while durationAtLaterOffset will be five minutes and 53 seconds.

    Also, these two offsets are the same:

    // Both have offsets +08:05:52
    ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
    ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();
    

    But these two are different:

    // +08:05:52
    ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();
    
    // +08:00
    ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();
    

    You can see the same problem comparing 1927-12-31 23:59:59 with 1928-01-01 00:00:00, though, in this case, it is the earlier offset that produces the longer divergence, and it is the earlier date that has two possible offsets.

    Another way to approach this is to check whether there's a transition going on. We can do this like this:

    // Null
    ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
    
    // An overlap transition
    ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
    

    You can check whether the transition is an overlap where there's more than one valid offset for that date/time or a gap where that date/time is not valid for that zone id - by using the isOverlap() and isGap() methods on zot4.

    I hope this helps people handle this sort of issue once Java 8 becomes widely available, or to those using Java 7 who adopt the JSR 310 backport.

    0 讨论(0)
  • 2020-11-21 08:27

    Instead of converting each date, you can use the following code:

    long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
    System.out.println(difference);
    

    And then see that the result is:

    1
    
    0 讨论(0)
  • 2020-11-21 08:29

    You've encountered a local time discontinuity:

    When local standard time was about to reach Sunday, 1. January 1928, 00:00:00 clocks were turned backward 0:05:52 hours to Saturday, 31. December 1927, 23:54:08 local standard time instead

    This is not particularly strange and has happened pretty much everywhere at one time or another as timezones were switched or changed due to political or administrative actions.

    0 讨论(0)
  • 2020-11-21 08:30

    I'm sorry to say, but the time discontinuity has moved a bit in

    JDK 6 two years ago, and in JDK 7 just recently in update 25.

    Lesson to learn: avoid non-UTC times at all costs, except maybe for display.

    0 讨论(0)
  • 2020-11-21 08:31

    The moral of this strangeness is:

    • Use dates and times in UTC wherever possible.
    • If you can not display a date or time in UTC, always indicate the time-zone.
    • If you can not require an input date/time in UTC, require an explicitly indicated time-zone.
    0 讨论(0)
  • 2020-11-21 08:32

    As others said, it's a time change in 1927 in Shanghai.

    It was 23:54:07 in Shanghai, in the local standard time, but then after 5 minutes and 52 seconds, it turned to the next day at 00:00:00, and then local standard time changed back to 23:54:08. So, that's why the difference between the two times is 343 seconds, not 1 second, as you would have expected.

    The time can also mess up in other places like the US. The US has Daylight Saving Time. When the Daylight Saving Time starts the time goes forward 1 hour. But after a while, the Daylight Saving Time ends, and it goes backward 1 hour back to the standard time zone. So sometimes when comparing times in the US the difference is about 3600 seconds not 1 second.

    But there is something different about these two-time changes. The latter changes continuously and the former was just a change. It didn't change back or change again by the same amount.

    It's better to use UTC unless if needed to use non-UTC time like in display.

    0 讨论(0)
提交回复
热议问题