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