001: /**
002: *******************************************************************************
003: * Copyright (C) 2000-2005, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.dev.test.timezone;
007:
008: import com.ibm.icu.text.*;
009: import com.ibm.icu.util.*;
010: import com.ibm.icu.dev.test.*;
011: import java.util.Date;
012:
013: /**
014: * A test which discovers the boundaries of DST programmatically and verifies
015: * that they are correct.
016: */
017: public class TimeZoneBoundaryTest extends TestFmwk {
018: static final int ONE_SECOND = 1000;
019: static final int ONE_MINUTE = 60 * ONE_SECOND;
020: static final int ONE_HOUR = 60 * ONE_MINUTE;
021: static final long ONE_DAY = 24 * ONE_HOUR;
022: static final long ONE_YEAR = (long) (365.25 * ONE_DAY);
023: static final long SIX_MONTHS = ONE_YEAR / 2;
024:
025: static final int MONTH_LENGTH[] = { 31, 29, 31, 30, 31, 30, 31, 31,
026: 30, 31, 30, 31 };
027:
028: // These values are empirically determined to be correct
029: static final long PST_1997_BEG = 860320800000L;
030: static final long PST_1997_END = 877856400000L;
031:
032: // Minimum interval for binary searches in ms; should be no larger
033: // than 1000.
034: static final long INTERVAL = 10; // Milliseconds
035:
036: // [3Jan01 Liu] Updated for 2000f data
037: static final String AUSTRALIA = "Australia/Adelaide";
038: static final long AUSTRALIA_1997_BEG = 877797000000L;
039: static final long AUSTRALIA_1997_END = 859653000000L;
040:
041: public static void main(String[] args) throws Exception {
042: new TimeZoneBoundaryTest().run(args);
043: }
044:
045: /**
046: * Date.toString().substring() Boundary Test
047: * Look for a DST changeover to occur within 6 months of the given Date.
048: * The initial Date.toString() should yield a string containing the
049: * startMode as a SUBSTRING. The boundary will be tested to be
050: * at the expectedBoundary value.
051: */
052: void findDaylightBoundaryUsingDate(Date d, String startMode,
053: long expectedBoundary) {
054: // Given a date with a year start, find the Daylight onset
055: // and end. The given date should be 1/1/xx in some year.
056:
057: if (d.toString().indexOf(startMode) == -1) {
058: logln("Error: " + startMode + " not present in " + d);
059: }
060:
061: // Use a binary search, assuming that we have a Standard
062: // time at the midpoint.
063: long min = d.getTime();
064: long max = min + SIX_MONTHS;
065:
066: while ((max - min) > INTERVAL) {
067: long mid = (min + max) >> 1;
068: String s = new Date(mid).toString();
069: // logln(s);
070: if (s.indexOf(startMode) != -1) {
071: min = mid;
072: } else {
073: max = mid;
074: }
075: }
076:
077: logln("Date Before: " + showDate(min));
078: logln("Date After: " + showDate(max));
079: long mindelta = expectedBoundary - min;
080: // not used long maxdelta = max - expectedBoundary;
081: if (mindelta >= 0 && mindelta <= INTERVAL && mindelta >= 0
082: && mindelta <= INTERVAL)
083: logln("PASS: Expected boundary at " + expectedBoundary);
084: else
085: errln("FAIL: Expected boundary at " + expectedBoundary);
086: }
087:
088: // This test cannot be compiled until the inDaylightTime() method of GregorianCalendar
089: // becomes public.
090: // static void findDaylightBoundaryUsingCalendar(Date d, boolean startsInDST)
091: // {
092: // // Given a date with a year start, find the Daylight onset
093: // // and end. The given date should be 1/1/xx in some year.
094: //
095: // GregorianCalendar cal = new GregorianCalendar();
096: // cal.setTime(d);
097: // if (cal.inDaylightTime() != startsInDST)
098: // {
099: // logln("Error: inDaylightTime(" + d + ") != " + startsInDST);
100: // }
101: //
102: // // Use a binary search, assuming that we have a Standard
103: // // time at the midpoint.
104: // long min = d.getTime();
105: // long max = min + (long)(365.25 / 2 * 24*60*60*1000);
106: //
107: // while ((max - min) > INTERVAL)
108: // {
109: // long mid = (min + max) >> 1;
110: // cal.setTime(new Date(mid));
111: // if (cal.inDaylightTime() == startsInDST)
112: // {
113: // min = mid;
114: // }
115: // else
116: // {
117: // max = mid;
118: // }
119: // }
120: //
121: // logln("Calendar Before: " + showDate(min));
122: // logln("Calendar After: " + showDate(max));
123: // }
124:
125: void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST,
126: long expectedBoundary) {
127: findDaylightBoundaryUsingTimeZone(d, startsInDST,
128: expectedBoundary, TimeZone.getDefault());
129: }
130:
131: void findDaylightBoundaryUsingTimeZone(Date d, boolean startsInDST,
132: long expectedBoundary, TimeZone tz) {
133: // Given a date with a year start, find the Daylight onset
134: // and end. The given date should be 1/1/xx in some year.
135:
136: // Use a binary search, assuming that we have a Standard
137: // time at the midpoint.
138: long min = d.getTime();
139: long max = min + SIX_MONTHS;
140:
141: if (tz.inDaylightTime(d) != startsInDST) {
142: errln("FAIL: " + tz.getID() + " inDaylightTime(" + d
143: + ") != " + startsInDST);
144: startsInDST = !startsInDST; // Flip over; find the apparent value
145: }
146:
147: if (tz.inDaylightTime(new Date(max)) == startsInDST) {
148: errln("FAIL: " + tz.getID() + " inDaylightTime("
149: + (new Date(max)) + ") != " + (!startsInDST));
150: return;
151: }
152:
153: while ((max - min) > INTERVAL) {
154: long mid = (min + max) >> 1;
155: boolean isIn = tz.inDaylightTime(new Date(mid));
156: if (isIn == startsInDST) {
157: min = mid;
158: } else {
159: max = mid;
160: }
161: }
162:
163: logln(tz.getID() + " Before: " + showDate(min, tz));
164: logln(tz.getID() + " After: " + showDate(max, tz));
165:
166: long mindelta = expectedBoundary - min;
167: // not used long maxdelta = max - expectedBoundary;
168: DateFormat fmt = DateFormat.getDateTimeInstance(
169: DateFormat.LONG, DateFormat.LONG);
170: fmt.setTimeZone(tz);
171: if (mindelta >= 0 && mindelta <= INTERVAL && mindelta >= 0
172: && mindelta <= INTERVAL)
173: logln("PASS: Expected boundary at " + expectedBoundary
174: + " = " + fmt.format(new Date(expectedBoundary)));
175: else
176: errln("FAIL: Expected boundary at " + expectedBoundary
177: + " = " + fmt.format(new Date(expectedBoundary)));
178: }
179:
180: private static String showDate(long l) {
181: return showDate(new Date(l));
182: }
183:
184: private static String showDate(Date d) {
185: java.util.Calendar cal = java.util.Calendar.getInstance();
186: cal.setTime(d);
187: return "" + (cal.get(Calendar.YEAR) - 1900) + "/"
188: + showNN(cal.get(Calendar.MONTH) + 1) + "/"
189: + showNN(cal.get(Calendar.DAY_OF_MONTH)) + " "
190: + showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":"
191: + showNN(cal.get(Calendar.MINUTE)) + " \"" + d
192: + "\" = " + d.getTime();
193: }
194:
195: private static String showDate(long l, TimeZone z) {
196: return showDate(new Date(l), z);
197: }
198:
199: private static String showDate(Date d, TimeZone zone) {
200: DateFormat fmt = DateFormat.getDateTimeInstance(
201: DateFormat.LONG, DateFormat.LONG);
202: fmt.setTimeZone(zone);
203: java.util.Calendar cal = java.util.Calendar.getInstance();
204: cal.setTime(d);
205: return "" + (cal.get(Calendar.YEAR) - 1900) + "/"
206: + showNN(cal.get(Calendar.MONTH) + 1) + "/"
207: + showNN(cal.get(Calendar.DAY_OF_MONTH)) + " "
208: + showNN(cal.get(Calendar.HOUR_OF_DAY)) + ":"
209: + showNN(cal.get(Calendar.MINUTE)) + " \"" + d
210: + "\" = " + fmt.format(d) + " = " + d.getTime();
211: }
212:
213: private static String showNN(int n) {
214: return ((n < 10) ? "0" : "") + n;
215: }
216:
217: /**
218: * Given a date, a TimeZone, and expected values for inDaylightTime,
219: * useDaylightTime, zone and DST offset, verify that this is the case.
220: */
221: void verifyDST(String tag, Calendar cal, TimeZone time_zone,
222: boolean expUseDaylightTime, boolean expInDaylightTime,
223: int expRawOffset, int expOffset) {
224: Date d = cal.getTime();
225:
226: logln("-- " + tag + ": " + d + " in zone " + time_zone.getID()
227: + " (" + d.getTime() / 3600000.0 + ")");
228:
229: if (time_zone.inDaylightTime(d) == expInDaylightTime)
230: logln("PASS: inDaylightTime = "
231: + time_zone.inDaylightTime(d));
232: else
233: errln("FAIL: inDaylightTime = "
234: + time_zone.inDaylightTime(d));
235:
236: if (time_zone.useDaylightTime() == expUseDaylightTime)
237: logln("PASS: useDaylightTime = "
238: + time_zone.useDaylightTime());
239: else
240: errln("FAIL: useDaylightTime = "
241: + time_zone.useDaylightTime());
242:
243: if (time_zone.getRawOffset() == expRawOffset)
244: logln("PASS: getRawOffset() = " + expRawOffset
245: / (double) ONE_HOUR);
246: else
247: errln("FAIL: getRawOffset() = " + time_zone.getRawOffset()
248: / (double) ONE_HOUR + "; expected " + expRawOffset
249: / (double) ONE_HOUR);
250:
251: //GregorianCalendar gc = new GregorianCalendar(time_zone);
252: //gc.setTime(d);
253: int offset = time_zone.getOffset(cal.get(Calendar.ERA), cal
254: .get(Calendar.YEAR), cal.get(Calendar.MONTH), cal
255: .get(Calendar.DAY_OF_MONTH), cal
256: .get(Calendar.DAY_OF_WEEK), ((cal
257: .get(Calendar.HOUR_OF_DAY) * 60 + cal
258: .get(Calendar.MINUTE)) * 60 + cal.get(Calendar.SECOND))
259: * 1000 + cal.get(Calendar.MILLISECOND));
260: if (offset == expOffset)
261: logln("PASS: getOffset() = " + offset / (double) ONE_HOUR);
262: else {
263: logln("era="
264: + cal.get(Calendar.ERA)
265: + ", year="
266: + cal.get(Calendar.YEAR)
267: + ", month="
268: + cal.get(Calendar.MONTH)
269: + ", dom="
270: + cal.get(Calendar.DAY_OF_MONTH)
271: + ", dow="
272: + cal.get(Calendar.DAY_OF_WEEK)
273: + ", time-of-day="
274: + (((cal.get(Calendar.HOUR_OF_DAY) * 60 + cal
275: .get(Calendar.MINUTE)) * 60 + cal
276: .get(Calendar.SECOND)) * 1000 + cal
277: .get(Calendar.MILLISECOND)) / 3600000.0
278: + " hours");
279: errln("FAIL: getOffset() = " + offset / (double) ONE_HOUR
280: + "; expected " + expOffset / (double) ONE_HOUR);
281: }
282: }
283:
284: /**
285: * Check that the given year/month/dom/hour maps to and from the
286: * given epochHours. This verifies the functioning of the
287: * calendar and time zone in conjunction with one another,
288: * including the calendar time->fields and fields->time and
289: * the time zone getOffset method.
290: *
291: * @param epochHours hours after Jan 1 1970 0:00 GMT.
292: */
293: void verifyMapping(Calendar cal, int year, int month, int dom,
294: int hour, double epochHours) {
295: double H = 3600000.0;
296: cal.clear();
297: cal.set(year, month, dom, hour, 0, 0);
298: Date d = cal.getTime();
299: double e = d.getTime() / H;
300: Date ed = new Date((long) (epochHours * H));
301: if (e == epochHours) {
302: logln("Ok: " + year + "/" + (month + 1) + "/" + dom + " "
303: + hour + ":00 => " + e + " (" + ed + ")");
304: } else {
305: errln("FAIL: " + year + "/" + (month + 1) + "/" + dom + " "
306: + hour + ":00 => " + e + " ("
307: + new Date((long) (e * H)) + ")" + ", expected "
308: + epochHours + " (" + ed + ")");
309: }
310: cal.setTime(ed);
311: if (cal.get(Calendar.YEAR) == year
312: && cal.get(Calendar.MONTH) == month
313: && cal.get(Calendar.DATE) == dom
314: && cal.get(Calendar.MILLISECONDS_IN_DAY) == hour * 3600000) {
315: logln("Ok: " + epochHours + " (" + ed + ") => "
316: + cal.get(Calendar.YEAR) + "/"
317: + (cal.get(Calendar.MONTH) + 1) + "/"
318: + cal.get(Calendar.DATE) + " "
319: + cal.get(Calendar.MILLISECONDS_IN_DAY) / H);
320: } else {
321: errln("FAIL: " + epochHours + " (" + ed + ") => "
322: + cal.get(Calendar.YEAR) + "/"
323: + (cal.get(Calendar.MONTH) + 1) + "/"
324: + cal.get(Calendar.DATE) + " "
325: + cal.get(Calendar.MILLISECONDS_IN_DAY) / H
326: + ", expected " + year + "/" + (month + 1) + "/"
327: + dom + " " + hour);
328: }
329: }
330:
331: // NOTE: Enable this code to check the behavior of the underlying JDK,
332: // using a JDK Calendar object.
333: //
334: // int millisInDay(java.util.Calendar cal) {
335: // return ((cal.get(Calendar.HOUR_OF_DAY) * 60 +
336: // cal.get(Calendar.MINUTE)) * 60 +
337: // cal.get(Calendar.SECOND)) * 1000 +
338: // cal.get(Calendar.MILLISECOND);
339: // }
340: //
341: // void verifyMapping(java.util.Calendar cal, int year, int month, int dom, int hour,
342: // double epochHours) {
343: // cal.clear();
344: // cal.set(year, month, dom, hour, 0, 0);
345: // Date d = cal.getTime();
346: // double e = d.getTime() / 3600000.0;
347: // Date ed = new Date((long)(epochHours * 3600000));
348: // if (e == epochHours) {
349: // logln("Ok: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
350: // e + " (" + ed + ")");
351: // } else {
352: // errln("FAIL: " + year + "/" + (month+1) + "/" + dom + " " + hour + ":00 => " +
353: // e + " (" + new Date((long)(e * 3600000)) + ")" +
354: // ", expected " + epochHours + " (" + ed + ")");
355: // }
356: // cal.setTime(ed);
357: // if (cal.get(Calendar.YEAR) == year &&
358: // cal.get(Calendar.MONTH) == month &&
359: // cal.get(Calendar.DATE) == dom &&
360: // millisInDay(cal) == hour * 3600000) {
361: // logln("Ok: " + epochHours + " (" + ed + ") => " +
362: // cal.get(Calendar.YEAR) + "/" +
363: // (cal.get(Calendar.MONTH)+1) + "/" +
364: // cal.get(Calendar.DATE) + " " +
365: // millisInDay(cal)/3600000.0);
366: // } else {
367: // errln("FAIL: " + epochHours + " (" + ed + ") => " +
368: // cal.get(Calendar.YEAR) + "/" +
369: // (cal.get(Calendar.MONTH)+1) + "/" +
370: // cal.get(Calendar.DATE) + " " +
371: // millisInDay(cal)/3600000.0 +
372: // ", expected " + year + "/" + (month+1) + "/" + dom +
373: // " " + hour);
374: // }
375: // }
376:
377: public void TestBoundaries() {
378: TimeZone save = TimeZone.getDefault();
379:
380: // Check basic mappings. We had a problem with this for ICU
381: // 2.8 after migrating to using pass-through time zones. The
382: // problem appeared only on JDK 1.3.
383: TimeZone pst = safeGetTimeZone("PST");
384: Calendar tempcal = Calendar.getInstance(pst);
385: verifyMapping(tempcal, 1997, Calendar.APRIL, 3, 0, 238904.0);
386: verifyMapping(tempcal, 1997, Calendar.APRIL, 4, 0, 238928.0);
387: verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 0, 238952.0);
388: verifyMapping(tempcal, 1997, Calendar.APRIL, 5, 23, 238975.0);
389: verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 0, 238976.0);
390: verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 1, 238977.0);
391: verifyMapping(tempcal, 1997, Calendar.APRIL, 6, 3, 238978.0);
392:
393: TimeZone utc = safeGetTimeZone("UTC");
394: Calendar utccal = Calendar.getInstance(utc);
395: verifyMapping(utccal, 1997, Calendar.APRIL, 6, 0, 238968.0);
396:
397: // NOTE: Enable this code to check the behavior of the underlying JDK,
398: // using a JDK Calendar object.
399: //
400: // java.util.TimeZone jdkpst = java.util.TimeZone.getTimeZone("PST");
401: // java.util.Calendar jdkcal = java.util.Calendar.getInstance(jdkpst);
402: // verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 0, 238952.0);
403: // verifyMapping(jdkcal, 1997, Calendar.APRIL, 5, 23, 238975.0);
404: // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 0, 238976.0);
405: // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 1, 238977.0);
406: // verifyMapping(jdkcal, 1997, Calendar.APRIL, 6, 3, 238978.0);
407:
408: tempcal.clear();
409: tempcal.set(1997, Calendar.APRIL, 6);
410: Date d = tempcal.getTime();
411:
412: try {
413: TimeZone.setDefault(pst);
414:
415: // DST changeover for PST is 4/6/1997 at 2 hours past midnight
416: // at 238978.0 epoch hours.
417:
418: // i is minutes past midnight standard time
419: for (int i = -120; i <= 180; i += 60) {
420: boolean inDST = (i >= 120);
421: tempcal.setTimeInMillis(d.getTime() + i * 60 * 1000);
422: verifyDST("hour=" + i / 60, tempcal, pst, true, inDST,
423: -8 * ONE_HOUR, inDST ? -7 * ONE_HOUR : -8
424: * ONE_HOUR);
425: }
426: } finally {
427: TimeZone.setDefault(save);
428: }
429:
430: if (true) {
431: // This only works in PST/PDT
432: TimeZone.setDefault(safeGetTimeZone("PST"));
433: logln("========================================");
434: tempcal.set(1997, 0, 1);
435: findDaylightBoundaryUsingDate(tempcal.getTime(), "PST",
436: PST_1997_BEG);
437: logln("========================================");
438: tempcal.set(1997, 6, 1);
439: findDaylightBoundaryUsingDate(tempcal.getTime(), "PDT",
440: PST_1997_END);
441: }
442:
443: // if (true)
444: // {
445: // logln("========================================");
446: // findDaylightBoundaryUsingCalendar(new Date(97,0,1), false);
447: // logln("========================================");
448: // findDaylightBoundaryUsingCalendar(new Date(97,6,1), true);
449: // }
450:
451: if (true) {
452: // Southern hemisphere test
453: logln("========================================");
454: TimeZone z = safeGetTimeZone(AUSTRALIA);
455: tempcal.set(1997, 0, 1);
456: findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true,
457: AUSTRALIA_1997_END, z);
458: logln("========================================");
459: tempcal.set(1997, 6, 1);
460: findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false,
461: AUSTRALIA_1997_BEG, z);
462: }
463:
464: if (true) {
465: logln("========================================");
466: tempcal.set(1997, 0, 1);
467: findDaylightBoundaryUsingTimeZone(tempcal.getTime(), false,
468: PST_1997_BEG);
469: logln("========================================");
470: tempcal.set(1997, 6, 1);
471: findDaylightBoundaryUsingTimeZone(tempcal.getTime(), true,
472: PST_1997_END);
473: }
474:
475: // This just shows the offset for April 4-7 in 1997. This is redundant
476: // with a test above, so we disable it.
477: if (false) {
478: TimeZone z = TimeZone.getDefault();
479: tempcal.set(1997, 3, 4);
480: logln(z.getOffset(1, 97, 3, 4, 6, 0) + " "
481: + tempcal.getTime());
482: tempcal.set(1997, 3, 5);
483: logln(z.getOffset(1, 97, 3, 5, 7, 0) + " "
484: + tempcal.getTime());
485: tempcal.set(1997, 3, 6);
486: logln(z.getOffset(1, 97, 3, 6, 1, 0) + " "
487: + tempcal.getTime());
488: tempcal.set(1997, 3, 7);
489: logln(z.getOffset(1, 97, 3, 7, 2, 0) + " "
490: + tempcal.getTime());
491: }
492: }
493:
494: //----------------------------------------------------------------------
495: // Can't do any of these without a public inDaylightTime in GC
496: //----------------------------------------------------------------------
497:
498: // static GregorianCalendar cal = new GregorianCalendar();
499: //
500: // static void _testUsingBinarySearch(Date d, boolean startsInDST)
501: // {
502: // // Given a date with a year start, find the Daylight onset
503: // // and end. The given date should be 1/1/xx in some year.
504: //
505: // // Use a binary search, assuming that we have a Standard
506: // // time at the midpoint.
507: // long min = d.getTime();
508: // long max = min + (long)(365.25 / 2 * ONE_DAY);
509: //
510: // // First check the max
511: // cal.setTime(new Date(max));
512: // if (cal.inDaylightTime() == startsInDST)
513: // {
514: // logln("Error: inDaylightTime(" + (new Date(max)) + ") != " + (!startsInDST));
515: // }
516: //
517: // cal.setTime(d);
518: // if (cal.inDaylightTime() != startsInDST)
519: // {
520: // logln("Error: inDaylightTime(" + d + ") != " + startsInDST);
521: // }
522: //
523: // while ((max - min) > INTERVAL)
524: // {
525: // long mid = (min + max) >> 1;
526: // cal.setTime(new Date(mid));
527: // if (cal.inDaylightTime() == startsInDST)
528: // {
529: // min = mid;
530: // }
531: // else
532: // {
533: // max = mid;
534: // }
535: // }
536: //
537: // logln("Binary Search Before: " + showDate(min));
538: // logln("Binary Search After: " + showDate(max));
539: // }
540: //
541: // static void _testUsingMillis(Date d, boolean startsInDST)
542: // {
543: // long millis = d.getTime();
544: // long max = millis + (long)(370 * ONE_DAY); // A year plus extra
545: //
546: // boolean lastDST = startsInDST;
547: // while (millis < max)
548: // {
549: // cal.setTime(new Date(millis));
550: // boolean inDaylight = cal.inDaylightTime();
551: //
552: // if (inDaylight != lastDST)
553: // {
554: // logln("Switch " + (inDaylight ? "into" : "out of")
555: // + " DST at " + (new Date(millis)));
556: // lastDST = inDaylight;
557: // }
558: //
559: // millis += 15*ONE_MINUTE;
560: // }
561: // }
562: //
563: // static void _testUsingFields(int y, boolean startsInDST)
564: // {
565: // boolean lastDST = startsInDST;
566: // for (int m = 0; m < 12; ++m)
567: // {
568: // for (int d = 1; d <= MONTH_LENGTH[m]; ++d)
569: // {
570: // for (int h = 0; h < 24; ++h)
571: // {
572: // for (int min = 0; min < 60; min += 15)
573: // {
574: // cal.clear();
575: // cal.set(y, m, d, h, min);
576: // boolean inDaylight = cal.inDaylightTime();
577: // if (inDaylight != lastDST)
578: // {
579: // lastDST = inDaylight;
580: // log("Switch " + (lastDST ? "into" : "out of")
581: // + " DST at " + y + "/" + (m+1) + "/" + d
582: // + " " + showNN(h) + ":" + showNN(min));
583: // logln(" " + cal.getTime());
584: //
585: // cal.set(y, m, d, h-1, 45);
586: // log("Before = "
587: //+ y + "/" + (m+1) + "/" + d
588: //+ " " + showNN(h-1) + ":" + showNN(45));
589: // logln(" " + cal.getTime());
590: // }
591: // }
592: // }
593: // }
594: // }
595: // }
596: //
597: // public void Test1()
598: // {
599: // logln(Locale.getDefault().getDisplayName());
600: // logln(TimeZone.getDefault().getID());
601: // logln(new Date(0));
602: //
603: // if (true)
604: // {
605: // logln("========================================");
606: // _testUsingBinarySearch(new Date(97,0,1), false);
607: // logln("========================================");
608: // _testUsingBinarySearch(new Date(97,6,1), true);
609: // }
610: //
611: // if (true)
612: // {
613: // logln("========================================");
614: // logln("Stepping using millis");
615: // _testUsingMillis(new Date(97,0,1), false);
616: // }
617: //
618: // if (true)
619: // {
620: // logln("========================================");
621: // logln("Stepping using fields");
622: // _testUsingFields(1997, false);
623: // }
624: //
625: // if (false)
626: // {
627: // cal.clear();
628: // cal.set(1997, 3, 5, 10, 0);
629: // // cal.inDaylightTime();
630: // logln("Date = " + cal.getTime());
631: // logln("Millis = " + cal.getTime().getTime()/3600000);
632: // }
633: // }
634:
635: //----------------------------------------------------------------------
636: //----------------------------------------------------------------------
637: //----------------------------------------------------------------------
638:
639: void _testUsingBinarySearch(SimpleTimeZone tz, Date d,
640: long expectedBoundary) {
641: // Given a date with a year start, find the Daylight onset
642: // and end. The given date should be 1/1/xx in some year.
643:
644: // Use a binary search, assuming that we have a Standard
645: // time at the midpoint.
646: long min = d.getTime();
647: long max = min + (long) (365.25 / 2 * ONE_DAY);
648:
649: // First check the boundaries
650: boolean startsInDST = tz.inDaylightTime(d);
651:
652: if (tz.inDaylightTime(new Date(max)) == startsInDST) {
653: errln("Error: inDaylightTime(" + (new Date(max)) + ") != "
654: + (!startsInDST));
655: }
656:
657: while ((max - min) > INTERVAL) {
658: long mid = (min + max) >> 1;
659: if (tz.inDaylightTime(new Date(mid)) == startsInDST) {
660: min = mid;
661: } else {
662: max = mid;
663: }
664: }
665:
666: logln("Binary Search Before: " + showDate(min));
667: logln("Binary Search After: " + showDate(max));
668:
669: long mindelta = expectedBoundary - min;
670: // not used long maxdelta = max - expectedBoundary;
671: if (mindelta >= 0 && mindelta <= INTERVAL && mindelta >= 0
672: && mindelta <= INTERVAL)
673: logln("PASS: Expected boundary at " + expectedBoundary);
674: else
675: errln("FAIL: Expected boundary at " + expectedBoundary);
676: }
677:
678: /*
679: static void _testUsingMillis(Date d, boolean startsInDST)
680: {
681: long millis = d.getTime();
682: long max = millis + (long)(370 * ONE_DAY); // A year plus extra
683:
684: boolean lastDST = startsInDST;
685: while (millis < max)
686: {
687: cal.setTime(new Date(millis));
688: boolean inDaylight = cal.inDaylightTime();
689:
690: if (inDaylight != lastDST)
691: {
692: logln("Switch " + (inDaylight ? "into" : "out of")
693: + " DST at " + (new Date(millis)));
694: lastDST = inDaylight;
695: }
696:
697: millis += 15*ONE_MINUTE;
698: }
699: }
700: */
701:
702: /**
703: * Test new rule formats.
704: */
705: public void TestNewRules() {
706: //logln(Locale.getDefault().getDisplayName());
707: //logln(TimeZone.getDefault().getID());
708: //logln(new Date(0));
709:
710: if (true) {
711: // Doesn't matter what the default TimeZone is here, since we
712: // are creating our own TimeZone objects.
713:
714: SimpleTimeZone tz;
715: java.util.Calendar tempcal = java.util.Calendar
716: .getInstance();
717: tempcal.clear();
718:
719: logln("-----------------------------------------------------------------");
720: logln("Aug 2ndTues .. Mar 15");
721: tz = new SimpleTimeZone(-8 * ONE_HOUR, "Test_1",
722: Calendar.AUGUST, 2, Calendar.TUESDAY, 2 * ONE_HOUR,
723: Calendar.MARCH, 15, 0, 2 * ONE_HOUR);
724: //logln(tz.toString());
725: logln("========================================");
726: tempcal.set(1997, 0, 1);
727: _testUsingBinarySearch(tz, tempcal.getTime(), 858416400000L);
728: logln("========================================");
729: tempcal.set(1997, 6, 1);
730: _testUsingBinarySearch(tz, tempcal.getTime(), 871380000000L);
731:
732: logln("-----------------------------------------------------------------");
733: logln("Apr Wed>=14 .. Sep Sun<=20");
734: tz = new SimpleTimeZone(-8 * ONE_HOUR, "Test_2",
735: Calendar.APRIL, 14, -Calendar.WEDNESDAY,
736: 2 * ONE_HOUR, Calendar.SEPTEMBER, -20,
737: -Calendar.SUNDAY, 2 * ONE_HOUR);
738: //logln(tz.toString());
739: logln("========================================");
740: tempcal.set(1997, 0, 1);
741: _testUsingBinarySearch(tz, tempcal.getTime(), 861184800000L);
742: logln("========================================");
743: tempcal.set(1997, 6, 1);
744: _testUsingBinarySearch(tz, tempcal.getTime(), 874227600000L);
745: }
746:
747: /*
748: if (true)
749: {
750: logln("========================================");
751: logln("Stepping using millis");
752: _testUsingMillis(new Date(97,0,1), false);
753: }
754:
755: if (true)
756: {
757: logln("========================================");
758: logln("Stepping using fields");
759: _testUsingFields(1997, false);
760: }
761:
762: if (false)
763: {
764: cal.clear();
765: cal.set(1997, 3, 5, 10, 0);
766: // cal.inDaylightTime();
767: logln("Date = " + cal.getTime());
768: logln("Millis = " + cal.getTime().getTime()/3600000);
769: }
770: */
771: }
772:
773: //----------------------------------------------------------------------
774: //----------------------------------------------------------------------
775: //----------------------------------------------------------------------
776: // Long Bug
777: //----------------------------------------------------------------------
778: //----------------------------------------------------------------------
779: //----------------------------------------------------------------------
780:
781: //public void Test3()
782: //{
783: // findDaylightBoundaryUsingTimeZone(new Date(97,6,1), true);
784: //}
785:
786: /**
787: * Find boundaries by stepping.
788: */
789: void findBoundariesStepwise(int year, long interval, TimeZone z,
790: int expectedChanges) {
791: java.util.Calendar tempcal = java.util.Calendar.getInstance();
792: tempcal.clear();
793: tempcal.set(year, Calendar.JANUARY, 1);
794: Date d = tempcal.getTime();
795: long time = d.getTime(); // ms
796: long limit = time + ONE_YEAR + ONE_DAY;
797: boolean lastState = z.inDaylightTime(d);
798: int changes = 0;
799: logln("-- Zone " + z.getID() + " starts in " + year
800: + " with DST = " + lastState);
801: logln("useDaylightTime = " + z.useDaylightTime());
802: while (time < limit) {
803: d.setTime(time);
804: boolean state = z.inDaylightTime(d);
805: if (state != lastState) {
806: logln((state ? "Entry " : "Exit ") + "at " + d);
807: lastState = state;
808: ++changes;
809: }
810: time += interval;
811: }
812: if (changes == 0) {
813: if (!lastState && !z.useDaylightTime())
814: logln("No DST");
815: else
816: errln("FAIL: DST all year, or no DST with true useDaylightTime");
817: } else if (changes != 2) {
818: errln("FAIL: " + changes
819: + " changes seen; should see 0 or 2");
820: } else if (!z.useDaylightTime()) {
821: errln("FAIL: useDaylightTime false but 2 changes seen");
822: }
823: if (changes != expectedChanges) {
824: errln("FAIL: " + changes + " changes seen; expected "
825: + expectedChanges);
826: }
827: }
828:
829: public void TestStepwise() {
830: findBoundariesStepwise(1997, ONE_DAY,
831: safeGetTimeZone("America/New_York"), 2);
832: // disabled Oct 2003 aliu; ACT could mean anything, depending on the underlying JDK, as of 2.8
833: // findBoundariesStepwise(1997, ONE_DAY, safeGetTimeZone("ACT"), 2);
834: findBoundariesStepwise(1997, ONE_DAY,
835: safeGetTimeZone("America/Phoenix"), 0); // Added 3Jan01
836: findBoundariesStepwise(1997, ONE_DAY,
837: safeGetTimeZone(AUSTRALIA), 2);
838: }
839: }
|