2 *******************************************************************************
\r
3 * Copyright (C) 2000-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.dev.test.timezone;
\r
8 import java.util.Date;
\r
10 import com.ibm.icu.dev.test.TestFmwk;
\r
11 import com.ibm.icu.text.DateFormat;
\r
12 import com.ibm.icu.util.Calendar;
\r
13 import com.ibm.icu.util.SimpleTimeZone;
\r
14 import com.ibm.icu.util.TimeZone;
\r
17 * A test which discovers the boundaries of DST programmatically and verifies
\r
18 * that they are correct.
\r
20 public class TimeZoneBoundaryTest extends TestFmwk
\r
22 static final int ONE_SECOND = 1000;
\r
23 static final int ONE_MINUTE = 60*ONE_SECOND;
\r
24 static final int ONE_HOUR = 60*ONE_MINUTE;
\r
25 static final long ONE_DAY = 24*ONE_HOUR;
\r
26 static final long ONE_YEAR = (long)(365.25 * ONE_DAY);
\r
27 static final long SIX_MONTHS = ONE_YEAR / 2;
\r
29 static final int MONTH_LENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
\r
31 // These values are empirically determined to be correct
\r
32 static final long PST_1997_BEG = 860320800000L;
\r
33 static final long PST_1997_END = 877856400000L;
\r
35 // Minimum interval for binary searches in ms; should be no larger
\r
37 static final long INTERVAL = 10; // Milliseconds
\r
39 // [3Jan01 Liu] Updated for 2000f data
\r
40 static final String AUSTRALIA = "Australia/Adelaide";
\r
41 static final long AUSTRALIA_1997_BEG = 877797000000L;
\r
42 static final long AUSTRALIA_1997_END = 859653000000L;
\r
44 public static void main(String[] args) throws Exception {
\r
45 new TimeZoneBoundaryTest().run(args);
\r
49 * Date.toString().substring() Boundary Test
\r
50 * Look for a DST changeover to occur within 6 months of the given Date.
\r
51 * The initial Date.toString() should yield a string containing the
\r
52 * startMode as a SUBSTRING. The boundary will be tested to be
\r
53 * at the expectedBoundary value.
\r
55 void findDaylightBoundaryUsingDate(Date d, String startMode, long expectedBoundary)
\r
57 // Given a date with a year start, find the Daylight onset
\r
58 // and end. The given date should be 1/1/xx in some year.
\r
60 if (d.toString().indexOf(startMode) == -1)
\r
62 logln("Error: " + startMode + " not present in " + d);
\r
65 // Use a binary search, assuming that we have a Standard
\r
66 // time at the midpoint.
\r
67 long min = d.getTime();
\r
68 long max = min + SIX_MONTHS;
\r
70 while ((max - min) > INTERVAL)
\r
72 long mid = (min + max) >> 1;
\r
73 String s = new Date(mid).toString();
\r
75 if (s.indexOf(startMode) != -1)
\r
85 logln("Date Before: " + showDate(min));
\r
86 logln("Date After: " + showDate(max));
\r
87 long mindelta = expectedBoundary - min;
\r
88 // not used long maxdelta = max - expectedBoundary;
\r
89 if (mindelta >= 0 && mindelta <= INTERVAL &&
\r
90 mindelta >= 0 && mindelta <= INTERVAL)
\r
91 logln("PASS: Expected boundary at " + expectedBoundary);
\r
93 errln("FAIL: Expected boundary at " + expectedBoundary);
\r
96 // This test cannot be compiled until the inDaylightTime() method of GregorianCalendar
\r
98 // static void findDaylightBoundaryUsingCalendar(Date d, boolean startsInDST)
\r
100 // // Given a date with a year start, find the Daylight onset
\r
101 // // and end. The given date should be 1/1/xx in some year.
\r
103 // GregorianCalendar cal = new GregorianCalendar();
\r
105 // if (cal.inDaylightTime() != startsInDST)
\r
107 // logln("Error: inDaylightTime(" + d + ") != " + startsInDST);
\r
110 // // Use a binary search, assuming that we have a Standard
\r
111 // // time at the midpoint.
\r
112 // long min = d.getTime();
\r
113 // long max = min + (long)(365.25 / 2 * 24*60*60*1000);
\r
115 // while ((max - min) > INTERVAL)
\r
117 // long mid = (min + max) >> 1;
\r
118 // cal.setTime(new Date(mid));
\r
119 // if (cal.inDaylightTime() == startsInDST)
\r
129 // logln("Calendar Before: " + showDate(min));
\r
130 // logln("Calendar After: " + showDate(max));
\r
133 void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST, long expectedBoundary)
\r
135 findDaylightBoundaryUsingTimeZone(d, startsInDST, expectedBoundary,
\r
136 TimeZone.getDefault());
\r
139 void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST,
\r
140 long expectedBoundary, TimeZone tz)
\r
142 // Given a date with a year start, find the Daylight onset
\r
143 // and end. The given date should be 1/1/xx in some year.
\r
145 // Use a binary search, assuming that we have a Standard
\r
146 // time at the midpoint.
\r
147 long min = d.getTime();
\r
148 long max = min + SIX_MONTHS;
\r
150 if (tz.inDaylightTime(d) != startsInDST)
\r
152 errln("FAIL: " + tz.getID() + " inDaylightTime(" +
\r
153 d + ") != " + startsInDST);
\r
154 startsInDST = !startsInDST; // Flip over; find the apparent value
\r
157 if (tz.inDaylightTime(new Date(max)) == startsInDST)
\r
159 errln("FAIL: " + tz.getID() + " inDaylightTime(" +
\r
160 (new Date(max)) + ") != " + (!startsInDST));
\r
164 while ((max - min) > INTERVAL)
\r
166 long mid = (min + max) >> 1;
\r
167 boolean isIn = tz.inDaylightTime(new Date(mid));
\r
168 if (isIn == startsInDST)
\r
178 logln(tz.getID() + " Before: " + showDate(min, tz));
\r
179 logln(tz.getID() + " After: " + showDate(max, tz));
\r
181 long mindelta = expectedBoundary - min;
\r
182 // not used long maxdelta = max - expectedBoundary;
\r
183 DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
\r
184 fmt.setTimeZone(tz);
\r
185 if (mindelta >= 0 && mindelta <= INTERVAL &&
\r
186 mindelta >= 0 && mindelta <= INTERVAL)
\r
187 logln("PASS: Expected boundary at " + expectedBoundary + " = " + fmt.format(new Date(expectedBoundary)));
\r
189 errln("FAIL: Expected boundary at " + expectedBoundary + " = " + fmt.format(new Date(expectedBoundary)));
\r
192 private static String showDate(long l)
\r
194 return showDate(new Date(l));
\r
197 private static String showDate(Date d)
\r
199 java.util.Calendar cal = java.util.Calendar.getInstance();
\r
201 return "" + (cal.get(Calendar.YEAR) - 1900) + "/" +
\r
202 showNN(cal.get(Calendar.MONTH) + 1) + "/" +
\r
203 showNN(cal.get(Calendar.DAY_OF_MONTH)) + " " +
\r
204 showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":"
\r
205 + showNN(cal.get(Calendar.MINUTE)) + " \"" + d + "\" = " +
\r
209 private static String showDate(long l, TimeZone z)
\r
211 return showDate(new Date(l), z);
\r
214 private static String showDate(Date d, TimeZone zone)
\r
216 DateFormat fmt = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
\r
217 fmt.setTimeZone(zone);
\r
218 java.util.Calendar cal = java.util.Calendar.getInstance();
\r
220 return "" + (cal.get(Calendar.YEAR) - 1900) + "/" +
\r
221 showNN(cal.get(Calendar.MONTH) + 1) + "/" +
\r
222 showNN(cal.get(Calendar.DAY_OF_MONTH)) + " " +
\r
223 showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":" +
\r
224 showNN(cal.get(Calendar.MINUTE)) + " \"" + d + "\" = " +
\r
225 fmt.format(d) + " = " + d.getTime();
\r
228 private static String showNN(int n)
\r
230 return ((n < 10) ? "0" : "") + n;
\r
234 * Given a date, a TimeZone, and expected values for inDaylightTime,
\r
235 * useDaylightTime, zone and DST offset, verify that this is the case.
\r
237 void verifyDST(String tag, Calendar cal, TimeZone time_zone,
\r
238 boolean expUseDaylightTime, boolean expInDaylightTime,
\r
239 int expRawOffset, int expOffset)
\r
241 Date d = cal.getTime();
\r
243 logln("-- " + tag + ": " + d +
\r
244 " in zone " + time_zone.getID() + " (" +
\r
245 d.getTime()/3600000.0 + ")");
\r
247 if (time_zone.inDaylightTime(d) == expInDaylightTime)
\r
248 logln("PASS: inDaylightTime = " + time_zone.inDaylightTime(d));
\r
250 errln("FAIL: inDaylightTime = " + time_zone.inDaylightTime(d));
\r
252 if (time_zone.useDaylightTime() == expUseDaylightTime)
\r
253 logln("PASS: useDaylightTime = " + time_zone.useDaylightTime());
\r
255 errln("FAIL: useDaylightTime = " + time_zone.useDaylightTime());
\r
257 if (time_zone.getRawOffset() == expRawOffset)
\r
258 logln("PASS: getRawOffset() = " + expRawOffset/(double)ONE_HOUR);
\r
260 errln("FAIL: getRawOffset() = " + time_zone.getRawOffset()/(double)ONE_HOUR +
\r
261 "; expected " + expRawOffset/(double)ONE_HOUR);
\r
263 //GregorianCalendar gc = new GregorianCalendar(time_zone);
\r
265 int offset = time_zone.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
\r
266 cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK),
\r
267 ((cal.get(Calendar.HOUR_OF_DAY) * 60 +
\r
268 cal.get(Calendar.MINUTE)) * 60 +
\r
269 cal.get(Calendar.SECOND)) * 1000 +
\r
270 cal.get(Calendar.MILLISECOND));
\r
271 if (offset == expOffset)
\r
272 logln("PASS: getOffset() = " + offset/(double)ONE_HOUR);
\r
274 logln("era=" + cal.get(Calendar.ERA) +
\r
275 ", year=" + cal.get(Calendar.YEAR) +
\r
276 ", month=" + cal.get(Calendar.MONTH) +
\r
277 ", dom=" + cal.get(Calendar.DAY_OF_MONTH) +
\r
278 ", dow=" + cal.get(Calendar.DAY_OF_WEEK) +
\r
279 ", time-of-day=" + (((cal.get(Calendar.HOUR_OF_DAY) * 60 +
\r
280 cal.get(Calendar.MINUTE)) * 60 +
\r
281 cal.get(Calendar.SECOND)) * 1000 +
\r
282 cal.get(Calendar.MILLISECOND)) / 3600000.0 +
\r
284 errln("FAIL: getOffset() = " + offset/(double)ONE_HOUR +
\r
285 "; expected " + expOffset/(double)ONE_HOUR);
\r
290 * Check that the given year/month/dom/hour maps to and from the
\r
291 * given epochHours. This verifies the functioning of the
\r
292 * calendar and time zone in conjunction with one another,
\r
293 * including the calendar time->fields and fields->time and
\r
294 * the time zone getOffset method.
\r
296 * @param epochHours hours after Jan 1 1970 0:00 GMT.
\r
298 void verifyMapping(Calendar cal, int year, int month, int dom, int hour,
\r
299 double epochHours) {
\r
300 double H = 3600000.0;
\r
302 cal.set(year, month, dom, hour, 0, 0);
\r
303 Date d = cal.getTime();
\r
304 double e = d.getTime() / H;
\r
305 Date ed = new Date((long)(epochHours * H));
\r
306 if (e == epochHours) {
\r
307 logln("Ok: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
\r
308 e + " (" + ed + ")");
\r
310 errln("FAIL: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
\r
311 e + " (" + new Date((long)(e * H)) + ")" +
\r
312 ", expected " + epochHours + " (" + ed + ")");
\r
315 if (cal.get(Calendar.YEAR) == year &&
\r
316 cal.get(Calendar.MONTH) == month &&
\r
317 cal.get(Calendar.DATE) == dom &&
\r
318 cal.get(Calendar.MILLISECONDS_IN_DAY) == hour * 3600000) {
\r
319 logln("Ok: " + epochHours + " (" + ed + ") => " +
\r
320 cal.get(Calendar.YEAR) + "/" +
\r
321 (cal.get(Calendar.MONTH)+1) + "/" +
\r
322 cal.get(Calendar.DATE) + " " +
\r
323 cal.get(Calendar.MILLISECONDS_IN_DAY)/H);
\r
325 errln("FAIL: " + epochHours + " (" + ed + ") => " +
\r
326 cal.get(Calendar.YEAR) + "/" +
\r
327 (cal.get(Calendar.MONTH)+1) + "/" +
\r
328 cal.get(Calendar.DATE) + " " +
\r
329 cal.get(Calendar.MILLISECONDS_IN_DAY)/H +
\r
330 ", expected " + year + "/" + (month+1) + "/" + dom +
\r
335 // NOTE: Enable this code to check the behavior of the underlying JDK,
\r
336 // using a JDK Calendar object.
\r
338 // int millisInDay(java.util.Calendar cal) {
\r
339 // return ((cal.get(Calendar.HOUR_OF_DAY) * 60 +
\r
340 // cal.get(Calendar.MINUTE)) * 60 +
\r
341 // cal.get(Calendar.SECOND)) * 1000 +
\r
342 // cal.get(Calendar.MILLISECOND);
\r
345 // void verifyMapping(java.util.Calendar cal, int year, int month, int dom, int hour,
\r
346 // double epochHours) {
\r
348 // cal.set(year, month, dom, hour, 0, 0);
\r
349 // Date d = cal.getTime();
\r
350 // double e = d.getTime() / 3600000.0;
\r
351 // Date ed = new Date((long)(epochHours * 3600000));
\r
352 // if (e == epochHours) {
\r
353 // logln("Ok: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
\r
354 // e + " (" + ed + ")");
\r
356 // errln("FAIL: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
\r
357 // e + " (" + new Date((long)(e * 3600000)) + ")" +
\r
358 // ", expected " + epochHours + " (" + ed + ")");
\r
360 // cal.setTime(ed);
\r
361 // if (cal.get(Calendar.YEAR) == year &&
\r
362 // cal.get(Calendar.MONTH) == month &&
\r
363 // cal.get(Calendar.DATE) == dom &&
\r
364 // millisInDay(cal) == hour * 3600000) {
\r
365 // logln("Ok: " + epochHours + " (" + ed + ") => " +
\r
366 // cal.get(Calendar.YEAR) + "/" +
\r
367 // (cal.get(Calendar.MONTH)+1) + "/" +
\r
368 // cal.get(Calendar.DATE) + " " +
\r
369 // millisInDay(cal)/3600000.0);
\r
371 // errln("FAIL: " + epochHours + " (" + ed + ") => " +
\r
372 // cal.get(Calendar.YEAR) + "/" +
\r
373 // (cal.get(Calendar.MONTH)+1) + "/" +
\r
374 // cal.get(Calendar.DATE) + " " +
\r
375 // millisInDay(cal)/3600000.0 +
\r
376 // ", expected " + year + "/" + (month+1) + "/" + dom +
\r
381 public void TestBoundaries()
\r
383 TimeZone save = TimeZone.getDefault();
\r
385 // Check basic mappings. We had a problem with this for ICU
\r
386 // 2.8 after migrating to using pass-through time zones. The
\r
387 // problem appeared only on JDK 1.3.
\r
388 TimeZone pst = safeGetTimeZone("PST");
\r
389 Calendar tempcal = Calendar.getInstance(pst);
\r
390 verifyMapping(tempcal, 1997, Calendar.APRIL, 3, 0, 238904.0);
\r
391 verifyMapping(tempcal, 1997, Calendar.APRIL, 4, 0, 238928.0);
\r
392 verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 0, 238952.0);
\r
393 verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 23, 238975.0);
\r
394 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 0, 238976.0);
\r
395 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 1, 238977.0);
\r
396 verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 3, 238978.0);
\r
398 TimeZone utc = safeGetTimeZone("UTC");
\r
399 Calendar utccal = Calendar.getInstance(utc);
\r
400 verifyMapping(utccal, 1997, Calendar.APRIL, 6, 0, 238968.0);
\r
402 // NOTE: Enable this code to check the behavior of the underlying JDK,
\r
403 // using a JDK Calendar object.
\r
405 // java.util.TimeZone jdkpst = java.util.TimeZone.getTimeZone("PST");
\r
406 // java.util.Calendar jdkcal = java.util.Calendar.getInstance(jdkpst);
\r
407 // verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 0, 238952.0);
\r
408 // verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 23, 238975.0);
\r
409 // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 0, 238976.0);
\r
410 // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 1, 238977.0);
\r
411 // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 3, 238978.0);
\r
414 tempcal.set(1997, Calendar.APRIL, 6);
\r
415 Date d = tempcal.getTime();
\r
418 TimeZone.setDefault(pst);
\r
420 // DST changeover for PST is 4/6/1997 at 2 hours past midnight
\r
421 // at 238978.0 epoch hours.
\r
423 // i is minutes past midnight standard time
\r
424 for (int i=-120; i<=180; i+=60)
\r
426 boolean inDST = (i >= 120);
\r
427 tempcal.setTimeInMillis(d.getTime() + i*60*1000);
\r
428 verifyDST("hour=" + i/60,
\r
429 tempcal, pst, true, inDST, -8*ONE_HOUR,
\r
430 inDST ? -7*ONE_HOUR : -8*ONE_HOUR);
\r
433 TimeZone.setDefault(save);
\r
436 // We no longer use ICU TimeZone implementation for Java
\r
437 // default TimeZone. Java 1.3 or older version do not
\r
438 // support historic transitions, therefore, the test below
\r
439 // will fail on such environment (with the latest TimeZone
\r
440 // patch for US 2007+ rule).
\r
441 String javaver = System.getProperty("java.version", "1.3");
\r
442 if (!javaver.startsWith("1.3"))
\r
444 // This only works in PST/PDT
\r
445 TimeZone.setDefault(safeGetTimeZone("PST"));
\r
446 logln("========================================");
\r
447 tempcal.set(1997, 0, 1);
\r
448 findDaylightBoundaryUsingDate(tempcal.getTime(), "PST", PST_1997_BEG);
\r
449 logln("========================================");
\r
450 tempcal.set(1997, 6, 1);
\r
451 findDaylightBoundaryUsingDate(tempcal.getTime(), "PDT", PST_1997_END);
\r
456 // logln("========================================");
\r
457 // findDaylightBoundaryUsingCalendar(new Date(97,0,1), false);
\r
458 // logln("========================================");
\r
459 // findDaylightBoundaryUsingCalendar(new Date(97,6,1), true);
\r
464 // Southern hemisphere test
\r
465 logln("========================================");
\r
466 TimeZone z = safeGetTimeZone(AUSTRALIA);
\r
467 tempcal.set(1997, 0, 1);
\r
468 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true, AUSTRALIA_1997_END, z);
\r
469 logln("========================================");
\r
470 tempcal.set(1997, 6, 1);
\r
471 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false, AUSTRALIA_1997_BEG, z);
\r
476 logln("========================================");
\r
477 tempcal.set(1997, 0, 1);
\r
478 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false, PST_1997_BEG);
\r
479 logln("========================================");
\r
480 tempcal.set(1997, 6, 1);
\r
481 findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true, PST_1997_END);
\r
484 // This just shows the offset for April 4-7 in 1997. This is redundant
\r
485 // with a test above, so we disable it.
\r
488 TimeZone z = TimeZone.getDefault();
\r
489 tempcal.set(1997, 3, 4);
\r
490 logln(z.getOffset(1, 97, 3, 4, 6, 0) + " " + tempcal.getTime());
\r
491 tempcal.set(1997, 3, 5);
\r
492 logln(z.getOffset(1, 97, 3, 5, 7, 0) + " " + tempcal.getTime());
\r
493 tempcal.set(1997, 3, 6);
\r
494 logln(z.getOffset(1, 97, 3, 6, 1, 0) + " " + tempcal.getTime());
\r
495 tempcal.set(1997, 3, 7);
\r
496 logln(z.getOffset(1, 97, 3, 7, 2, 0) + " " + tempcal.getTime());
\r
501 //----------------------------------------------------------------------
\r
502 // Can't do any of these without a public inDaylightTime in GC
\r
503 //----------------------------------------------------------------------
\r
506 // static GregorianCalendar cal = new GregorianCalendar();
\r
508 // static void _testUsingBinarySearch(Date d, boolean startsInDST)
\r
510 // // Given a date with a year start, find the Daylight onset
\r
511 // // and end. The given date should be 1/1/xx in some year.
\r
513 // // Use a binary search, assuming that we have a Standard
\r
514 // // time at the midpoint.
\r
515 // long min = d.getTime();
\r
516 // long max = min + (long)(365.25 / 2 * ONE_DAY);
\r
518 // // First check the max
\r
519 // cal.setTime(new Date(max));
\r
520 // if (cal.inDaylightTime() == startsInDST)
\r
522 // logln("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST));
\r
526 // if (cal.inDaylightTime() != startsInDST)
\r
528 // logln("Error: inDaylightTime(" + d + ") != " + startsInDST);
\r
531 // while ((max - min) > INTERVAL)
\r
533 // long mid = (min + max) >> 1;
\r
534 // cal.setTime(new Date(mid));
\r
535 // if (cal.inDaylightTime() == startsInDST)
\r
545 // logln("Binary Search Before: " + showDate(min));
\r
546 // logln("Binary Search After: " + showDate(max));
\r
549 // static void _testUsingMillis(Date d, boolean startsInDST)
\r
551 // long millis = d.getTime();
\r
552 // long max = millis + (long)(370 * ONE_DAY); // A year plus extra
\r
554 // boolean lastDST = startsInDST;
\r
555 // while (millis < max)
\r
557 // cal.setTime(new Date(millis));
\r
558 // boolean inDaylight = cal.inDaylightTime();
\r
560 // if (inDaylight != lastDST)
\r
562 // logln("Switch " + (inDaylight ? "into" : "out of")
\r
563 // + " DST at " + (new Date(millis)));
\r
564 // lastDST = inDaylight;
\r
567 // millis += 15*ONE_MINUTE;
\r
571 // static void _testUsingFields(int y, boolean startsInDST)
\r
573 // boolean lastDST = startsInDST;
\r
574 // for (int m = 0; m < 12; ++m)
\r
576 // for (int d = 1; d <= MONTH_LENGTH[m]; ++d)
\r
578 // for (int h = 0; h < 24; ++h)
\r
580 // for (int min = 0; min < 60; min += 15)
\r
583 // cal.set(y, m, d, h, min);
\r
584 // boolean inDaylight = cal.inDaylightTime();
\r
585 // if (inDaylight != lastDST)
\r
587 // lastDST = inDaylight;
\r
588 // log("Switch " + (lastDST ? "into" : "out of")
\r
589 // + " DST at " + y + "/" + (m+1) + "/" + d
\r
590 // + " " + showNN(h) + ":" + showNN(min));
\r
591 // logln(" " + cal.getTime());
\r
593 // cal.set(y, m, d, h-1, 45);
\r
595 //+ y + "/" + (m+1) + "/" + d
\r
596 //+ " " + showNN(h-1) + ":" + showNN(45));
\r
597 // logln(" " + cal.getTime());
\r
605 // public void Test1()
\r
607 // logln(Locale.getDefault().getDisplayName());
\r
608 // logln(TimeZone.getDefault().getID());
\r
609 // logln(new Date(0));
\r
613 // logln("========================================");
\r
614 // _testUsingBinarySearch(new Date(97,0,1), false);
\r
615 // logln("========================================");
\r
616 // _testUsingBinarySearch(new Date(97,6,1), true);
\r
621 // logln("========================================");
\r
622 // logln("Stepping using millis");
\r
623 // _testUsingMillis(new Date(97,0,1), false);
\r
628 // logln("========================================");
\r
629 // logln("Stepping using fields");
\r
630 // _testUsingFields(1997, false);
\r
636 // cal.set(1997, 3, 5, 10, 0);
\r
637 // // cal.inDaylightTime();
\r
638 // logln("Date = " + cal.getTime());
\r
639 // logln("Millis = " + cal.getTime().getTime()/3600000);
\r
643 //----------------------------------------------------------------------
\r
644 //----------------------------------------------------------------------
\r
645 //----------------------------------------------------------------------
\r
647 void _testUsingBinarySearch(SimpleTimeZone tz, Date d, long expectedBoundary)
\r
649 // Given a date with a year start, find the Daylight onset
\r
650 // and end. The given date should be 1/1/xx in some year.
\r
652 // Use a binary search, assuming that we have a Standard
\r
653 // time at the midpoint.
\r
654 long min = d.getTime();
\r
655 long max = min + (long)(365.25 / 2 * ONE_DAY);
\r
657 // First check the boundaries
\r
658 boolean startsInDST = tz.inDaylightTime(d);
\r
660 if (tz.inDaylightTime(new Date(max)) == startsInDST)
\r
662 errln("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST));
\r
665 while ((max - min) > INTERVAL)
\r
667 long mid = (min + max) >> 1;
\r
668 if (tz.inDaylightTime(new Date(mid)) == startsInDST)
\r
678 logln("Binary Search Before: " + showDate(min));
\r
679 logln("Binary Search After: " + showDate(max));
\r
681 long mindelta = expectedBoundary - min;
\r
682 // not used long maxdelta = max - expectedBoundary;
\r
683 if (mindelta >= 0 && mindelta <= INTERVAL &&
\r
684 mindelta >= 0 && mindelta <= INTERVAL)
\r
685 logln("PASS: Expected boundary at " + expectedBoundary);
\r
687 errln("FAIL: Expected boundary at " + expectedBoundary);
\r
691 static void _testUsingMillis(Date d, boolean startsInDST)
\r
693 long millis = d.getTime();
\r
694 long max = millis + (long)(370 * ONE_DAY); // A year plus extra
\r
696 boolean lastDST = startsInDST;
\r
697 while (millis < max)
\r
699 cal.setTime(new Date(millis));
\r
700 boolean inDaylight = cal.inDaylightTime();
\r
702 if (inDaylight != lastDST)
\r
704 logln("Switch " + (inDaylight ? "into" : "out of")
\r
705 + " DST at " + (new Date(millis)));
\r
706 lastDST = inDaylight;
\r
709 millis += 15*ONE_MINUTE;
\r
715 * Test new rule formats.
\r
717 public void TestNewRules()
\r
719 //logln(Locale.getDefault().getDisplayName());
\r
720 //logln(TimeZone.getDefault().getID());
\r
721 //logln(new Date(0));
\r
725 // Doesn't matter what the default TimeZone is here, since we
\r
726 // are creating our own TimeZone objects.
\r
729 java.util.Calendar tempcal = java.util.Calendar.getInstance();
\r
732 logln("-----------------------------------------------------------------");
\r
733 logln("Aug 2ndTues .. Mar 15");
\r
734 tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_1",
\r
735 Calendar.AUGUST, 2, Calendar.TUESDAY, 2*ONE_HOUR,
\r
736 Calendar.MARCH, 15, 0, 2*ONE_HOUR);
\r
737 //logln(tz.toString());
\r
738 logln("========================================");
\r
739 tempcal.set(1997, 0, 1);
\r
740 _testUsingBinarySearch(tz, tempcal.getTime(), 858416400000L);
\r
741 logln("========================================");
\r
742 tempcal.set(1997, 6, 1);
\r
743 _testUsingBinarySearch(tz, tempcal.getTime(), 871380000000L);
\r
745 logln("-----------------------------------------------------------------");
\r
746 logln("Apr Wed>=14 .. Sep Sun<=20");
\r
747 tz = new SimpleTimeZone(-8*ONE_HOUR, "Test_2",
\r
748 Calendar.APRIL, 14, -Calendar.WEDNESDAY, 2*ONE_HOUR,
\r
749 Calendar.SEPTEMBER, -20, -Calendar.SUNDAY, 2*ONE_HOUR);
\r
750 //logln(tz.toString());
\r
751 logln("========================================");
\r
752 tempcal.set(1997, 0, 1);
\r
753 _testUsingBinarySearch(tz, tempcal.getTime(), 861184800000L);
\r
754 logln("========================================");
\r
755 tempcal.set(1997, 6, 1);
\r
756 _testUsingBinarySearch(tz, tempcal.getTime(), 874227600000L);
\r
762 logln("========================================");
\r
763 logln("Stepping using millis");
\r
764 _testUsingMillis(new Date(97,0,1), false);
\r
769 logln("========================================");
\r
770 logln("Stepping using fields");
\r
771 _testUsingFields(1997, false);
\r
777 cal.set(1997, 3, 5, 10, 0);
\r
778 // cal.inDaylightTime();
\r
779 logln("Date = " + cal.getTime());
\r
780 logln("Millis = " + cal.getTime().getTime()/3600000);
\r
785 //----------------------------------------------------------------------
\r
786 //----------------------------------------------------------------------
\r
787 //----------------------------------------------------------------------
\r
789 //----------------------------------------------------------------------
\r
790 //----------------------------------------------------------------------
\r
791 //----------------------------------------------------------------------
\r
793 //public void Test3()
\r
795 // findDaylightBoundaryUsingTimeZone(new Date(97,6,1), true);
\r
799 * Find boundaries by stepping.
\r
801 void findBoundariesStepwise(int year, long interval, TimeZone z, int expectedChanges)
\r
803 java.util.Calendar tempcal = java.util.Calendar.getInstance();
\r
805 tempcal.set(year, Calendar.JANUARY, 1);
\r
806 Date d = tempcal.getTime();
\r
807 long time = d.getTime(); // ms
\r
808 long limit = time + ONE_YEAR + ONE_DAY;
\r
809 boolean lastState = z.inDaylightTime(d);
\r
811 logln("-- Zone " + z.getID() + " starts in " + year + " with DST = " + lastState);
\r
812 logln("useDaylightTime = " + z.useDaylightTime());
\r
813 while (time < limit)
\r
816 boolean state = z.inDaylightTime(d);
\r
817 if (state != lastState)
\r
819 logln((state ? "Entry " : "Exit ") +
\r
828 if (!lastState && !z.useDaylightTime()) logln("No DST");
\r
829 else errln("FAIL: DST all year, or no DST with true useDaylightTime");
\r
831 else if (changes != 2)
\r
833 errln("FAIL: " + changes + " changes seen; should see 0 or 2");
\r
835 else if (!z.useDaylightTime())
\r
837 errln("FAIL: useDaylightTime false but 2 changes seen");
\r
839 if (changes != expectedChanges)
\r
841 errln("FAIL: " + changes + " changes seen; expected " + expectedChanges);
\r
845 public void TestStepwise()
\r
847 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("America/New_York"), 2);
\r
848 // disabled Oct 2003 aliu; ACT could mean anything, depending on the underlying JDK, as of 2.8
\r
849 // findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("ACT"), 2);
\r
850 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("America/Phoenix"), 0); // Added 3Jan01
\r
851 findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone(AUSTRALIA), 2);
\r