JDK-4958050 : GregorianCalendar changes hour field when adding one day
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2003-11-21
  • Updated: 2004-03-25
  • Resolved: 2003-12-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b32Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Description

Name: erR10175			Date: 11/21/2003


 
  The following method of the class java.util.GregorianCalendar
  
public void add(int field, int amount)

changes the HOUR field when the DAY_OF_YEAR field is incremented/decremented.

The documentation of the method allows to change smaller field which is expected to 
be invariant only if it is out of range:

"Add rule 2. If a smaller field is expected to be invariant, but it is impossible 
for it to be equal to its prior value because of changes in its minimum or maximum 
after field is changed, then its value is adjusted to be as close as possible to
its expected value. A smaller field represents a smaller unit of time. HOUR is 
a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are 
not expected to be invariant. The calendar system determines what fields are expected 
to be invariant."

In the test (see below) the HOUR is invariant when changing DAY_OF_YEAR. So it should 
not change, but there are some dates in time zone "Asia/Novosibirsk" (and some other
time zones, e.g. AST, ART, AGT) where adding 1 day to the DAY_OF_YEAR changes 
the HOUR field.

This bug affects new test in JCK 1.5
   api/javax_xml/datatype/Duration/index.html#NormalizeWith[NormalizeWith001]

The bug is found in jdk1.5.0/beta/b28 on Solaris 2.9, x86 platform
and reproduces on Solaris 2.8, sparc if the user.timezone property is set to one 
of mentioned above.

To reproduce the bug compile and run the following code as shown
in the log below:
------------------------------ test.java
import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.TimeZone;

public class test {

    public static void main(String [] args) {
        System.out.println("current time zone: " + TimeZone.getDefault());
        int daySteps = 1;
        Calendar startTime = new GregorianCalendar(1583, 0, 1);
        
        int h = 0;
        int m = 0;
        int s = 0;
        for (int i = 0; i <= 1000000; ++i) {
            Calendar oldTime = (Calendar)startTime.clone();
            startTime.add(Calendar.DAY_OF_YEAR, daySteps);
            if (h != startTime.get(Calendar.HOUR_OF_DAY)
             || m != startTime.get(Calendar.MINUTE)
             || s != startTime.get(Calendar.SECOND)) {
                System.out.println("\nstartTime = " + timeStr(oldTime));
                System.out.println("adding " + daySteps + " days");
                System.out.println("endTime = " + timeStr(startTime));
                h = startTime.get(Calendar.HOUR_OF_DAY);
                m = startTime.get(Calendar.MINUTE);
                s = startTime.get(Calendar.SECOND);
            }
        }
    }

    static String timeStr(Calendar c) {
        return c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH)+1-Calendar.JANUARY)
             + "-" + c.get(Calendar.DAY_OF_MONTH) 
             + " " + c.get(Calendar.HOUR_OF_DAY)
             + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND);
    }

}
----------------------------------------------------

------------------------------------------------ log
$javac test.java && java -Duser.timezone=Asia/Novosibirsk -cp . -showversion test
java version "1.5.0-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta-b28)
Java HotSpot(TM) Client VM (build 1.5.0-beta-b28, mixed mode)

current time zone: sun.util.calendar.ZoneInfo[id="Asia/Novosibirsk",offset=21600000,dstSavings=3600000,useDaylight=true,transitions=119,lastRule=java.util.SimpleTimeZone[id=Asia/Novosibirsk,offset=21600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=7200000,startTimeMode=1,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=7200000,endTimeMode=1]]

startTime = 1900-1-1 0:0:0
adding 1 days
endTime = 1900-1-1 23:31:40

startTime = 1919-12-13 23:31:40
adding 1 days
endTime = 1919-12-15 0:0:0

startTime = 1930-6-20 0:0:0
adding 1 days
endTime = 1930-6-21 1:0:0

startTime = 1991-3-31 1:0:0
adding 1 days
endTime = 1991-4-1 0:0:0

startTime = 1992-1-19 0:0:0
adding 1 days
endTime = 1992-1-20 1:0:0

startTime = 1993-5-22 1:0:0
adding 1 days
endTime = 1993-5-23 0:0:0
----------------------------------------------------

======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta FIXED IN: tiger-beta INTEGRATED IN: tiger-b32 tiger-beta VERIFIED IN: tiger-beta2
14-06-2004

EVALUATION add() doesn't take care of GMT (raw) offset changes (not DST changes). ###@###.### 2003-11-25 In general, it's impossible for the time fields (HOUR_OF_DAY, MINUTE, etc) to be equal to their prior values because of time zone offset changes (raw offset and/or daylight time). For example, if the date/time is 2003-4-5 (Sat) 2:30:00 local time in the America/Los_Angeles time zone, adding one day would create 2003-4-6 (Sun) 2:30 which doesn't exist due to the daylight saving time. Then, GregorianCalendar adjusts the time to 1:30. -- import java.util.*; public class test2 { public static void main(String[] args) { Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles")); cal.clear(); cal.set(2003, cal.APRIL, 5, 2, 30, 0); System.out.println("before add: " + timeStr(cal)); cal.add(cal.DAY_OF_YEAR, +1); System.out.println(" after add: " + timeStr(cal)); } static String timeStr(Calendar c) { int offset = c.get(c.ZONE_OFFSET) + c.get(c.DST_OFFSET); offset /= 60000; StringBuffer sb = new StringBuffer(); if (offset >= 0) { sb.append('+'); } else { sb.append('-'); offset = -offset; } sb.append(offset); return c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH)+1-Calendar.JANUARY) + "-" + c.get(Calendar.DAY_OF_MONTH) + " " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) + sb.toString(); } } -- Output: $ java -showversion test2 java version "1.3.1" Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1-b24) Java HotSpot(TM) Client VM (build 1.3.1-b24, mixed mode) before add: 2003-4-5 2:30:0-480 after add: 2003-4-6 1:30:0-480 ###@###.### 2003-11-25 After fixing raw offset adjustment, the test program in Description outputs as follows. $ java test Asia/Novosibirsk current time zone: startTime = 1930-6-20 0:0:0+0600; Fri Jun 20 00:00:00 NOVT 1930 adding 1 days endTime = 1930-6-21 1:0:0+0700; Sat Jun 21 01:00:00 NOVT 1930 This is consistent with the zdump command output on Linux (RH AS 2.1). (1930-6-21 00:00:00 NOVT doesn't exist.) > /usr/sbin/zdump -v Asia/Novosibirsk ... Asia/Novosibirsk Fri Jun 20 17:59:59 1930 UTC = Fri Jun 20 23:59:59 1930 NOVT isdst=0 gmtoff=21600 Asia/Novosibirsk Fri Jun 20 18:00:00 1930 UTC = Sat Jun 21 01:00:00 1930 NOVT isdst=0 gmtoff=25200 ... ###@###.### 2003-11-25
25-11-2003