Search code examples
javadatetimedst

Confusing test fail - daylight saving time


On a nightly build of Jenkins, one of our tests failed at 2:00:12am. After some time debugging and changing the systemtime of my computer, I was very confused. Then I wrote the following test (simulating the issue), which fails, but I'm unable to understand why. I tried Google, but found nothing similar.
Can anyone explain why the last assert fails?

@Test
public void testFirstBeforeSecond_atDayLightSavingTime() throws ParseException {
    Date first = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 00:59");
    Date second = new SimpleDateFormat("dd-MM-yyyy HH:mm").parse("25-10-2015 01:01");
    assertThat(first.before(second), is(true)); // Ok, as expected

    first = add(first, Calendar.HOUR_OF_DAY, 2);
    second = add(second, Calendar.HOUR_OF_DAY, 2);
    assertThat(first.before(second), is(true)); // Ok, as expected

    first = add(first, Calendar.DAY_OF_YEAR, 2);
    second = add(second, Calendar.DAY_OF_YEAR, 2);
    assertThat(first.before(second), is(true)); // Fails?
}

private Date add(Date date, int field, int amount) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeZone(TimeZone.getTimeZone("Europe/Brussels"));
    calendar.setTime(date);
    calendar.add(field, amount);
    return calendar.getTime();
}

(In Brussels time zone, daylight saving time ended on 25-10-15 at 3am. The clock then jumped back one hour.)


Solution

  • If you print out first and second at each step of the way, here's what you get:

    Your first matchup is

    Sun Oct 25 00:59:00 CEST 2015
    Sun Oct 25 01:01:00 CEST 2015
    

    which is exactly as expected. Two times, two minutes apart, during Central European Summer Time.

    Second matchup gets interesting. You added two hours to each date:

    Sun Oct 25 02:59:00 CEST 2015
    Sun Oct 25 02:01:00 CET 2015
    

    Now the two times span the DST switchover. First time is during summer time, 2:59; second time is during standard time, at 2:01.

    When you add two days to it, it looks like Java forgets all about summer time:

    Tue Oct 27 02:59:00 CET 2015
    Tue Oct 27 02:01:00 CET 2015
    

    2:59 and 2:01, exactly as it was ... this might be two days later by the calendar, but the first time is certainly not 48 hours later than step two!

    If you change the final sets of additions to

        first = add(first, Calendar.HOUR_OF_DAY, 48);
        second = add(second, Calendar.HOUR_OF_DAY, 48);
    

    then the problem goes away:

    Tue Oct 27 01:59:00 CET 2015
    Tue Oct 27 02:01:00 CET 2015
    

    My guess is that the Java designers had to make some guesses about the expected behaviour of what "N days later" meant when that spans a DST switchover.