JDK-7022766 : (cal) calendar.set works unexpectedly for DST
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 6u24
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2011-02-27
  • Updated: 2012-03-20
  • Resolved: 2011-03-01
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) Client VM (build 19.1-b02, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Windows XP Version 2002 SP3

A DESCRIPTION OF THE PROBLEM :
Check this example:

public class TestDST {

    public static void main(String[] args) {
        List<Calendar> c = new ArrayList<Calendar>(3);
        c.add(createCalendar(TimeZone.getTimeZone("Europe/Zagreb")));
        c.add(createCalendar(TimeZone.getTimeZone("Europe/Ljubljana")));
        c.add(createCalendar(TimeZone.getTimeZone("Europe/London")));
        c.add(createCalendar(TimeZone.getTimeZone("UTC")));
        for (Calendar cal : c) {
            cal.setTimeInMillis(1256428800000L);
            System.out.println("\n" + cal.getTimeZone().getID());
            System.out.println("pre="+cal.getTime() + " H="+cal.get(Calendar.HOUR) + " H_OF_DAY="+cal.get(Calendar.HOUR_OF_DAY) + " MIN=" + cal.get(Calendar.MINUTE) + " "+cal.getTimeInMillis());
            //cal.set(Calendar.DST_OFFSET, cal.get(Calendar.DST_OFFSET));
            cal.set(Calendar.MINUTE, 0);
            System.out.println("p0="+cal.getTime() + " H="+cal.get(Calendar.HOUR) + " H_OF_DAY="+cal.get(Calendar.HOUR_OF_DAY) + " MIN=" + cal.get(Calendar.MINUTE) + " "+cal.getTimeInMillis());
        }
    }

    public static Calendar createCalendar(TimeZone timeZone) {
        Calendar cal = Calendar.getInstance(timeZone);
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        return cal;
    }

}


As you see, all times are rounded by HOUR, so MINUTE = 0
If you set VM parameter -Duser.timezone=UTC you see a BUG (or use any other UTC related like example UTC+1).
The method cal.set should set the MINUTE field to ZERO and before that it has allready been a ZERO (practically dummy action), so nothing should change.
But it DOES!!!
Time shift's one HOUR forward for all calendars except the one created with UTC TimeZone.
It will also happen if you try to set SECOND and MILISECOND to 0 (before also allready 0).
Actually Calendar's local_fields don't change, but TimeInMillis DO change.
Strange.
But if you uncomment the line where the DST_OFFSET is set (as it is get, so actually doing nothing) everything works fine for any -Duser.timezone ARGUMENT (also if you don't use VM parameter -Duser.timezone=UTC, no BUG) .

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. run the example => worx fine
2. run the example with VM arg -Duser.timezone=UTC => BUG
2. uncomment (the only commented line) => worx fine

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
1.
all results/times are equal

2.
all results/times should be equal (Why would the VM arg -Duser.timezone=UTC affect)

3.
all results/times are equal
ACTUAL -
1.
all results/times are equal => expected

2.
If Calendar field MINUTE is = 0, I expect setting Calendar field MINUTE again to 0 (practically dummy action) would NOT change HOUR field !!!
Hour field going up by 1 is BUG.
Why does the VM arg -Duser.timezone=UTC affects

3.
Why would setting DST field to an existing value correct this BUG ... ?

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class TestDST {

    public static void main(String[] args) {
        List<Calendar> c = new ArrayList<Calendar>(3);
        c.add(createCalendar(TimeZone.getTimeZone("Europe/Zagreb")));
        c.add(createCalendar(TimeZone.getTimeZone("Europe/Ljubljana")));
        c.add(createCalendar(TimeZone.getTimeZone("Europe/London")));
        c.add(createCalendar(TimeZone.getTimeZone("UTC")));
        for (Calendar cal : c) {
            cal.setTimeInMillis(1256428800000L);
            System.out.println("\n" + cal.getTimeZone().getID());
            System.out.println("pre="+cal.getTime() + " H="+cal.get(Calendar.HOUR) + " H_OF_DAY="+cal.get(Calendar.HOUR_OF_DAY) + " MIN=" + cal.get(Calendar.MINUTE) + " "+cal.getTimeInMillis());
            //cal.set(Calendar.DST_OFFSET, cal.get(Calendar.DST_OFFSET));
            cal.set(Calendar.MINUTE, 0);
            System.out.println("p0="+cal.getTime() + " H="+cal.get(Calendar.HOUR) + " H_OF_DAY="+cal.get(Calendar.HOUR_OF_DAY) + " MIN=" + cal.get(Calendar.MINUTE) + " "+cal.getTimeInMillis());
        }
    }

    public static Calendar createCalendar(TimeZone timeZone) {
        Calendar cal = Calendar.getInstance(timeZone);
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        return cal;
    }

}

---------- END SOURCE ----------

Comments
EVALUATION During the daylight time to standard time transition, there's a duplicate in local wall-clock time. In the sample code, 2:00am (local time in Europe/Zagreb) can be in either daylight time or standard time. The given time stamp 1256428800000L is 2:00am in DST, however, setting any field triggers recalculation of local time from the field values (even thought setting the MINUTE field to the same value), not from the millisecond time stamp 1256428800000L. When there are two possibilities on local 2:00am, Calendar takes the standard time. So, the first local time 2:00am is in DST, and the second one is 2:00am in standard time (after setting the MINUTE field). If you specify daylight time offset (DST_OFFSET field) explicitly, Calendar will take the local time in DST.
01-03-2011