Name: mf23781 Date: 03/17/99
Across the European Union summer-time should end at 1 a.m. GMT.
See http://europa.eu.int/eur-lex/en/lif/en_397L0044.html for the
Community legislation in force.
The current code seems to make summer-time end 1 hour earlier than that.
The code in TimeZone.java and SimpleTimeZone.java seems to indicate that
the European directive 97/44/EC is being followed, but things don't
quite seem to work.
test case summer.java:
------------------------------------------------------------------------------------
import java.util.*;
import java.text.*;
public class summer {
public static void main(String[] args) {
int errors = 0;
TimeZone tGMT = TimeZone.getTimeZone("GMT" /*not "Europe/London"*/);
TimeZone tParis = TimeZone.getTimeZone("ECT" /*"Europe/Paris"*/);
TimeZone tIstanbul = TimeZone.getTimeZone("EET" /*"Europe/Istanbul"*/);
Calendar cGMT = Calendar.getInstance(tGMT, Locale.UK);
Calendar cParis = Calendar.getInstance(tParis, Locale.UK);
Calendar cAthens = Calendar.getInstance(tIstanbul, Locale.UK);
String ss = "dd MMMM yyyy HH:mm:ss.SSS z";
SimpleDateFormat sdf = new SimpleDateFormat(ss);
SimpleDateFormat sdfGMT = new SimpleDateFormat(ss);
sdfGMT.setCalendar(cGMT);
SimpleDateFormat sdfParis = new SimpleDateFormat(ss);
sdfParis.setCalendar(cParis);
SimpleDateFormat sdfAthens = new SimpleDateFormat(ss);
sdfAthens.setCalendar(cAthens);
Date dt;
String s1, s2;
cGMT.clear();
cGMT.set(1999, 3 - 1, 28, 0, 59, 59);
dt =cGMT.getTime();
s1 = "Time in GMT is "+sdfGMT.format(dt);
s2 = "Time in GMT is 28 March 1999 00:59:59.000 GMT+00:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Paris is "+sdfParis.format(dt);
s2 = "Time in Paris is 28 March 1999 01:59:59.000 GMT+01:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Athens is "+sdfAthens.format(dt);
s2 = "Time in Athens is 28 March 1999 02:59:59.000 GMT+02:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
System.out.println();
cGMT.clear();
cGMT.set(1999, 3 - 1, 28, 1, 00, 00);
dt =cGMT.getTime();
s1 = "Time in GMT is "+sdfGMT.format(dt);
s2 = "Time in GMT is 28 March 1999 01:00:00.000 GMT+00:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Paris is "+sdfParis.format(dt);
s2 = "Time in Paris is 28 March 1999 03:00:00.000 GMT+02:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Athens is "+sdfAthens.format(dt);
s2 = "Time in Athens is 28 March 1999 04:00:00.000 GMT+03:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
System.out.println();
cGMT.clear();
cGMT.set(1999, 10 - 1, 31, 0, 59, 59);
dt =cGMT.getTime();
s1 = "Time in GMT is "+sdfGMT.format(dt);
s2 = "Time in GMT is 31 October 1999 00:59:59.000 GMT+00:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Paris is "+sdfParis.format(dt);
s2 = "Time in Paris is 31 October 1999 02:59:59.000 GMT+02:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Athens is "+sdfAthens.format(dt);
s2 = "Time in Athens is 31 October 1999 03:59:59.000 GMT+03:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
System.out.println();
cGMT.clear();
cGMT.set(1999, 10 - 1, 31, 1, 00, 00);
dt =cGMT.getTime();
s1 = "Time in GMT is "+sdfGMT.format(dt);
s2 = "Time in GMT is 31 October 1999 01:00:00.000 GMT+00:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Paris is "+sdfParis.format(dt);
s2 = "Time in Paris is 31 October 1999 02:00:00.000 GMT+01:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
s1 = "Time in Athens is "+sdfAthens.format(dt);
s2 = "Time in Athens is 31 October 1999 03:00:00.000 GMT+02:00";
System.out.println(s1);
if (!s1.equals(s2)) {
System.out.println("Error: expected "+s2);
++errors;
}
System.out.println();
System.out.println("Testing for summer time change-over through the year - please wait");
SimpleDateFormat sdfParis2 = new SimpleDateFormat("z");
sdfParis2.setCalendar(cParis);
cGMT.clear();
cGMT.set(1999, 1 - 1, 1, 0, 0, 0);
dt =cGMT.getTime();
long l1 = dt.getTime();
cGMT.clear();
cGMT.set(2000, 1 - 1, 1, 0, 0, 0);
dt =cGMT.getTime();
long l2 = dt.getTime();
Date dd = new Date(l1);
System.out.println("Time in GMT is "+sdfGMT.format(dd));
System.out.println("Time in Paris is "+sdfParis.format(dd));
String ssref = sdfParis2.format(dd), sx = null;
System.out.println(ssref);
Date pd = null;
for (long l3 = l1; l3 < l2; l3 += 10000) {
dd = new Date(l3);
sx = sdfParis2.format(dd);
if (!sx.equals(ssref)) {
System.out.println();
System.out.println("Time in GMT is "+sdfGMT.format(pd));
System.out.println("Time in Paris is "+sdfParis.format(pd));
System.out.println("Time in GMT is "+sdfGMT.format(dd));
System.out.println("Time in Paris is "+sdfParis.format(dd));
ssref = sx;
}
pd = dd;
}
if (errors > 0) throw new RuntimeException("" + errors + " error(s) detected");
}
}
------------------------------------------------------------------------------------
Output is:-
Time in GMT is 31 October 1999 00:59:59.000 GMT+00:00
Time in Paris is 31 October 1999 01:59:59.000 GMT+01:00
Error: expected Time in Paris is 31 October 1999 02:59:59.000 GMT+02:00
Time in Athens is 31 October 1999 02:59:59.000 GMT+02:00
Error: expected Time in Athens is 31 October 1999 03:59:59.000 GMT+03:00
Time in GMT is 31 October 1999 01:00:00.000 GMT+00:00
Time in Paris is 31 October 1999 02:00:00.000 GMT+01:00
Time in Athens is 31 October 1999 03:00:00.000 GMT+02:00
The code in SimpleTimeZone.java
// Compare the date to the starting and ending rules. For the ending
// rule comparison, we add the dstSavings to the millis passed in to convert
// them from standard to wall time. +1 = date>rule, -1 = date<rule, 0 =
// date==rule.
int startCompare = compareToRule(month, day, dayOfWeek, millis,
startMode, startMonth, startDayOfWeek,
startDay, startTime);
int endCompare = compareToRule(month, day, dayOfWeek, millis + dstSavings,
endMode, endMonth, endDayOfWeek,
endDay, endTime);
This compares wall-clock time (including summer time adjustment for the ending
time) against the endTime of the rule, so the endTime must be held in wall-clock
time.
In TimeZone.java:
new SimpleTimeZone(1 * millisPerHour, "Europe/Paris" /*CE%sT*/,
Calendar.MARCH, -1, Calendar.SUNDAY /*DOW_IN_DOM*/, 2 * millisPerHour,
Calendar.OCTOBER, -1, Calendar.SUNDAY /*DOW_IN_DOM*/, 2 * millisPerHour, 1 * millisPerHour),
// Rule EU 1981 max - Mar lastSun 1:00u 1:00 S
// Rule EU 1996 max - Oct lastSun 1:00u 0 -
so the rule is stated, in the comment, in UTC (the 1:00u) but the offset for ending,
in the actual code, doesn't include the summer-time adjustment of 1 hour in addition
to the timezone offset.
This same mistake has been made for all rules specified as xx:00u
---------------------------------------------------------------------------------------------
Browsing this same code shows some summertime offsets coded as negative numbers. I doubt if
these are correct, because summertime start and end are normally early morning, but also it
is clear that the algorithm in compareToRule assumes a positive offset and could yield an
incorrect result with a negative offset.
The European Union legislation specifies that the 1am GMT change applies only to the EU itself
and not its overseas territories.
(Review ID: 55690)
======================================================================