Search code examples
java-8dstjava-timedate-differencezoneddatetime

ZonedDateTime -> ChronoUnit.HOURS.between(zd1(Hour 2), zd2(Hour 1)) return -2


I am trying to understand why is it that the result of the code is -2, initially I thought it should be -1, but this one really puzzled me

LocalDateTime ld1 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 0);
ZonedDateTime zd1 = ZonedDateTime.of(ld1, ZoneId.of("US/Eastern"));
LocalDateTime ld2 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 1, 0);
ZonedDateTime zd2 = ZonedDateTime.of(ld2, ZoneId.of("US/Eastern"));
long x = ChronoUnit.HOURS.between(zd1, zd2);
System.out.println(x);

output : -2

LocalDateTime ld1 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 3, 0);
ZonedDateTime zd1 = ZonedDateTime.of(ld1, ZoneId.of("US/Eastern"));
LocalDateTime ld2 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 0);
ZonedDateTime zd2 = ZonedDateTime.of(ld2, ZoneId.of("US/Eastern"));
long x = ChronoUnit.HOURS.between(zd1, zd2);
System.out.println(x);

Below are the examples which results works as I expected!

output : -1

LocalDateTime ld1 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 0);
ZonedDateTime zd1 = ZonedDateTime.of(ld1, ZoneId.of("US/Eastern"));
LocalDateTime ld2 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 3, 0);
ZonedDateTime zd2 = ZonedDateTime.of(ld2, ZoneId.of("US/Eastern"));
long x = ChronoUnit.HOURS.between(zd1, zd2);
System.out.println(x);

output : 1

If someone can help me understand this better, really appreciated.


Solution

  • US/Eastern, in IANA database (the source where those names come from) is the same as America/New_York timezone (check this list to check the equivalence - search for US/Eastern).

    And in America/New_York timezone, Daylight Saving Time ended at November 1st 2015: at 2 AM, clocks shift back 1 hour to 1 AM, and offset changed from -04:00 to -05:00.

    If you check the ZonedDateTime's, you'll see the offset change:

    System.out.println(zd1);
    System.out.println(zd2);
    

    The output is:

    2015-11-01T02:00-05:00[US/Eastern]
    2015-11-01T01:00-04:00[US/Eastern]

    If you see the corresponding Instant, you'll see both dates in UTC:

    System.out.println(zd1.toInstant());
    System.out.println(zd2.toInstant());
    

    2015-11-01T07:00:00Z
    2015-11-01T05:00:00Z

    Note that the difference between those dates is 2 hours. You can check this by adding 1 and 2 hours to zd2:

    System.out.println(zd2);
    System.out.println(zd2.plusHours(1));
    System.out.println(zd2.plusHours(2));
    

    The output is:

    2015-11-01T01:00-04:00[US/Eastern]
    2015-11-01T01:00-05:00[US/Eastern]
    2015-11-01T02:00-05:00[US/Eastern]

    So, starting at 1 AM in offset -04:00, 1 hour later you are at 2 AM in offset -04:00, but due to DST change, clocks shift back to 1 AM in offset -05:00. Then, 1 hour later, you're at 2 AM in offset 05:00 (zd1). That's why the difference is 2 hours.

    This doesn't happen in your second test, because both dates are in the same offset (-05:00), after DST change occurred. So, the difference is just 1 hour. You can again check this by printing the dates and the corresponding Instant:

    LocalDateTime ld1 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 2, 0);
    ZonedDateTime zd1 = ZonedDateTime.of(ld1, ZoneId.of("US/Eastern"));
    LocalDateTime ld2 = LocalDateTime.of(2015, Month.NOVEMBER, 1, 3, 0);
    ZonedDateTime zd2 = ZonedDateTime.of(ld2, ZoneId.of("US/Eastern"));
    System.out.println(zd1);
    System.out.println(zd2);
    System.out.println(zd1.toInstant());
    System.out.println(zd2.toInstant());
    

    2015-11-01T02:00-05:00[US/Eastern]
    2015-11-01T03:00-05:00[US/Eastern]
    2015-11-01T07:00:00Z
    2015-11-01T08:00:00Z

    Note that the offset is the same, and by the corresponding UTC Instant we can see that the difference is only 1 hour.