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
----------------------------------------------------
======================================================================