• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /**
5  *******************************************************************************
6  * Copyright (C) 2000-2016, International Business Machines Corporation and
7  * others. All Rights Reserved.
8  *******************************************************************************
9  */
10 
11 package ohos.global.icu.dev.test.timezone;
12 
13 import java.io.ByteArrayInputStream;
14 import java.io.ByteArrayOutputStream;
15 import java.io.IOException;
16 import java.io.ObjectInputStream;
17 import java.io.ObjectOutputStream;
18 import java.lang.reflect.InvocationTargetException;
19 import java.util.Arrays;
20 import java.util.Date;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Set;
24 
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.junit.runners.JUnit4;
28 
29 import ohos.global.icu.dev.test.TestFmwk;
30 import ohos.global.icu.impl.ICUData;
31 import ohos.global.icu.impl.TimeZoneAdapter;
32 import ohos.global.icu.text.SimpleDateFormat;
33 import ohos.global.icu.util.BasicTimeZone;
34 import ohos.global.icu.util.Calendar;
35 import ohos.global.icu.util.DateTimeRule;
36 import ohos.global.icu.util.GregorianCalendar;
37 import ohos.global.icu.util.InitialTimeZoneRule;
38 import ohos.global.icu.util.RuleBasedTimeZone;
39 import ohos.global.icu.util.SimpleTimeZone;
40 import ohos.global.icu.util.TimeArrayTimeZoneRule;
41 import ohos.global.icu.util.TimeZone;
42 import ohos.global.icu.util.TimeZone.SystemTimeZoneType;
43 import ohos.global.icu.util.TimeZoneRule;
44 import ohos.global.icu.util.TimeZoneTransition;
45 import ohos.global.icu.util.ULocale;
46 import ohos.global.icu.util.UResourceBundle;
47 import ohos.global.icu.util.VTimeZone;
48 import ohos.global.icu.util.VersionInfo;
49 
50 
51 /**
52  * @test 1.22 99/09/21
53  * @bug 4028006 4044013 4096694 4107276 4107570 4112869 4130885
54  * @summary test TimeZone
55  * @build TimeZoneTest
56  */
57 
58 @RunWith(JUnit4.class)
59 public class TimeZoneTest extends TestFmwk
60 {
61     static final int millisPerHour = 3600000;
62 
63     // Some test case data is current date/tzdata version sensitive and producing errors
64     // when year/rule are changed. Although we want to keep our eyes on test failures
65     // caused by tzdata changes while development, keep maintaining test data in maintenance
66     // stream is a little bit hassle. ICU 49 or later versions are using minor version field
67     // to indicate a development build (0) or official release build (others). For development
68     // builds, a test failure triggers an error, while release builds only report them in
69     // verbose mode with logln.
70     static final boolean isDevelopmentBuild = (VersionInfo.ICU_VERSION.getMinor() == 0);
71 
72     /**
73      * NOTE: As of ICU 2.8, the mapping of 3-letter legacy aliases
74      * to `real' Olson IDs is under control of the underlying JDK.
75      * This test may fail on one JDK and pass on another; don't be
76      * too concerned.  Alan
77      *
78      * Bug 4130885
79      * Certain short zone IDs, used since 1.1.x, are incorrect.
80      *
81      * The worst of these is:
82      *
83      * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
84      * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
85      * or AZOST, depending on which zone is meant, but in no case is it CAT.
86      *
87      * Other wrong zone IDs:
88      *
89      * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
90      * GMT-5:00. European Central time is abbreviated CEST.
91      *
92      * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
93      * GMT-11:00. Solomon Island time is SBT.
94      *
95      * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
96      * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
97      *
98      * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
99      * another bug.] It should be "AKST". AST is Atlantic Standard Time,
100      * GMT-4:00.
101      *
102      * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
103      * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
104      * from MST with daylight savings.
105      *
106      * In addition to these problems, a number of zones are FAKE. That is, they
107      * don't match what people use in the real world.
108      *
109      * FAKE zones:
110      *
111      * EET (should be EEST)
112      * ART (should be EEST)
113      * MET (should be IRST)
114      * NET (should be AMST)
115      * PLT (should be PKT)
116      * BST (should be BDT)
117      * VST (should be ICT)
118      * CTT (should be CST) +
119      * ACT (should be CST) +
120      * AET (should be EST) +
121      * MIT (should be WST) +
122      * IET (should be EST) +
123      * PRT (should be AST) +
124      * CNT (should be NST)
125      * AGT (should be ARST)
126      * BET (should be EST) +
127      *
128      * + A zone with the correct name already exists and means something
129      * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
130      * used for Brazil (BET).
131      */
132     @Test
TestShortZoneIDs()133     public void TestShortZoneIDs() throws Exception {
134 
135         // Note: If the default TimeZone type is JDK, some time zones
136         // may differ from the test data below.  For example, "MST" on
137         // IBM JRE is an alias of "America/Denver" for supporting Java 1.1
138         // backward compatibility, while Olson tzdata (and ICU) treat it
139         // as -7hour fixed offset/no DST.
140         boolean isJDKTimeZone = (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_JDK);
141         if (isJDKTimeZone) {
142             logln("Warning: Using JDK TimeZone.  Some test cases may not return expected results.");
143         }
144 
145         ZoneDescriptor[] REFERENCE_LIST = {
146             new ZoneDescriptor("HST", -600, false), // Olson northamerica -10:00
147             new ZoneDescriptor("AST", -540, true),  // ICU Link - America/Anchorage
148             new ZoneDescriptor("PST", -480, true),  // ICU Link - America/Los_Angeles
149             new ZoneDescriptor("PNT", -420, false), // ICU Link - America/Phoenix
150             new ZoneDescriptor("MST", -420, false), // updated Aug 2003 aliu
151             new ZoneDescriptor("CST", -360, true),  // Olson northamerica -7:00
152             new ZoneDescriptor("IET", -300, true),  // ICU Link - America/Indiana/Indianapolis
153             new ZoneDescriptor("EST", -300, false), // Olson northamerica -5:00
154             new ZoneDescriptor("PRT", -240, false), // ICU Link - America/Puerto_Rico
155             new ZoneDescriptor("CNT", -210, true),  // ICU Link - America/St_Johns
156             new ZoneDescriptor("AGT", -180, false), // ICU Link - America/Argentina/Buenos_Aires
157             // Per https://mm.icann.org/pipermail/tz-announce/2019-July/000056.html
158             // Brazil has canceled DST and will stay on standard time indefinitely.
159             new ZoneDescriptor("BET", -180, false),  // ICU Link - America/Sao_Paulo
160             new ZoneDescriptor("GMT", 0, false),    // Olson etcetera Link - Etc/GMT
161             new ZoneDescriptor("UTC", 0, false),    // Olson etcetera 0
162             new ZoneDescriptor("ECT", 60, true),    // ICU Link - Europe/Paris
163             new ZoneDescriptor("MET", 60, true),    // Olson europe 1:00 C-Eur
164             new ZoneDescriptor("CAT", 120, false),  // ICU Link - Africa/Harare
165             new ZoneDescriptor("ART", 120, false),  // ICU Link - Africa/Cairo
166             new ZoneDescriptor("EET", 120, true),   // Olson europe 2:00 EU
167             new ZoneDescriptor("EAT", 180, false),  // ICU Link - Africa/Addis_Ababa
168             new ZoneDescriptor("NET", 240, false),  // ICU Link - Asia/Yerevan
169             new ZoneDescriptor("PLT", 300, false),  // ICU Link - Asia/Karachi
170             new ZoneDescriptor("IST", 330, false),  // ICU Link - Asia/Kolkata
171             new ZoneDescriptor("BST", 360, false),  // ICU Link - Asia/Dhaka
172             new ZoneDescriptor("VST", 420, false),  // ICU Link - Asia/Ho_Chi_Minh
173             new ZoneDescriptor("CTT", 480, false),  // ICU Link - Asia/Shanghai
174             new ZoneDescriptor("JST", 540, false),  // ICU Link - Asia/Tokyo
175             new ZoneDescriptor("ACT", 570, false),  // ICU Link - Australia/Darwin
176             new ZoneDescriptor("AET", 600, true),   // ICU Link - Australia/Sydney
177             new ZoneDescriptor("SST", 660, false),  // ICU Link - Pacific/Guadalcanal
178             new ZoneDescriptor("NST", 720, true),   // ICU Link - Pacific/Auckland
179             new ZoneDescriptor("MIT", 780, true),   // ICU Link - Pacific/Apia
180 
181             new ZoneDescriptor("Etc/Unknown", 0, false),    // CLDR
182 
183             new ZoneDescriptor("SystemV/AST4ADT", -240, true),
184             new ZoneDescriptor("SystemV/EST5EDT", -300, true),
185             new ZoneDescriptor("SystemV/CST6CDT", -360, true),
186             new ZoneDescriptor("SystemV/MST7MDT", -420, true),
187             new ZoneDescriptor("SystemV/PST8PDT", -480, true),
188             new ZoneDescriptor("SystemV/YST9YDT", -540, true),
189             new ZoneDescriptor("SystemV/AST4", -240, false),
190             new ZoneDescriptor("SystemV/EST5", -300, false),
191             new ZoneDescriptor("SystemV/CST6", -360, false),
192             new ZoneDescriptor("SystemV/MST7", -420, false),
193             new ZoneDescriptor("SystemV/PST8", -480, false),
194             new ZoneDescriptor("SystemV/YST9", -540, false),
195             new ZoneDescriptor("SystemV/HST10", -600, false),
196         };
197 
198         for (int i=0; i<REFERENCE_LIST.length; ++i) {
199             ZoneDescriptor referenceZone = REFERENCE_LIST[i];
200             ZoneDescriptor currentZone = new ZoneDescriptor(TimeZone.getTimeZone(referenceZone.getID()));
201             if (referenceZone.equals(currentZone)) {
202                 logln("ok " + referenceZone);
203             }
204             else {
205                 if (!isDevelopmentBuild || isJDKTimeZone) {
206                     logln("Warning: Expected " + referenceZone +
207                             "; got " + currentZone);
208                 } else {
209                     errln("Fail: Expected " + referenceZone +
210                             "; got " + currentZone);
211                 }
212             }
213         }
214     }
215 
216     /**
217      * A descriptor for a zone; used to regress the short zone IDs.
218      */
219     static class ZoneDescriptor {
220         String id;
221         int offset; // In minutes
222         boolean daylight;
223 
ZoneDescriptor(TimeZone zone)224         ZoneDescriptor(TimeZone zone) {
225             this.id = zone.getID();
226             this.offset = zone.getRawOffset() / 60000;
227             this.daylight = zone.useDaylightTime();
228         }
229 
ZoneDescriptor(String id, int offset, boolean daylight)230         ZoneDescriptor(String id, int offset, boolean daylight) {
231             this.id = id;
232             this.offset = offset;
233             this.daylight = daylight;
234         }
235 
getID()236         public String getID() { return id; }
237 
238         @Override
equals(Object o)239         public boolean equals(Object o) {
240             ZoneDescriptor that = (ZoneDescriptor)o;
241             return that != null &&
242                 id.equals(that.id) &&
243                 offset == that.offset &&
244                 daylight == that.daylight;
245         }
246 
247         @Override
toString()248         public String toString() {
249             int min = offset;
250             char sign = '+';
251             if (min < 0) { sign = '-'; min = -min; }
252 
253             return "Zone[\"" + id + "\", GMT" + sign + (min/60) + ':' +
254                 (min%60<10?"0":"") + (min%60) + ", " +
255                 (daylight ? "Daylight" : "Standard") + "]";
256         }
257 
compare(Object o1, Object o2)258         public static int compare(Object o1, Object o2) {
259             ZoneDescriptor i1 = (ZoneDescriptor)o1;
260             ZoneDescriptor i2 = (ZoneDescriptor)o2;
261             if (i1.offset > i2.offset) return 1;
262             if (i1.offset < i2.offset) return -1;
263             if (i1.daylight && !i2.daylight) return 1;
264             if (!i1.daylight && i2.daylight) return -1;
265             return i1.id.compareTo(i2.id);
266         }
267     }
268 
269     /**
270      * As part of the VM fix (see CCC approved RFE 4028006, bug
271      * 4044013), TimeZone.getTimeZone() has been modified to recognize
272      * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
273      * GMT[+-]hh.  Test this behavior here.
274      *
275      * Bug 4044013
276      */
277     @Test
TestCustomParse()278     public void TestCustomParse() {
279         String[] DATA = {
280             // ID               offset(sec)     output ID
281             "GMT",              "0",            "GMT",      // system ID
282             "GMT-YOUR.AD.HERE", "0",            TimeZone.UNKNOWN_ZONE_ID,
283             "GMT0",             "0",            "GMT0",     // system ID
284             "GMT+0",            "0",            "GMT+0",    // system ID
285             "GMT+1",            "3600",         "GMT+01:00",
286             "GMT-0030",         "-1800",        "GMT-00:30",
287             "GMT+15:99",        "0",            TimeZone.UNKNOWN_ZONE_ID,
288             "GMT+",             "0",            TimeZone.UNKNOWN_ZONE_ID,
289             "GMT-",             "0",            TimeZone.UNKNOWN_ZONE_ID,
290             "GMT+0:",           "0",            TimeZone.UNKNOWN_ZONE_ID,
291             "GMT-:",            "0",            TimeZone.UNKNOWN_ZONE_ID,
292             "GMT+0010",         "600",          "GMT+00:10",
293             "GMT-10",           "-36000",       "GMT-10:00",
294             "GMT+30",           "0",            TimeZone.UNKNOWN_ZONE_ID,
295             "GMT-3:30",         "-12600",       "GMT-03:30",
296             "GMT-230",          "-9000",        "GMT-02:30",
297             "GMT+05:13:05",     "18785",        "GMT+05:13:05",
298             "GMT-71023",        "-25823",       "GMT-07:10:23",
299             "GMT+01:23:45:67",  "0",            TimeZone.UNKNOWN_ZONE_ID,
300             "GMT+01:234",       "0",            TimeZone.UNKNOWN_ZONE_ID,
301             "GMT-2:31:123",     "0",            TimeZone.UNKNOWN_ZONE_ID,
302             "GMT+3:75",         "0",            TimeZone.UNKNOWN_ZONE_ID,
303             "GMT-01010101",     "0",            TimeZone.UNKNOWN_ZONE_ID,
304         };
305         for (int i = 0; i < DATA.length; i += 3) {
306             String id = DATA[i];
307             int offset = Integer.parseInt(DATA[i+1]);
308             String expId = DATA[i+2];
309 
310             TimeZone zone = TimeZone.getTimeZone(id);
311             String gotID = zone.getID();
312             int gotOffset = zone.getRawOffset()/1000;
313 
314             logln(id + " -> " + gotID + " " + gotOffset);
315 
316             if (offset != gotOffset) {
317                 errln("FAIL: Unexpected offset for " + id + " - returned:" + gotOffset + " expected:" + offset);
318             }
319             if (!expId.equals(gotID)) {
320                 if (TimeZone.getDefaultTimeZoneType() != TimeZone.TIMEZONE_ICU) {
321                     logln("ID for " + id + " - returned:" + gotID + " expected:" + expId);
322                 } else {
323                     errln("FAIL: Unexpected ID for " + id + " - returned:" + gotID + " expected:" + expId);
324                 }
325             }
326         }
327     }
328 
329     /**
330      * Test the basic functionality of the getDisplayName() API.
331      *
332      * Bug 4112869
333      * Bug 4028006
334      *
335      * See also API change request A41.
336      *
337      * 4/21/98 - make smarter, so the test works if the ext resources
338      * are present or not.
339      */
340     @Test
TestDisplayName()341     public void TestDisplayName() {
342         TimeZone zone = TimeZone.getTimeZone("PST");
343         String name = zone.getDisplayName(Locale.ENGLISH);
344         logln("PST->" + name);
345 
346         // dlf - we now (3.4.1) return generic time
347         if (!name.equals("Pacific Time"))
348             errln("Fail: Expected \"Pacific Time\", got " + name +
349                   " for " + zone);
350 
351         //*****************************************************************
352         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
353         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
354         // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
355         //*****************************************************************
356 
357         // Test to allow the user to choose to get all the forms
358         // (z, zzzz, Z, ZZZZ, v, vvvv)
359         // todo: check to see whether we can test for all of pst, pdt, pt
360         Object[] DATA = {
361             // z and zzzz
362             Boolean.FALSE, new Integer(TimeZone.SHORT), "PST",
363             Boolean.TRUE,  new Integer(TimeZone.SHORT), "PDT",
364             Boolean.FALSE, new Integer(TimeZone.LONG),  "Pacific Standard Time",
365             Boolean.TRUE,  new Integer(TimeZone.LONG),  "Pacific Daylight Time",
366             // v and vvvv
367             Boolean.FALSE, new Integer(TimeZone.SHORT_GENERIC), "PT",
368             Boolean.TRUE,  new Integer(TimeZone.SHORT_GENERIC), "PT",
369             Boolean.FALSE, new Integer(TimeZone.LONG_GENERIC),  "Pacific Time",
370             Boolean.TRUE,  new Integer(TimeZone.LONG_GENERIC),  "Pacific Time",
371             // z and ZZZZ
372             Boolean.FALSE, new Integer(TimeZone.SHORT_GMT), "-0800",
373             Boolean.TRUE,  new Integer(TimeZone.SHORT_GMT), "-0700",
374             Boolean.FALSE, new Integer(TimeZone.LONG_GMT),  "GMT-08:00",
375             Boolean.TRUE,  new Integer(TimeZone.LONG_GMT),  "GMT-07:00",
376             // V and VVVV
377             Boolean.FALSE, new Integer(TimeZone.SHORT_COMMONLY_USED), "PST",
378             Boolean.TRUE,  new Integer(TimeZone.SHORT_COMMONLY_USED), "PDT",
379             Boolean.FALSE, new Integer(TimeZone.GENERIC_LOCATION),  "Los Angeles Time",
380             Boolean.TRUE,  new Integer(TimeZone.GENERIC_LOCATION),  "Los Angeles Time",
381         };
382 
383         for (int i=0; i<DATA.length; i+=3) {
384             name = zone.getDisplayName(((Boolean)DATA[i]).booleanValue(),
385                                        ((Integer)DATA[i+1]).intValue(),
386                                        Locale.ENGLISH);
387             if (!name.equals(DATA[i+2]))
388                 errln("Fail: Expected " + DATA[i+2] + "; got " + name);
389         }
390 
391         // Make sure that we don't display the DST name by constructing a fake
392         // PST zone that has DST all year long.
393         // dlf - this test is no longer relevant, we display generic time now
394         //    so the behavior of the timezone doesn't matter
395         SimpleTimeZone zone2 = new SimpleTimeZone(0, "PST");
396         zone2.setStartRule(Calendar.JANUARY, 1, 0);
397         zone2.setEndRule(Calendar.DECEMBER, 31, 86399999);
398         logln("Modified PST inDaylightTime->" + zone2.inDaylightTime(new Date()));
399         name = zone2.getDisplayName(Locale.ENGLISH);
400         logln("Modified PST->" + name);
401         if (!name.equals("Pacific Time"))
402             errln("Fail: Expected \"Pacific Time\"");
403 
404         // Make sure we get the default display format for Locales
405         // with no display name data.
406         Locale mt_MT = new Locale("mt", "MT");
407         name = zone.getDisplayName(mt_MT);
408         //*****************************************************************
409         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
410         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
411         // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
412         //*****************************************************************
413         logln("PST(mt_MT)->" + name);
414 
415         // Now be smart -- check to see if zh resource is even present.
416         // If not, we expect the en fallback behavior.
417 
418         // in icu4j 2.1 we know we have the zh_CN locale data, though it's incomplete
419 //    /"DateFormatZoneData",
420         UResourceBundle enRB = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,Locale.ENGLISH);
421         UResourceBundle mtRB = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, mt_MT);
422         boolean noZH = enRB == mtRB;
423 
424         if (noZH) {
425             logln("Warning: Not testing the mt_MT behavior because resource is absent");
426             if (!name.equals("Pacific Standard Time"))
427                 errln("Fail: Expected Pacific Standard Time for PST in mt_MT but got ");
428         }
429         // dlf - we will use generic time, or if unavailable, GMT for standard time in the zone
430         //     - we now (3.4.1) have localizations for this zone, so change test string
431         else if(!name.equals("\u0126in ta\u2019 Los Angeles") &&
432             !name.equals("GMT-08:00") &&
433             !name.equals("GMT-8:00") &&
434             !name.equals("GMT-0800") &&
435             !name.equals("GMT-800")) {
436 
437             errln("Fail: got '" + name + "', expected GMT-08:00 or something similar\n" +
438                   "************************************************************\n" +
439                   "THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED\n" +
440                   "************************************************************");
441         }
442 
443         // Now try a non-existent zone
444         zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
445         name = zone2.getDisplayName(Locale.ENGLISH);
446         logln("GMT+90min->" + name);
447         if (!name.equals("GMT+01:30") &&
448             !name.equals("GMT+1:30") &&
449             !name.equals("GMT+0130") &&
450             !name.equals("GMT+130"))
451             errln("Fail: Expected GMT+01:30 or something similar");
452 
453         // cover getDisplayName() - null arg
454         ULocale save = ULocale.getDefault();
455         ULocale.setDefault(ULocale.US);
456         name = zone2.getDisplayName();
457         logln("GMT+90min->" + name + "for default display locale");
458         if (!name.equals("GMT+01:30") &&
459             !name.equals("GMT+1:30") &&
460             !name.equals("GMT+0130") &&
461             !name.equals("GMT+130"))
462             errln("Fail: Expected GMT+01:30 or something similar");
463         ULocale.setDefault(save);
464 
465     }
466 
467 
468     @Test
TestDisplayName2()469     public void TestDisplayName2() {
470         Date now = new Date();
471 
472         String[] timezones = {"America/Chicago", "Europe/Moscow", "Europe/Rome", "Asia/Shanghai", "WET" };
473         String[] locales = {"en", "fr", "de", "ja", "zh_TW", "zh_Hans" };
474         for (int j = 0; j < locales.length; ++j) {
475             ULocale locale = new ULocale(locales[j]);
476             for (int i = 0; i < timezones.length; ++i) {
477                 TimeZone tz = TimeZone.getTimeZone(timezones[i]);
478                 String displayName0 = tz.getDisplayName(locale);
479                 SimpleDateFormat dt = new SimpleDateFormat("vvvv", locale);
480                 dt.setTimeZone(tz);
481                 String displayName1 = dt.format(now);  // date value _does_ matter if we fallback to GMT
482                 logln(locale.getDisplayName() + ", " + tz.getID() + ": " + displayName0);
483                 if (!displayName1.equals(displayName0)) {
484                     // This could happen when the date used is in DST,
485                     // because TimeZone.getDisplayName(ULocale) may use
486                     // localized GMT format for the time zone's standard
487                     // time.
488                     if (tz.inDaylightTime(now)) {
489                         // Try getDisplayName with daylight argument
490                         displayName0 = tz.getDisplayName(true, TimeZone.LONG_GENERIC, locale);
491                     }
492                     if (!displayName1.equals(displayName0)) {
493                         errln(locale.getDisplayName() + ", " + tz.getID() +
494                                 ": expected " + displayName1 + " but got: " + displayName0);
495                     }
496                 }
497             }
498         }
499     }
500 
501     @Test
TestGenericAPI()502     public void TestGenericAPI() {
503         // It's necessary to use a real existing time zone here, some systems (Android) will not
504         // accept any arbitrary TimeZone object to be used as the default.
505         String id = "GMT-12:00";
506         int offset = -12 * 60 * 60 * 1000;
507 
508         SimpleTimeZone zone = new SimpleTimeZone(offset, id);
509         if (zone.useDaylightTime()) errln("FAIL: useDaylightTime should return false");
510 
511         TimeZone zoneclone = (TimeZone)zone.clone();
512         if (!zoneclone.equals(zone)) errln("FAIL: clone or operator== failed");
513         zoneclone.setID("abc");
514         if (zoneclone.equals(zone)) errln("FAIL: clone or operator!= failed");
515 
516         zoneclone = (TimeZone)zone.clone();
517         if (!zoneclone.equals(zone)) errln("FAIL: clone or operator== failed");
518         zoneclone.setRawOffset(45678);
519         if (zoneclone.equals(zone)) errln("FAIL: clone or operator!= failed");
520 
521         // set/getDefault
522         TimeZone saveDefault = TimeZone.getDefault();
523         TimeZone.setDefault(zone);
524         TimeZone defaultzone = TimeZone.getDefault();
525         if (defaultzone == zone) {
526             errln("FAIL: Default object is identical, not clone");
527         }
528         if (!defaultzone.equals(zone)) {
529             errln("FAIL: Default object is not equal");
530         }
531         java.util.TimeZone javaDefault = java.util.TimeZone.getDefault();
532         if (offset != javaDefault.getRawOffset() || !id.equals(javaDefault.getID())) {
533             errln("FAIL: Java runtime default time zone is not synchronized");
534         }
535 
536         String anotheId = "AnotherZone";
537         int anotherOffset = 23456;
538         SimpleTimeZone anotherZone = new SimpleTimeZone(anotherOffset, anotheId);
539         TimeZone.setICUDefault(anotherZone);
540         TimeZone newICUDefaultZone = TimeZone.getDefault();
541         if (newICUDefaultZone == anotherZone) {
542             errln("FAIL: New ICU default object is identical, not clone");
543         }
544         if (!newICUDefaultZone.equals(anotherZone)) {
545             errln("FAIL: New ICU default object is not equal");
546         }
547         javaDefault = java.util.TimeZone.getDefault();
548         if (offset != javaDefault.getRawOffset() || !id.equals(javaDefault.getID())) {
549             errln("FAIL: Java runtime default time zone was updated");
550         }
551 
552         TimeZone.setDefault(saveDefault);
553 
554 
555 
556         String tzver = TimeZone.getTZDataVersion();
557         if (tzver.length() != 5 /* 4 digits + 1 letter */) {
558             errln("FAIL: getTZDataVersion returned " + tzver);
559         } else {
560             logln("PASS: tzdata version: " + tzver);
561         }
562     }
563 
564     @Test
TestRuleAPI()565     public void TestRuleAPI()
566     {
567         // ErrorCode status = ZERO_ERROR;
568 
569         int offset = (int)(60*60*1000*1.75); // Pick a weird offset
570         SimpleTimeZone zone = new SimpleTimeZone(offset, "TestZone");
571         if (zone.useDaylightTime()) errln("FAIL: useDaylightTime should return false");
572 
573         // Establish our expected transition times.  Do this with a non-DST
574         // calendar with the (above) declared local offset.
575         GregorianCalendar gc = new GregorianCalendar(zone);
576         gc.clear();
577         gc.set(1990, Calendar.MARCH, 1);
578         long marchOneStd = gc.getTime().getTime(); // Local Std time midnight
579         gc.clear();
580         gc.set(1990, Calendar.JULY, 1);
581         long julyOneStd = gc.getTime().getTime(); // Local Std time midnight
582 
583         // Starting and ending hours, WALL TIME
584         int startHour = (int)(2.25 * 3600000);
585         int endHour   = (int)(3.5  * 3600000);
586 
587         zone.setStartRule(Calendar.MARCH, 1, 0, startHour);
588         zone.setEndRule  (Calendar.JULY,  1, 0, endHour);
589 
590         gc = new GregorianCalendar(zone);
591         // if (failure(status, "new GregorianCalendar")) return;
592 
593         long marchOne = marchOneStd + startHour;
594         long julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
595 
596         long expMarchOne = 636251400000L;
597         if (marchOne != expMarchOne)
598         {
599             errln("FAIL: Expected start computed as " + marchOne +
600                   " = " + new Date(marchOne));
601             logln("      Should be                  " + expMarchOne +
602                   " = " + new Date(expMarchOne));
603         }
604 
605         long expJulyOne = 646793100000L;
606         if (julyOne != expJulyOne)
607         {
608             errln("FAIL: Expected start computed as " + julyOne +
609                   " = " + new Date(julyOne));
610             logln("      Should be                  " + expJulyOne +
611                   " = " + new Date(expJulyOne));
612         }
613 
614         Calendar cal1 = Calendar.getInstance();
615         cal1.set(1990, Calendar.JANUARY, 1);
616         Calendar cal2 = Calendar.getInstance();
617         cal2.set(1990, Calendar.JUNE, 1);
618         _testUsingBinarySearch(zone, cal1.getTimeInMillis(),
619                                cal2.getTimeInMillis(), marchOne);
620         cal1.set(1990, Calendar.JUNE, 1);
621         cal2.set(1990, Calendar.DECEMBER, 31);
622         _testUsingBinarySearch(zone, cal1.getTimeInMillis(),
623                                cal2.getTimeInMillis(), julyOne);
624 
625         if (zone.inDaylightTime(new Date(marchOne - 1000)) ||
626             !zone.inDaylightTime(new Date(marchOne)))
627             errln("FAIL: Start rule broken");
628         if (!zone.inDaylightTime(new Date(julyOne - 1000)) ||
629             zone.inDaylightTime(new Date(julyOne)))
630             errln("FAIL: End rule broken");
631 
632         zone.setStartYear(1991);
633         if (zone.inDaylightTime(new Date(marchOne)) ||
634             zone.inDaylightTime(new Date(julyOne - 1000)))
635             errln("FAIL: Start year broken");
636 
637         // failure(status, "TestRuleAPI");
638         // delete gc;
639         // delete zone;
640     }
641 
_testUsingBinarySearch(SimpleTimeZone tz, long min, long max, long expectedBoundary)642     void _testUsingBinarySearch(SimpleTimeZone tz, long min, long max, long expectedBoundary)
643     {
644         // ErrorCode status = ZERO_ERROR;
645         boolean startsInDST = tz.inDaylightTime(new Date(min));
646         // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
647         if (tz.inDaylightTime(new Date(max)) == startsInDST) {
648             logln("Error: inDaylightTime(" + new Date(max) + ") != " + (!startsInDST));
649             return;
650         }
651         // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
652         while ((max - min) > INTERVAL) {
653             long mid = (min + max) / 2;
654             if (tz.inDaylightTime(new Date(mid)) == startsInDST) {
655                 min = mid;
656             }
657             else {
658                 max = mid;
659             }
660             // if (failure(status, "SimpleTimeZone::inDaylightTime")) return;
661         }
662         logln("Binary Search Before: " + min + " = " + new Date(min));
663         logln("Binary Search After:  " + max + " = " + new Date(max));
664         long mindelta = expectedBoundary - min;
665         // not used long maxdelta = max - expectedBoundary;
666         if (mindelta >= 0 &&
667             mindelta <= INTERVAL &&
668             mindelta >= 0 &&
669             mindelta <= INTERVAL)
670             logln("PASS: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
671         else
672             errln("FAIL: Expected bdry:  " + expectedBoundary + " = " + new Date(expectedBoundary));
673     }
674 
675     static final int INTERVAL = 100;
676 
677     // Bug 006; verify the offset for a specific zone.
678     @Test
TestPRTOffset()679     public void TestPRTOffset()
680     {
681         TimeZone tz = TimeZone.getTimeZone( "PRT" );
682         if( tz == null ) {
683             errln( "FAIL: TimeZone(PRT) is null" );
684         }
685         else{
686             if (tz.getRawOffset() != (-4*millisPerHour))
687                 warnln("FAIL: Offset for PRT should be -4, got " +
688                       tz.getRawOffset() / (double)millisPerHour);
689         }
690 
691     }
692 
693     // Test various calls
694     @Test
TestVariousAPI518()695     public void TestVariousAPI518()
696     {
697         TimeZone time_zone = TimeZone.getTimeZone("PST");
698         Calendar cal = Calendar.getInstance();
699         cal.set(1997, Calendar.APRIL, 30);
700         Date d = cal.getTime();
701 
702         logln("The timezone is " + time_zone.getID());
703 
704         if (time_zone.inDaylightTime(d) != true)
705             errln("FAIL: inDaylightTime returned false");
706 
707         if (time_zone.useDaylightTime() != true)
708             errln("FAIL: useDaylightTime returned false");
709 
710         if (time_zone.getRawOffset() != -8*millisPerHour)
711             errln( "FAIL: getRawOffset returned wrong value");
712 
713         GregorianCalendar gc = new GregorianCalendar();
714         gc.setTime(d);
715         if (time_zone.getOffset(GregorianCalendar.AD, gc.get(GregorianCalendar.YEAR), gc.get(GregorianCalendar.MONTH),
716                                 gc.get(GregorianCalendar.DAY_OF_MONTH),
717                                 gc.get(GregorianCalendar.DAY_OF_WEEK), 0)
718             != -7*millisPerHour)
719             errln("FAIL: getOffset returned wrong value");
720     }
721 
722     // Test getAvailableID API
723     @Test
TestGetAvailableIDs913()724     public void TestGetAvailableIDs913()
725     {
726         StringBuffer buf = new StringBuffer("TimeZone.getAvailableIDs() = { ");
727         String[] s = TimeZone.getAvailableIDs();
728         for (int i=0; i<s.length; ++i)
729         {
730             if (i > 0) buf.append(", ");
731             buf.append(s[i]);
732         }
733         buf.append(" };");
734         logln(buf.toString());
735 
736         buf.setLength(0);
737         buf.append("TimeZone.getAvailableIDs(GMT+02:00) = { ");
738         s = TimeZone.getAvailableIDs(+2 * 60 * 60 * 1000);
739         for (int i=0; i<s.length; ++i)
740         {
741             if (i > 0) buf.append(", ");
742             buf.append(s[i]);
743         }
744         buf.append(" };");
745         logln(buf.toString());
746 
747         TimeZone tz = TimeZone.getTimeZone("PST");
748         if (tz != null)
749             logln("getTimeZone(PST) = " + tz.getID());
750         else
751             errln("FAIL: getTimeZone(PST) = null");
752 
753         tz = TimeZone.getTimeZone("America/Los_Angeles");
754         if (tz != null)
755             logln("getTimeZone(America/Los_Angeles) = " + tz.getID());
756         else
757             errln("FAIL: getTimeZone(PST) = null");
758 
759         // Bug 4096694
760         tz = TimeZone.getTimeZone("NON_EXISTENT");
761         if (tz == null)
762             errln("FAIL: getTimeZone(NON_EXISTENT) = null");
763         else if (!tz.getID().equals(TimeZone.UNKNOWN_ZONE_ID))
764             errln("FAIL: getTimeZone(NON_EXISTENT) = " + tz.getID());
765     }
766 
767     @Test
TestGetAvailableIDsNew()768     public void TestGetAvailableIDsNew() {
769         Set<String> any = TimeZone.getAvailableIDs(SystemTimeZoneType.ANY, null, null);
770         Set<String> canonical = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
771         Set<String> canonicalLoc = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL_LOCATION, null, null);
772 
773         checkContainsAll(any, "ANY", canonical, "CANONICAL");
774         checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC");
775 
776         Set<String> any_US = TimeZone.getAvailableIDs(SystemTimeZoneType.ANY, "US", null);
777         Set<String> canonical_US = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, "US", null);
778         Set<String> canonicalLoc_US = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL_LOCATION, "US", null);
779 
780         checkContainsAll(any, "ANY", any_US, "ANY_US");
781         checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US");
782         checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US");
783 
784         checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US");
785         checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US");
786 
787         final int HOUR = 60*60*1000;
788         Set<String> any_W5 = TimeZone.getAvailableIDs(SystemTimeZoneType.ANY, null, -5 * HOUR);
789         Set<String> any_CA_W5 = TimeZone.getAvailableIDs(SystemTimeZoneType.ANY, "CA", -5 * HOUR);
790 
791         checkContainsAll(any, "ANY", any_W5, "ANY_W5");
792         checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5");
793 
794         boolean[] isSystemID = new boolean[1];
795 
796         // An ID in any set, but not in canonical set must not be a canonical ID
797         for (String id : any) {
798             if (canonical.contains(id)) {
799                 continue;
800             }
801             String cid = TimeZone.getCanonicalID(id, isSystemID);
802             if (id.equals(cid)) {
803                 errln("FAIL: canonical ID [" + id + "] is not in CANONICAL");
804             }
805             if (!isSystemID[0]) {
806                 errln("FAIL: ANY contains non-system ID: " + id);
807             }
808         }
809 
810         // canonical set must contains only canonical IDs
811         for (String id : canonical) {
812             String cid = TimeZone.getCanonicalID(id, isSystemID);
813             if (!id.equals(cid)) {
814                 errln("FAIL: CANONICAL contains non-canonical ID: " + id);
815             }
816             if (!isSystemID[0]) {
817                 errln("FAIL: CANONICAL contains non-system ID: " + id);
818             }
819         }
820 
821         // canonicalLoc set must contains only canonical location IDs
822         for (String id : canonicalLoc) {
823             String cid = TimeZone.getCanonicalID(id, isSystemID);
824             if (!id.equals(cid)) {
825                 errln("FAIL: CANONICAL contains non-canonical ID: " + id);
826             }
827             if (!isSystemID[0]) {
828                 errln("FAIL: CANONICAL contains non-system ID: " + id);
829             }
830             String region = TimeZone.getRegion(id);
831             if (region.equals("001")) {
832                 errln("FAIL: CANONICALLOC contains non location zone: " + id);
833             }
834         }
835 
836         // any_US must contain only US zones
837         for (String id : any_US) {
838             String region = TimeZone.getRegion(id);
839             if (!region.equals("US")) {
840                 errln("FAIL: ANY_US contains non-US zone ID: " + id);
841             }
842         }
843 
844         // any_W5 must contain only GMT-05:00 zones
845         for (String id : any_W5) {
846             TimeZone tz = TimeZone.getTimeZone(id);
847             if (tz.getRawOffset() != -5 * HOUR) {
848                 errln("FAIL: ANY_W5 contains a zone whose offset is not -5:00: " + id);
849             }
850         }
851 
852         // No US zones with GMT+14:00
853         Set<String> any_US_E14 = TimeZone.getAvailableIDs(SystemTimeZoneType.ANY, "US", 14 * HOUR);
854         if (!any_US_E14.isEmpty()) {
855             errln("FAIL: ANY_US_E14 must be empty");
856         }
857     }
858 
checkContainsAll(Set<String> set1, String name1, Set<String> set2, String name2)859     private void checkContainsAll(Set<String> set1, String name1, Set<String> set2, String name2) {
860         if (!set1.containsAll(set2)) {
861             StringBuilder buf = new StringBuilder();
862             for (String s : set2) {
863                 if (!set1.contains(s)) {
864                     if (buf.length() != 0) {
865                         buf.append(",");
866                     }
867                     buf.append(s);
868                 }
869             }
870             errln("FAIL: " + name1 + " does not contain all of " + name2 + " - missing: {" + buf + "}");
871         }
872     }
873 
874     /**
875      * Bug 4107276
876      */
877     @Test
TestDSTSavings()878     public void TestDSTSavings() {
879         // It might be better to find a way to integrate this test into the main TimeZone
880         // tests above, but I don't have time to figure out how to do this (or if it's
881         // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
882         SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "dstSavingsTest",
883                                                Calendar.MARCH, 1, 0, 0, Calendar.SEPTEMBER, 1, 0, 0,
884                                                (int)(0.5 * millisPerHour));
885 
886         if (tz.getRawOffset() != -5 * millisPerHour)
887             errln("Got back a raw offset of " + (tz.getRawOffset() / millisPerHour) +
888                   " hours instead of -5 hours.");
889         if (!tz.useDaylightTime())
890             errln("Test time zone should use DST but claims it doesn't.");
891         if (tz.getDSTSavings() != 0.5 * millisPerHour)
892             errln("Set DST offset to 0.5 hour, but got back " + (tz.getDSTSavings() /
893                                                                  millisPerHour) + " hours instead.");
894 
895         int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
896                                   Calendar.THURSDAY, 10 * millisPerHour);
897         if (offset != -5 * millisPerHour)
898             errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
899                   + (offset / millisPerHour) + " hours.");
900 
901         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
902                               10 * millisPerHour);
903         if (offset != -4.5 * millisPerHour)
904             errln("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got "
905                   + (offset / millisPerHour) + " hours.");
906 
907         tz.setDSTSavings(millisPerHour);
908         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JANUARY, 1,
909                               Calendar.THURSDAY, 10 * millisPerHour);
910         if (offset != -5 * millisPerHour)
911             errln("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got "
912                   + (offset / millisPerHour) + " hours.");
913 
914         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.JUNE, 1, Calendar.MONDAY,
915                               10 * millisPerHour);
916         if (offset != -4 * millisPerHour)
917             errln("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got "
918                   + (offset / millisPerHour) + " hours.");
919     }
920 
921     /**
922      * Bug 4107570
923      */
924     @Test
TestAlternateRules()925     public void TestAlternateRules() {
926         // Like TestDSTSavings, this test should probably be integrated somehow with the main
927         // test at the top of this class, but I didn't have time to figure out how to do that.
928         //                      --rtg 1/28/98
929 
930         SimpleTimeZone tz = new SimpleTimeZone(-5 * millisPerHour, "alternateRuleTest");
931 
932         // test the day-of-month API
933         tz.setStartRule(Calendar.MARCH, 10, 12 * millisPerHour);
934         tz.setEndRule(Calendar.OCTOBER, 20, 12 * millisPerHour);
935 
936         int offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 5,
937                                   Calendar.THURSDAY, 10 * millisPerHour);
938         if (offset != -5 * millisPerHour)
939             errln("The offset for 10AM, 3/5/98 should have been -5 hours, but we got "
940                   + (offset / millisPerHour) + " hours.");
941 
942         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 15,
943                               Calendar.SUNDAY, 10 * millisPerHour);
944         if (offset != -4 * millisPerHour)
945             errln("The offset for 10AM, 3/15/98 should have been -4 hours, but we got "
946                   + (offset / millisPerHour) + " hours.");
947 
948         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
949                               Calendar.THURSDAY, 10 * millisPerHour);
950         if (offset != -4 * millisPerHour)
951             errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
952                   + (offset / millisPerHour) + " hours.");
953 
954         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 25,
955                               Calendar.SUNDAY, 10 * millisPerHour);
956         if (offset != -5 * millisPerHour)
957             errln("The offset for 10AM, 10/25/98 should have been -5 hours, but we got "
958                   + (offset / millisPerHour) + " hours.");
959 
960         // test the day-of-week-after-day-in-month API
961         tz.setStartRule(Calendar.MARCH, 10, Calendar.FRIDAY, 12 * millisPerHour, true);
962         tz.setEndRule(Calendar.OCTOBER, 20, Calendar.FRIDAY, 12 * millisPerHour, false);
963 
964         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 11,
965                               Calendar.WEDNESDAY, 10 * millisPerHour);
966         if (offset != -5 * millisPerHour)
967             errln("The offset for 10AM, 3/11/98 should have been -5 hours, but we got "
968                   + (offset / millisPerHour) + " hours.");
969 
970         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.MARCH, 14,
971                               Calendar.SATURDAY, 10 * millisPerHour);
972         if (offset != -4 * millisPerHour)
973             errln("The offset for 10AM, 3/14/98 should have been -4 hours, but we got "
974                   + (offset / millisPerHour) + " hours.");
975 
976         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 15,
977                               Calendar.THURSDAY, 10 * millisPerHour);
978         if (offset != -4 * millisPerHour)
979             errln("The offset for 10AM, 10/15/98 should have been -4 hours, but we got "
980                   + (offset / millisPerHour) + " hours.");
981 
982         offset = tz.getOffset(GregorianCalendar.AD, 1998, Calendar.OCTOBER, 17,
983                               Calendar.SATURDAY, 10 * millisPerHour);
984         if (offset != -5 * millisPerHour)
985             errln("The offset for 10AM, 10/17/98 should have been -5 hours, but we got "
986                   + (offset / millisPerHour) + " hours.");
987     }
988 
989     @Test
TestEquivalencyGroups()990     public void TestEquivalencyGroups() {
991         String id = "America/Los_Angeles";
992         int n = TimeZone.countEquivalentIDs(id);
993         if (n < 2) {
994             errln("FAIL: countEquivalentIDs(" + id + ") returned " + n +
995                   ", expected >= 2");
996         }
997         for (int i=0; i<n; ++i) {
998             String s = TimeZone.getEquivalentID(id, i);
999             if (s.length() == 0) {
1000                 errln("FAIL: getEquivalentID(" + id + ", " + i +
1001                       ") returned \"" + s + "\", expected valid ID");
1002             } else {
1003                 logln("" + i + ":" + s);
1004             }
1005         }
1006 
1007         // JB#5480 - equivalent IDs should not be empty within range
1008         String[] ids = TimeZone.getAvailableIDs();
1009         for (int i = 0; i < ids.length; i++) {
1010             int nEquiv = TimeZone.countEquivalentIDs(ids[i]);
1011             // Each equivalent ID must not be empty
1012             for (int j = 0; j < nEquiv; j++) {
1013                 String equivID = TimeZone.getEquivalentID(ids[i], j);
1014                 if (equivID.length() == 0) {
1015                     errln("FAIL: getEquivalentID(" + ids[i] + ", " + i +
1016                             ") returned \"" + equivID + "\", expected valid ID");
1017                 }
1018             }
1019             // equivalent ID out of range must be empty
1020             String outOfRangeID = TimeZone.getEquivalentID(ids[i], nEquiv);
1021             if (outOfRangeID.length() != 0) {
1022                 errln("FAIL: getEquivalentID(" + ids[i] + ", " + i +
1023                         ") returned \"" + outOfRangeID + "\", expected empty string");
1024             }
1025         }
1026 
1027         // Ticket#8927 invalid system ID
1028         final String[] invaldIDs = {"GMT-05:00", "Hello World!", ""};
1029         for (String invld : invaldIDs) {
1030             int nEquiv = TimeZone.countEquivalentIDs(invld);
1031             if (nEquiv != 0) {
1032                 errln("FAIL: countEquivalentIDs(" + invld + ") returned: " + nEquiv
1033                         + ", expected: 0");
1034             }
1035             String sEquiv0 = TimeZone.getEquivalentID(invld, 0);
1036             if (sEquiv0.length() > 0) {
1037                 errln("FAIL: getEquivalentID(" + invld + ", 0) returned \"" + sEquiv0
1038                         + "\", expected empty string");
1039             }
1040         }
1041     }
1042 
1043     @Test
TestCountries()1044     public void TestCountries() {
1045         // Make sure America/Los_Angeles is in the "US" group, and
1046         // Asia/Tokyo isn't.  Vice versa for the "JP" group.
1047 
1048         String[] s = TimeZone.getAvailableIDs("US");
1049         boolean la = false, tokyo = false;
1050         String laZone = "America/Los_Angeles", tokyoZone = "Asia/Tokyo";
1051 
1052         for (int i=0; i<s.length; ++i) {
1053             if (s[i].equals(laZone)) {
1054                 la = true;
1055             }
1056             if (s[i].equals(tokyoZone)) {
1057                 tokyo = true;
1058             }
1059         }
1060         if (!la ) {
1061             errln("FAIL: " + laZone + " in US = " + la);
1062         }
1063         if (tokyo) {
1064             errln("FAIL: " + tokyoZone + " in US = " + tokyo);
1065         }
1066         s = TimeZone.getAvailableIDs("JP");
1067         la = false; tokyo = false;
1068 
1069         for (int i=0; i<s.length; ++i) {
1070             if (s[i].equals(laZone)) {
1071                 la = true;
1072             }
1073             if (s[i].equals(tokyoZone)) {
1074                 tokyo = true;
1075             }
1076         }
1077         if (la) {
1078             errln("FAIL: " + laZone + " in JP = " + la);
1079         }
1080         if (!tokyo) {
1081             errln("FAIL: " + tokyoZone + " in JP = " + tokyo);
1082         }
1083     }
1084 
1085     @Test
TestFractionalDST()1086     public void TestFractionalDST() {
1087         String tzName = "Australia/Lord_Howe"; // 30 min offset
1088         java.util.TimeZone tz_java = java.util.TimeZone.getTimeZone(tzName);
1089         int dst_java = 0;
1090         try {
1091             // hack so test compiles and runs in both JDK 1.3 and JDK 1.4
1092             final Object[] args = new Object[0];
1093             final Class[] argtypes = new Class[0];
1094             java.lang.reflect.Method m = tz_java.getClass().getMethod("getDSTSavings", argtypes);
1095             dst_java = ((Integer) m.invoke(tz_java, args)).intValue();
1096             if (dst_java <= 0 || dst_java >= 3600000) { // didn't get the fractional time zone we wanted
1097             errln("didn't get fractional time zone!");
1098             }
1099         } catch (NoSuchMethodException e) {
1100             // see JDKTimeZone for the reason for this code
1101             dst_java = 3600000;
1102         } catch (IllegalAccessException e) {
1103             // see JDKTimeZone for the reason for this code
1104             errln(e.getMessage());
1105             dst_java = 3600000;
1106         } catch (InvocationTargetException e) {
1107             // see JDKTimeZone for the reason for this code
1108             errln(e.getMessage());
1109             dst_java = 3600000;
1110         } catch (SecurityException e) {
1111             warnln(e.getMessage());
1112             return;
1113         }
1114 
1115         ohos.global.icu.util.TimeZone tz_icu = ohos.global.icu.util.TimeZone.getTimeZone(tzName);
1116         int dst_icu = tz_icu.getDSTSavings();
1117 
1118         if (dst_java != dst_icu) {
1119             warnln("java reports dst savings of " + dst_java +
1120               " but icu reports " + dst_icu +
1121               " for tz " + tz_icu.getID());
1122         } else {
1123             logln("both java and icu report dst savings of " + dst_java + " for tz " + tz_icu.getID());
1124         }
1125     }
1126 
1127     @Test
TestGetOffsetDate()1128     public void TestGetOffsetDate() {
1129         Calendar cal = Calendar.getInstance();
1130         cal.set(1997, Calendar.JANUARY, 30);
1131         long date = cal.getTimeInMillis();
1132 
1133     TimeZone tz_icu = TimeZone.getTimeZone("America/Los_Angeles");
1134     int offset = tz_icu.getOffset(date);
1135     if (offset != -28800000) {
1136         errln("expected offset -28800000, got: " + offset);
1137     }
1138 
1139     cal.set(1997, Calendar.JULY, 30);
1140     date = cal.getTimeInMillis();
1141     offset = tz_icu.getOffset(date);
1142     if (offset != -25200000) {
1143         errln("expected offset -25200000, got: " + offset);
1144     }
1145     }
1146 
1147     // jb4484
1148     @Test
TestSimpleTimeZoneSerialization()1149     public void TestSimpleTimeZoneSerialization()
1150     {
1151         SimpleTimeZone stz0 = new SimpleTimeZone(32400000, "MyTimeZone");
1152         SimpleTimeZone stz1 = new SimpleTimeZone(32400000, "Asia/Tokyo");
1153         SimpleTimeZone stz2 = new SimpleTimeZone(32400000, "Asia/Tokyo");
1154         stz2.setRawOffset(0);
1155         SimpleTimeZone stz3 = new SimpleTimeZone(32400000, "Asia/Tokyo");
1156         stz3.setStartYear(100);
1157         SimpleTimeZone stz4 = new SimpleTimeZone(32400000, "Asia/Tokyo");
1158         stz4.setStartYear(1000);
1159         stz4.setDSTSavings(1800000);
1160         stz4.setStartRule(3, 4, 180000);
1161         stz4.setEndRule(6, 3, 4, 360000);
1162         SimpleTimeZone stz5 = new SimpleTimeZone(32400000, "Asia/Tokyo");
1163         stz5.setStartRule(2, 3, 4, 360000);
1164         stz5.setEndRule(6, 3, 4, 360000);
1165 
1166         SimpleTimeZone[] stzs = { stz0, stz1, stz2, stz3, stz4, stz5, };
1167 
1168         for (int i = 0; i < stzs.length; ++i) {
1169             SimpleTimeZone stz = stzs[i];
1170             try {
1171                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1172                 ObjectOutputStream oos = new ObjectOutputStream(baos);
1173                 oos.writeObject(stz);
1174                 oos.close();
1175                 byte[] bytes = baos.toByteArray();
1176                 logln("id: " + stz.getID() + " length: " + bytes.length);
1177 
1178                 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1179                 ObjectInputStream ois = new ObjectInputStream(bais);
1180 
1181                 SimpleTimeZone stzDeserialized = (SimpleTimeZone)ois.readObject();
1182                 ois.close();
1183 
1184                 assertEquals("time zones", stz, stzDeserialized);
1185             }
1186             catch (ClassCastException cce) {
1187                 cce.printStackTrace();
1188                 errln("could not deserialize SimpleTimeZone");
1189             }
1190             catch (IOException ioe) {
1191                 errln(ioe.getMessage());
1192             }
1193             catch (ClassNotFoundException cnfe) {
1194                 errln(cnfe.getMessage());
1195             }
1196         }
1197     }
1198 
1199     // jb4175
1200     /* Generated by org.unicode.cldr.tool.CountItems */
1201     private static final String[] timeZoneTestNames = {
1202         "America/Argentina/Buenos_Aires", "America/Buenos_Aires",
1203         "America/Argentina/Catamarca", "America/Catamarca",
1204         "America/Argentina/Cordoba", "America/Cordoba",
1205         "America/Argentina/Jujuy", "America/Jujuy",
1206         "America/Argentina/Mendoza", "America/Mendoza",
1207         "America/Atka", "America/Adak",
1208         "America/Ensenada", "America/Tijuana",
1209         "America/Fort_Wayne", "America/Indianapolis",
1210         "America/Indiana/Indianapolis", "America/Indianapolis",
1211         "America/Kentucky/Louisville", "America/Louisville",
1212         "America/Knox_IN", "America/Indiana/Knox",
1213         "America/Porto_Acre", "America/Rio_Branco",
1214         "America/Rosario", "America/Cordoba",
1215         "America/Virgin", "America/St_Thomas",
1216         "Asia/Ashkhabad", "Asia/Ashgabat",
1217         "Asia/Chungking", "Asia/Chongqing",
1218         "Asia/Dacca", "Asia/Dhaka",
1219         "Asia/Istanbul", "Europe/Istanbul",
1220         "Asia/Macao", "Asia/Macau",
1221         "Asia/Tel_Aviv", "Asia/Jerusalem",
1222         "Asia/Thimbu", "Asia/Thimphu",
1223         "Asia/Ujung_Pandang", "Asia/Makassar",
1224         "Asia/Ulan_Bator", "Asia/Ulaanbaatar",
1225         "Australia/ACT", "Australia/Sydney",
1226         "Australia/Canberra", "Australia/Sydney",
1227         "Australia/LHI", "Australia/Lord_Howe",
1228         "Australia/NSW", "Australia/Sydney",
1229         "Australia/North", "Australia/Darwin",
1230         "Australia/Queensland", "Australia/Brisbane",
1231         "Australia/South", "Australia/Adelaide",
1232         "Australia/Tasmania", "Australia/Hobart",
1233         "Australia/Victoria", "Australia/Melbourne",
1234         "Australia/West", "Australia/Perth",
1235         "Australia/Yancowinna", "Australia/Broken_Hill",
1236         "Brazil/Acre", "America/Rio_Branco",
1237         "Brazil/DeNoronha", "America/Noronha",
1238         "Brazil/East", "America/Sao_Paulo",
1239         "Brazil/West", "America/Manaus",
1240         "CST6CDT", "America/Chicago",
1241         "Canada/Atlantic", "America/Halifax",
1242         "Canada/Central", "America/Winnipeg",
1243         "Canada/East-Saskatchewan", "America/Regina",
1244         "Canada/Eastern", "America/Toronto",
1245         "Canada/Mountain", "America/Edmonton",
1246         "Canada/Newfoundland", "America/St_Johns",
1247         "Canada/Pacific", "America/Vancouver",
1248         "Canada/Saskatchewan", "America/Regina",
1249         "Canada/Yukon", "America/Whitehorse",
1250         "Chile/Continental", "America/Santiago",
1251         "Chile/EasterIsland", "Pacific/Easter",
1252         "Cuba", "America/Havana",
1253         "EST", "America/Indianapolis",
1254         "EST5EDT", "America/New_York",
1255         "Egypt", "Africa/Cairo",
1256         "Eire", "Europe/Dublin",
1257         "Etc/GMT+0", "Etc/GMT",
1258         "Etc/GMT-0", "Etc/GMT",
1259         "Etc/GMT0", "Etc/GMT",
1260         "Etc/Greenwich", "Etc/GMT",
1261         "Etc/UCT", "Etc/GMT",
1262         "Etc/UTC", "Etc/GMT",
1263         "Etc/Universal", "Etc/GMT",
1264         "Etc/Zulu", "Etc/GMT",
1265         "Europe/Nicosia", "Asia/Nicosia",
1266         "Europe/Tiraspol", "Europe/Chisinau",
1267         "GB", "Europe/London",
1268         "GB-Eire", "Europe/London",
1269         "GMT", "Etc/GMT",
1270         "GMT+0", "Etc/GMT",
1271         "GMT-0", "Etc/GMT",
1272         "GMT0", "Etc/GMT",
1273         "Greenwich", "Etc/GMT",
1274         "HST", "Pacific/Honolulu",
1275         "Hongkong", "Asia/Hong_Kong",
1276         "Iceland", "Atlantic/Reykjavik",
1277         "Iran", "Asia/Tehran",
1278         "Israel", "Asia/Jerusalem",
1279         "Jamaica", "America/Jamaica",
1280         "Japan", "Asia/Tokyo",
1281         "Kwajalein", "Pacific/Kwajalein",
1282         "Libya", "Africa/Tripoli",
1283         "MST", "America/Phoenix",
1284         "MST7MDT", "America/Denver",
1285         "Mexico/BajaNorte", "America/Tijuana",
1286         "Mexico/BajaSur", "America/Mazatlan",
1287         "Mexico/General", "America/Mexico_City",
1288         "NZ", "Pacific/Auckland",
1289         "NZ-CHAT", "Pacific/Chatham",
1290         "Navajo", "America/Shiprock", /* fixed from Mark's original */
1291         "PRC", "Asia/Shanghai",
1292         "PST8PDT", "America/Los_Angeles",
1293         "Pacific/Samoa", "Pacific/Pago_Pago",
1294         "Poland", "Europe/Warsaw",
1295         "Portugal", "Europe/Lisbon",
1296         "ROC", "Asia/Taipei",
1297         "ROK", "Asia/Seoul",
1298         "Singapore", "Asia/Singapore",
1299         "SystemV/AST4", "America/Puerto_Rico",
1300         "SystemV/AST4ADT", "America/Halifax",
1301         "SystemV/CST6", "America/Regina",
1302         "SystemV/CST6CDT", "America/Chicago",
1303         "SystemV/EST5", "America/Indianapolis",
1304         "SystemV/EST5EDT", "America/New_York",
1305         "SystemV/HST10", "Pacific/Honolulu",
1306         "SystemV/MST7", "America/Phoenix",
1307         "SystemV/MST7MDT", "America/Denver",
1308         "SystemV/PST8", "Pacific/Pitcairn",
1309         "SystemV/PST8PDT", "America/Los_Angeles",
1310         "SystemV/YST9", "Pacific/Gambier",
1311         "SystemV/YST9YDT", "America/Anchorage",
1312         "Turkey", "Europe/Istanbul",
1313         "UCT", "Etc/GMT",
1314         "US/Alaska", "America/Anchorage",
1315         "US/Aleutian", "America/Adak",
1316         "US/Arizona", "America/Phoenix",
1317         "US/Central", "America/Chicago",
1318         "US/East-Indiana", "America/Indianapolis",
1319         "US/Eastern", "America/New_York",
1320         "US/Hawaii", "Pacific/Honolulu",
1321         "US/Indiana-Starke", "America/Indiana/Knox",
1322         "US/Michigan", "America/Detroit",
1323         "US/Mountain", "America/Denver",
1324         "US/Pacific", "America/Los_Angeles",
1325         "US/Pacific-New", "America/Los_Angeles",
1326         "US/Samoa", "Pacific/Pago_Pago",
1327         "UTC", "Etc/GMT",
1328         "Universal", "Etc/GMT",
1329         "W-SU", "Europe/Moscow",
1330         "Zulu", "Etc/GMT",
1331     };
1332 
1333     @Test
TestOddTimeZoneNames()1334     public void TestOddTimeZoneNames() {
1335         for (int i = 0; i < timeZoneTestNames.length; i += 2) {
1336             String funkyName = timeZoneTestNames[i];
1337             String correctName = timeZoneTestNames[i+1];
1338 
1339             TimeZone ftz = TimeZone.getTimeZone(funkyName);
1340             TimeZone ctz = TimeZone.getTimeZone(correctName);
1341 
1342             String fdn = ftz.getDisplayName();
1343             long fro = ftz.getRawOffset();
1344             long fds = ftz.getDSTSavings();
1345             boolean fdy = ftz.useDaylightTime();
1346 
1347             String cdn = ctz.getDisplayName();
1348             long cro = ctz.getRawOffset();
1349             long cds = ctz.getDSTSavings();
1350             boolean cdy = ctz.useDaylightTime();
1351 
1352             if (!fdn.equals(cdn)) {
1353                 logln("display name (" + funkyName + ", " + correctName + ") expected: " + cdn + " but got: " + fdn);
1354             } else if (fro != cro) {
1355                 logln("offset (" + funkyName + ", " + correctName + ") expected: " + cro + " but got: " + fro);
1356             } else if (fds != cds) {
1357                 logln("daylight (" + funkyName + ", " + correctName + ") expected: " + cds + " but got: " + fds);
1358             } else if (fdy != cdy) {
1359                 logln("uses daylight (" + funkyName + ", " + correctName + ") expected: " + cdy + " but got: " + fdy);
1360             } else {
1361                 // no error, assume we're referencing the same internal java object
1362             }
1363         }
1364     }
1365 
1366     @Test
TestCoverage()1367     public void TestCoverage(){
1368         class StubTimeZone extends TimeZone{
1369             /**
1370              * For serialization
1371              */
1372             private static final long serialVersionUID = 8658654217433379343L;
1373             @Override
1374             public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {return 0;}
1375             @Override
1376             public void setRawOffset(int offsetMillis) {}
1377             @Override
1378             public int getRawOffset() {return 0;}
1379             @Override
1380             public boolean useDaylightTime() {return false;}
1381             @Override
1382             public boolean inDaylightTime(Date date) {return false;}
1383         }
1384         StubTimeZone stub = new StubTimeZone();
1385         StubTimeZone stub2 = (StubTimeZone) stub.clone();
1386         if (stub.getDSTSavings() != 0){
1387             errln("TimeZone.getDSTSavings() should return 0");
1388         }
1389         if (!stub.hasSameRules(stub2)){
1390             errln("TimeZone.clone() object should hasSameRules");
1391 
1392         }
1393     }
1394     @Test
TestMark()1395     public void TestMark(){
1396         String tzid = "America/Argentina/ComodRivadavia";
1397         TimeZone tz = TimeZone.getTimeZone(tzid);
1398         int offset = tz.getOffset(new Date().getTime());
1399         logln(tzid + ":\t" + offset);
1400         List list = Arrays.asList(TimeZone.getAvailableIDs());
1401         if(!list.contains(tzid)){
1402             errln("Could create the time zone but it is not in getAvailableIDs");
1403         }
1404     }
1405     @Test
TestZoneMeta()1406     public void TestZoneMeta() {
1407         java.util.TimeZone save = java.util.TimeZone.getDefault();
1408         java.util.TimeZone newZone = java.util.TimeZone.getTimeZone("GMT-08:00");
1409         ohos.global.icu.util.TimeZone.setDefault(null);
1410         java.util.TimeZone.setDefault(newZone);
1411         SimpleTimeZone zone = new SimpleTimeZone(0, "GMT");
1412         ohos.global.icu.util.TimeZone defaultZone = ohos.global.icu.util.TimeZone.getDefault();
1413         if(defaultZone==null){
1414             errln("TimeZone.getDefault() failed for GMT-08:00");
1415         }
1416         if(zone==null){
1417             errln("SimpleTimeZone(0, GMT-08:00) failed for GMT-08:00");
1418         }
1419         //reset
1420         java.util.TimeZone.setDefault(save);
1421     }
1422 
1423     // Copied from the protected constant in TimeZone.
1424     private static final int MILLIS_PER_HOUR = 60*60*1000;
1425 
1426     //  Test that a transition at the end of February is handled correctly.
1427     @Test
TestFebruary()1428     public void TestFebruary() {
1429         // Time zone with daylight savings time from the first Sunday in November
1430         // to the last Sunday in February.
1431         // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n.
1432         //
1433         // Note: In tzdata2007h, the rule had changed, so no actual zones uses
1434         // lastSun in Feb anymore.
1435         SimpleTimeZone tz1 = new SimpleTimeZone(
1436                            -3 * MILLIS_PER_HOUR,                    // raw offset: 3h before (west of) GMT
1437                            "nov-feb",
1438                            Calendar.NOVEMBER, 1, Calendar.SUNDAY,   // start: November, first, Sunday
1439                            0,                                       //        midnight wall time
1440                            Calendar.FEBRUARY, -1, Calendar.SUNDAY,  // end:   February, last, Sunday
1441                            0);                                      //        midnight wall time
1442 
1443         // Now hardcode the same rules as for Brazil in tzdata 2006n, so that
1444         // we cover the intended code even when in the future zoneinfo hardcodes
1445         // these transition dates.
1446         SimpleTimeZone tz2= new SimpleTimeZone(
1447                            -3 * MILLIS_PER_HOUR,                    // raw offset: 3h before (west of) GMT
1448                            "nov-feb2",
1449                            Calendar.NOVEMBER, 1, -Calendar.SUNDAY,  // start: November, 1 or after, Sunday
1450                            0,                                       //        midnight wall time
1451                            Calendar.FEBRUARY, -29, -Calendar.SUNDAY,// end:   February, 29 or before, Sunday
1452                            0);                                      //        midnight wall time
1453 
1454         // Gregorian calendar with the UTC time zone for getting sample test date/times.
1455         GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT"));
1456         // "Unable to create the UTC calendar: %s"
1457 
1458         int[] data = {
1459             // UTC time (6 fields) followed by
1460             // expected time zone offset in hours after GMT (negative=before GMT).
1461             // int year, month, day, hour, minute, second, offsetHours
1462             2006, Calendar.NOVEMBER,  5, 02, 59, 59, -3,
1463             2006, Calendar.NOVEMBER,  5, 03, 00, 00, -2,
1464             2007, Calendar.FEBRUARY, 25, 01, 59, 59, -2,
1465             2007, Calendar.FEBRUARY, 25, 02, 00, 00, -3,
1466 
1467             2007, Calendar.NOVEMBER,  4, 02, 59, 59, -3,
1468             2007, Calendar.NOVEMBER,  4, 03, 00, 00, -2,
1469             2008, Calendar.FEBRUARY, 24, 01, 59, 59, -2,
1470             2008, Calendar.FEBRUARY, 24, 02, 00, 00, -3,
1471 
1472             2008, Calendar.NOVEMBER,  2, 02, 59, 59, -3,
1473             2008, Calendar.NOVEMBER,  2, 03, 00, 00, -2,
1474             2009, Calendar.FEBRUARY, 22, 01, 59, 59, -2,
1475             2009, Calendar.FEBRUARY, 22, 02, 00, 00, -3,
1476 
1477             2009, Calendar.NOVEMBER,  1, 02, 59, 59, -3,
1478             2009, Calendar.NOVEMBER,  1, 03, 00, 00, -2,
1479             2010, Calendar.FEBRUARY, 28, 01, 59, 59, -2,
1480             2010, Calendar.FEBRUARY, 28, 02, 00, 00, -3
1481         };
1482 
1483         TimeZone timezones[] = { tz1, tz2 };
1484 
1485         TimeZone tz;
1486         Date dt;
1487         int t, i, raw, dst;
1488         int[] offsets = new int[2]; // raw = offsets[0], dst = offsets[1]
1489         for (t = 0; t < timezones.length; ++t) {
1490             tz = timezones[t];
1491             for (i = 0; i < data.length; i+=7) {
1492                 gc.set(data[i], data[i+1], data[i+2],
1493                        data[i+3], data[i+4], data[i+5]);
1494                 dt = gc.getTime();
1495                 tz.getOffset(dt.getTime(), false, offsets);
1496                 raw = offsets[0];
1497                 dst = offsets[1];
1498                 if ((raw + dst) != data[i+6] * MILLIS_PER_HOUR) {
1499                     errln("test case " + t + "." + (i/7) + ": " +
1500                           "tz.getOffset(" + data[i] + "-" + (data[i+1] + 1) + "-" + data[i+2] + " " +
1501                           data[i+3] + ":" + data[i+4] + ":" + data[i+5] +
1502                           ") returns " + raw + "+" + dst + " != " + data[i+6] * MILLIS_PER_HOUR);
1503                 }
1504             }
1505         }
1506     }
1507 
1508     @Test
TestCanonicalID()1509     public void TestCanonicalID() {
1510         // Some canonical IDs in CLDR are defined as "Link"
1511         // in Olson tzdata.
1512         final String[][] excluded1 = {
1513                 {"Africa/Addis_Ababa", "Africa/Nairobi"},
1514                 {"Africa/Asmera", "Africa/Nairobi"},
1515                 {"Africa/Bamako", "Africa/Abidjan"},
1516                 {"Africa/Bangui", "Africa/Lagos"},
1517                 {"Africa/Banjul", "Africa/Abidjan"},
1518                 {"Africa/Blantyre", "Africa/Maputo"},
1519                 {"Africa/Brazzaville", "Africa/Lagos"},
1520                 {"Africa/Bujumbura", "Africa/Maputo"},
1521                 {"Africa/Conakry", "Africa/Abidjan"},
1522                 {"Africa/Dakar", "Africa/Abidjan"},
1523                 {"Africa/Dar_es_Salaam", "Africa/Nairobi"},
1524                 {"Africa/Djibouti", "Africa/Nairobi"},
1525                 {"Africa/Douala", "Africa/Lagos"},
1526                 {"Africa/Freetown", "Africa/Abidjan"},
1527                 {"Africa/Gaborone", "Africa/Maputo"},
1528                 {"Africa/Harare", "Africa/Maputo"},
1529                 {"Africa/Kampala", "Africa/Nairobi"},
1530                 {"Africa/Khartoum", "Africa/Juba"},
1531                 {"Africa/Kigali", "Africa/Maputo"},
1532                 {"Africa/Kinshasa", "Africa/Lagos"},
1533                 {"Africa/Libreville", "Africa/Lagos"},
1534                 {"Africa/Lome", "Africa/Abidjan"},
1535                 {"Africa/Luanda", "Africa/Lagos"},
1536                 {"Africa/Lubumbashi", "Africa/Maputo"},
1537                 {"Africa/Lusaka", "Africa/Maputo"},
1538                 {"Africa/Maseru", "Africa/Johannesburg"},
1539                 {"Africa/Malabo", "Africa/Lagos"},
1540                 {"Africa/Mbabane", "Africa/Johannesburg"},
1541                 {"Africa/Mogadishu", "Africa/Nairobi"},
1542                 {"Africa/Niamey", "Africa/Lagos"},
1543                 {"Africa/Nouakchott", "Africa/Abidjan"},
1544                 {"Africa/Ouagadougou", "Africa/Abidjan"},
1545                 {"Africa/Porto-Novo", "Africa/Lagos"},
1546                 {"Africa/Sao_Tome", "Africa/Abidjan"},
1547                 {"America/Antigua", "America/Port_of_Spain"},
1548                 {"America/Anguilla", "America/Port_of_Spain"},
1549                 {"America/Curacao", "America/Aruba"},
1550                 {"America/Dominica", "America/Port_of_Spain"},
1551                 {"America/Grenada", "America/Port_of_Spain"},
1552                 {"America/Guadeloupe", "America/Port_of_Spain"},
1553                 {"America/Kralendijk", "America/Aruba"},
1554                 {"America/Lower_Princes", "America/Aruba"},
1555                 {"America/Marigot", "America/Port_of_Spain"},
1556                 {"America/Montserrat", "America/Port_of_Spain"},
1557                 {"America/Panama", "America/Cayman"},
1558                 {"America/Santa_Isabel", "America/Tijuana"},
1559                 {"America/Shiprock", "America/Denver"},
1560                 {"America/St_Barthelemy", "America/Port_of_Spain"},
1561                 {"America/St_Kitts", "America/Port_of_Spain"},
1562                 {"America/St_Lucia", "America/Port_of_Spain"},
1563                 {"America/St_Thomas", "America/Port_of_Spain"},
1564                 {"America/St_Vincent", "America/Port_of_Spain"},
1565                 {"America/Toronto", "America/Montreal"},
1566                 {"America/Tortola", "America/Port_of_Spain"},
1567                 {"America/Virgin", "America/Port_of_Spain"},
1568                 {"Antarctica/South_Pole", "Antarctica/McMurdo"},
1569                 {"Arctic/Longyearbyen", "Europe/Oslo"},
1570                 {"Asia/Kuwait", "Asia/Aden"},
1571                 {"Asia/Muscat", "Asia/Dubai"},
1572                 {"Asia/Phnom_Penh", "Asia/Bangkok"},
1573                 {"Asia/Qatar", "Asia/Bahrain"},
1574                 {"Asia/Riyadh", "Asia/Aden"},
1575                 {"Asia/Vientiane", "Asia/Bangkok"},
1576                 {"Atlantic/Jan_Mayen", "Europe/Oslo"},
1577                 {"Atlantic/St_Helena", "Africa/Abidjan"},
1578                 {"Europe/Bratislava", "Europe/Prague"},
1579                 {"Europe/Busingen", "Europe/Zurich"},
1580                 {"Europe/Guernsey", "Europe/London"},
1581                 {"Europe/Isle_of_Man", "Europe/London"},
1582                 {"Europe/Jersey", "Europe/London"},
1583                 {"Europe/Ljubljana", "Europe/Belgrade"},
1584                 {"Europe/Mariehamn", "Europe/Helsinki"},
1585                 {"Europe/Podgorica", "Europe/Belgrade"},
1586                 {"Europe/San_Marino", "Europe/Rome"},
1587                 {"Europe/Sarajevo", "Europe/Belgrade"},
1588                 {"Europe/Skopje", "Europe/Belgrade"},
1589                 {"Europe/Vaduz", "Europe/Zurich"},
1590                 {"Europe/Vatican", "Europe/Rome"},
1591                 {"Europe/Zagreb", "Europe/Belgrade"},
1592                 {"Indian/Antananarivo", "Africa/Nairobi"},
1593                 {"Indian/Comoro", "Africa/Nairobi"},
1594                 {"Indian/Mayotte", "Africa/Nairobi"},
1595                 {"Pacific/Auckland", "Antarctica/McMurdo"},
1596                 {"Pacific/Johnston", "Pacific/Honolulu"},
1597                 {"Pacific/Midway", "Pacific/Pago_Pago"},
1598                 {"Pacific/Saipan", "Pacific/Guam"},
1599         };
1600 
1601         // Following IDs are aliases of Etc/GMT in CLDR,
1602         // but Olson tzdata has 3 independent definitions
1603         // for Etc/GMT, Etc/UTC, Etc/UCT.
1604         // Until we merge them into one equivalent group
1605         // in zoneinfo.res, we exclude them in the test
1606         // below.
1607         final String[] excluded2 = {
1608                 "Etc/UCT", "UCT",
1609                 "Etc/UTC", "UTC",
1610                 "Etc/Universal", "Universal",
1611                 "Etc/Zulu", "Zulu",
1612         };
1613 
1614         // Walk through equivalency groups
1615         String[] ids = TimeZone.getAvailableIDs();
1616         for (int i = 0; i < ids.length; i++) {
1617             int nEquiv = TimeZone.countEquivalentIDs(ids[i]);
1618             if (nEquiv == 0) {
1619                 continue;
1620             }
1621             String canonicalID = null;
1622             boolean bFoundCanonical = false;
1623             // Make sure getCanonicalID returns the exact same result
1624             // for all entries within a same equivalency group with some
1625             // exceptions listed in exluded1.
1626             // Also, one of them must be canonical id.
1627             for (int j = 0; j < nEquiv; j++) {
1628                 String tmp = TimeZone.getEquivalentID(ids[i], j);
1629                 String tmpCanonical = TimeZone.getCanonicalID(tmp);
1630                 if (tmpCanonical == null) {
1631                     errln("FAIL: getCanonicalID(\"" + tmp + "\") returned null");
1632                     continue;
1633                 }
1634                 // Some exceptional cases
1635                 for (int k = 0; k < excluded1.length; k++) {
1636                     if (tmpCanonical.equals(excluded1[k][0])) {
1637                         tmpCanonical = excluded1[k][1];
1638                     }
1639                 }
1640 
1641                 if (j == 0) {
1642                     canonicalID = tmpCanonical;
1643                 } else if (!canonicalID.equals(tmpCanonical)) {
1644                     if (tmpCanonical.equals("Australia/Hobart") && canonicalID.equals("Australia/Currie")) {
1645                         continue;
1646                     }
1647                     errln("FAIL: getCanonicalID(\"" + tmp + "\") returned " + tmpCanonical + " expected:" + canonicalID);
1648                 }
1649 
1650                 if (canonicalID.equals(tmp)) {
1651                     bFoundCanonical = true;
1652                 }
1653             }
1654             // At least one ID in an equvalency group must match the
1655             // canonicalID
1656             if (!bFoundCanonical) {
1657                 // test exclusion because of differences between Olson tzdata and CLDR
1658                 boolean isExcluded = false;
1659                 for (int k = 0; k < excluded1.length; k++) {
1660                     if (ids[i].equals(excluded2[k])) {
1661                         isExcluded = true;
1662                         break;
1663                     }
1664                 }
1665                 if (isExcluded) {
1666                     continue;
1667                 }
1668 
1669                 errln("FAIL: No timezone ids match the canonical ID " + canonicalID);
1670             }
1671         }
1672         // Testing some special cases
1673         final String[][] data = {
1674                 {"GMT-03", "GMT-03:00", null},
1675                 {"GMT+4", "GMT+04:00", null},
1676                 {"GMT-055", "GMT-00:55", null},
1677                 {"GMT+430", "GMT+04:30", null},
1678                 {"GMT-12:15", "GMT-12:15", null},
1679                 {"GMT-091015", "GMT-09:10:15", null},
1680                 {"GMT+1:90", null, null},
1681                 {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", "true"},
1682                 {"Etc/Unknown", "Etc/Unknown", null},
1683                 {"bogus", null, null},
1684                 {"", null, null},
1685                 {"America/Marigot", "America/Marigot", "true"},     // Olson link, but CLDR canonical (#8953)
1686                 {"Europe/Bratislava", "Europe/Bratislava", "true"}, // Same as above
1687                 {null, null, null},
1688         };
1689         boolean[] isSystemID = new boolean[1];
1690         for (int i = 0; i < data.length; i++) {
1691             String canonical = TimeZone.getCanonicalID(data[i][0], isSystemID);
1692             if (canonical != null && !canonical.equals(data[i][1])
1693                     || canonical == null && data[i][1] != null) {
1694                 errln("FAIL: getCanonicalID(\"" + data[i][0] + "\") returned " + canonical
1695                         + " - expected: " + data[i][1]);
1696             }
1697             if ("true".equalsIgnoreCase(data[i][2]) != isSystemID[0]) {
1698                 errln("FAIL: getCanonicalID(\"" + data[i][0] + "\") set " + isSystemID[0]
1699                         + " to isSystemID");
1700             }
1701         }
1702     }
1703 
1704     @Test
TestSetDefault()1705     public void TestSetDefault() {
1706         java.util.TimeZone save = java.util.TimeZone.getDefault();
1707 
1708         /*
1709          * America/Caracs (Venezuela) changed the base offset from -4:00 to
1710          * -4:30 on Dec 9, 2007.
1711          */
1712 
1713         TimeZone icuCaracas = TimeZone.getTimeZone("America/Caracas", TimeZone.TIMEZONE_ICU);
1714         java.util.TimeZone jdkCaracas = java.util.TimeZone.getTimeZone("America/Caracas");
1715 
1716         // Set JDK America/Caracas as the default
1717         java.util.TimeZone.setDefault(jdkCaracas);
1718 
1719         java.util.Calendar jdkCal = java.util.Calendar.getInstance();
1720         jdkCal.clear();
1721         jdkCal.set(2007, java.util.Calendar.JANUARY, 1);
1722 
1723         int rawOffset = jdkCal.get(java.util.Calendar.ZONE_OFFSET);
1724         int dstSavings = jdkCal.get(java.util.Calendar.DST_OFFSET);
1725 
1726         int[] offsets = new int[2];
1727         icuCaracas.getOffset(jdkCal.getTime().getTime()/*jdkCal.getTimeInMillis()*/, false, offsets);
1728 
1729         boolean isTimeZoneSynchronized = true;
1730 
1731         if (rawOffset != offsets[0] || dstSavings != offsets[1]) {
1732             // JDK time zone rule is out of sync...
1733             logln("Rule for JDK America/Caracas is not same with ICU.  Skipping the rest.");
1734             isTimeZoneSynchronized = false;
1735         }
1736 
1737         if (isTimeZoneSynchronized) {
1738             // If JDK America/Caracas uses the same rule with ICU,
1739             // the following code should work well.
1740             TimeZone.setDefault(icuCaracas);
1741 
1742             // Create a new JDK calendar instance again.
1743             // This calendar should reflect the new default
1744             // set by ICU TimeZone#setDefault.
1745             jdkCal = java.util.Calendar.getInstance();
1746             jdkCal.clear();
1747             jdkCal.set(2007, java.util.Calendar.JANUARY, 1);
1748 
1749             rawOffset = jdkCal.get(java.util.Calendar.ZONE_OFFSET);
1750             dstSavings = jdkCal.get(java.util.Calendar.DST_OFFSET);
1751 
1752             if (rawOffset != offsets[0] || dstSavings != offsets[1]) {
1753                 errln("ERROR: Got offset [raw:" + rawOffset + "/dst:" + dstSavings
1754                           + "] Expected [raw:" + offsets[0] + "/dst:" + offsets[1] + "]");
1755             }
1756         }
1757 
1758         // Restore the original JDK time zone
1759         java.util.TimeZone.setDefault(save);
1760     }
1761 
1762     /*
1763      * Test Display Names, choosing zones and lcoales where there are multiple
1764      * meta-zones defined.
1765      */
1766     @Test
TestDisplayNamesMeta()1767     public void TestDisplayNamesMeta() {
1768         final Integer TZSHORT = new Integer(TimeZone.SHORT);
1769         final Integer TZLONG = new Integer(TimeZone.LONG);
1770 
1771         final Object[][] zoneDisplayTestData = {
1772             //  zone id             locale  summer          format      expected display name
1773             {"Europe/London",       "en",   Boolean.FALSE,  TZSHORT,    "GMT"},
1774             {"Europe/London",       "en",   Boolean.FALSE,  TZLONG,     "Greenwich Mean Time"},
1775             {"Europe/London",       "en",   Boolean.TRUE,   TZSHORT,    "GMT+1" /*"BST"*/},
1776             {"Europe/London",       "en",   Boolean.TRUE,   TZLONG,     "British Summer Time"},
1777 
1778             {"America/Anchorage",   "en",   Boolean.FALSE,  TZSHORT,    "AKST"},
1779             {"America/Anchorage",   "en",   Boolean.FALSE,  TZLONG,     "Alaska Standard Time"},
1780             {"America/Anchorage",   "en",   Boolean.TRUE,   TZSHORT,    "AKDT"},
1781             {"America/Anchorage",   "en",   Boolean.TRUE,   TZLONG,     "Alaska Daylight Time"},
1782 
1783             // Southern Hemisphere, all data from meta:Australia_Western
1784             {"Australia/Perth",     "en",   Boolean.FALSE,  TZSHORT,    "GMT+8"/*"AWST"*/},
1785             {"Australia/Perth",     "en",   Boolean.FALSE,  TZLONG,     "Australian Western Standard Time"},
1786             // Note: Perth does not observe DST currently. When display name is missing,
1787             // the localized GMT format with the current offset is used even daylight name was
1788             // requested. See #9350.
1789             {"Australia/Perth",     "en",   Boolean.TRUE,   TZSHORT,    "GMT+8"/*"AWDT"*/},
1790             {"Australia/Perth",     "en",   Boolean.TRUE,   TZLONG,     "Australian Western Daylight Time"},
1791 
1792             {"America/Sao_Paulo",   "en",   Boolean.FALSE,  TZSHORT,    "GMT-3"/*"BRT"*/},
1793             {"America/Sao_Paulo",   "en",   Boolean.FALSE,  TZLONG,     "Brasilia Standard Time"},
1794             // Per https://mm.icann.org/pipermail/tz-announce/2019-July/000056.html
1795             // Brazil has canceled DST and will stay on standard time indefinitely.
1796             // {"America/Sao_Paulo",   "en",   Boolean.TRUE,   TZSHORT,    "GMT-2"/*"BRST"*/},
1797             // {"America/Sao_Paulo",   "en",   Boolean.TRUE,   TZLONG,     "Brasilia Summer Time"},
1798 
1799             // No Summer Time, but had it before 1983.
1800             {"Pacific/Honolulu",    "en",   Boolean.FALSE,  TZSHORT,    "HST"},
1801             {"Pacific/Honolulu",    "en",   Boolean.FALSE,  TZLONG,     "Hawaii-Aleutian Standard Time"},
1802             {"Pacific/Honolulu",    "en",   Boolean.TRUE,   TZSHORT,    "HDT"},
1803             {"Pacific/Honolulu",    "en",   Boolean.TRUE,   TZLONG,     "Hawaii-Aleutian Daylight Time"},
1804 
1805             // Northern, has Summer, not commonly used.
1806             {"Europe/Helsinki",     "en",   Boolean.FALSE,  TZSHORT,    "GMT+2"/*"EET"*/},
1807             {"Europe/Helsinki",     "en",   Boolean.FALSE,  TZLONG,     "Eastern European Standard Time"},
1808             {"Europe/Helsinki",     "en",   Boolean.TRUE,   TZSHORT,    "GMT+3"/*"EEST"*/},
1809             {"Europe/Helsinki",     "en",   Boolean.TRUE,   TZLONG,     "Eastern European Summer Time"},
1810 
1811             // Repeating the test data for DST.  The test data below trigger the problem reported
1812             // by Ticket#6644
1813             {"Europe/London",       "en",   Boolean.TRUE,   TZSHORT,    "GMT+1" /*"BST"*/},
1814             {"Europe/London",       "en",   Boolean.TRUE,   TZLONG,     "British Summer Time"},
1815         };
1816 
1817         boolean isICUTimeZone = (TimeZone.getDefaultTimeZoneType() == TimeZone.TIMEZONE_ICU);
1818 
1819         boolean sawAnError = false;
1820         for (int testNum = 0; testNum < zoneDisplayTestData.length; testNum++) {
1821             ULocale locale = new ULocale((String)zoneDisplayTestData[testNum][1]);
1822             TimeZone zone = TimeZone.getTimeZone((String)zoneDisplayTestData[testNum][0]);
1823             String displayName = zone.getDisplayName(((Boolean)zoneDisplayTestData[testNum][2]).booleanValue(),
1824                     ((Integer)zoneDisplayTestData[testNum][3]).intValue());
1825             if (!displayName.equals(zoneDisplayTestData[testNum][4])) {
1826                 if (isDevelopmentBuild
1827                         && (isICUTimeZone || !((Boolean)zoneDisplayTestData[testNum][2]).booleanValue())) {
1828                     sawAnError = true;
1829                     errln("Incorrect time zone display name.  zone = "
1830                             + zoneDisplayTestData[testNum][0] + ",\n"
1831                             + "   locale = " + locale
1832                             + ",   style = " + (zoneDisplayTestData[testNum][3] == TZSHORT ? "SHORT" : "LONG")
1833                             + ",   Summertime = " + zoneDisplayTestData[testNum][2] + "\n"
1834                             + "   Expected " + zoneDisplayTestData[testNum][4]
1835                             + ",   Got " + displayName);
1836                 } else {
1837                     logln("Incorrect time zone display name.  zone = "
1838                             + zoneDisplayTestData[testNum][0] + ",\n"
1839                             + "   locale = " + locale
1840                             + ",   style = " + (zoneDisplayTestData[testNum][3] == TZSHORT ? "SHORT" : "LONG")
1841                             + ",   Summertime = " + zoneDisplayTestData[testNum][2] + "\n"
1842                             + "   Expected " + zoneDisplayTestData[testNum][4]
1843                             + ",   Got " + displayName);
1844                 }
1845             }
1846         }
1847         if (sawAnError) {
1848             logln("Note: Errors could be the result of changes to zoneStrings locale data");
1849         }
1850     }
1851 
1852     /*
1853      * Test case for hashCode problem reported by ticket#7690 OlsonTimeZone.hashCode() throws NPE.
1854      */
1855     @Test
TestHashCode()1856     public void TestHashCode() {
1857         String[] ids = TimeZone.getAvailableIDs();
1858 
1859         for (String id: ids) {
1860             TimeZone tz1 = TimeZone.getTimeZone(id);
1861             TimeZone tz2 = TimeZone.getTimeZone(id);
1862 
1863             // hash code are same for the same time zone
1864             if (tz1.hashCode() != tz2.hashCode()) {
1865                 errln("Fail: Two time zone instances for " + id + " have different hash values.");
1866             }
1867             // string representation should be also same
1868             if (!tz1.toString().equals(tz2.toString())) {
1869                 errln("Fail: Two time zone instances for " + id + " have different toString() values.");
1870             }
1871         }
1872     }
1873 
1874     /*
1875      * Test case for getRegion
1876      */
1877     @Test
TestGetRegion()1878     public void TestGetRegion() {
1879         final String[][] TEST_DATA = {
1880             {"America/Los_Angeles",             "US"},
1881             {"America/Indianapolis",            "US"},  // CLDR canonical, Olson backward
1882             {"America/Indiana/Indianapolis",    "US"},  // CLDR alias
1883             {"Mexico/General",                  "MX"},  // Link America/Mexico_City, Olson backward
1884             {"Etc/UTC",                         "001"},
1885             {"EST5EDT",                         "001"},
1886             {"PST",                             "US"},  // Link America/Los_Angeles
1887             {"Europe/Helsinki",                 "FI"},
1888             {"Europe/Mariehamn",                "AX"},  // Link Europe/Helsinki, but in zone.tab
1889             {"Asia/Riyadh",                     "SA"},
1890             // tz file solar87 was removed from tzdata2013i
1891             // {"Asia/Riyadh87",                   "001"}, // this should be "SA" actually, but not in zone.tab
1892             {"Etc/Unknown",                     null},  // CLDR canonical, but not a sysmte zone ID
1893             {"bogus",                           null},  // bogus
1894             {"GMT+08:00",                       null},  // a custom ID, not a system zone ID
1895         };
1896 
1897         for (String[] test : TEST_DATA) {
1898             try {
1899                 String region = TimeZone.getRegion(test[0]);
1900                 if (!region.equals(test[1])) {
1901                     if (test[1] == null) {
1902                         errln("Fail: getRegion(\"" + test[0] + "\") returns "
1903                                 + region + " [expected: IllegalArgumentException]");
1904                     } else {
1905                         errln("Fail: getRegion(\"" + test[0] + "\") returns "
1906                                 + region + " [expected: " + test[1] + "]");
1907                     }
1908                 }
1909             } catch (IllegalArgumentException e) {
1910                 if (test[1] != null) {
1911                     errln("Fail: getRegion(\"" + test[0]
1912                                 + "\") throws IllegalArgumentException [expected: " + test[1] + "]");
1913                 }
1914             }
1915         }
1916     }
1917 
1918     @Test
TestZoneFields()1919     public void TestZoneFields() {
1920         assertEquals("UNKNOWN_ZONE wrong ID", "Etc/Unknown", TimeZone.UNKNOWN_ZONE.getID());
1921         assertEquals("UNKNOWN_ZONE wrong offset", 0, TimeZone.UNKNOWN_ZONE.getRawOffset());
1922         assertFalse("UNKNOWN_ZONE uses DST", TimeZone.UNKNOWN_ZONE.useDaylightTime());
1923 
1924         assertEquals("GMT_ZONE wrong ID", "Etc/GMT", TimeZone.GMT_ZONE.getID());
1925         assertEquals("GMT_ZONE wrong offset", 0, TimeZone.GMT_ZONE.getRawOffset());
1926         assertFalse("GMT_ZONE uses DST", TimeZone.GMT_ZONE.useDaylightTime());
1927     }
1928 
1929     /*
1930      * Test case for Freezable
1931      */
1932     @Test
TestFreezable()1933     public void TestFreezable() {
1934         // Test zones - initially thawed
1935         TimeZone[] ZA1 = {
1936             TimeZone.getDefault(),
1937             TimeZone.getTimeZone("America/Los_Angeles", TimeZone.TIMEZONE_ICU),
1938             TimeZone.getTimeZone("America/Los_Angeles", TimeZone.TIMEZONE_JDK),
1939             new SimpleTimeZone(0, "stz"),
1940             new RuleBasedTimeZone("rbtz", new InitialTimeZoneRule("rbtz0", 0, 0)),
1941             VTimeZone.create("America/New_York"),
1942         };
1943 
1944         checkThawed(ZA1, "ZA1");
1945         // freeze
1946         for (int i = 0; i < ZA1.length; i++) {
1947             ZA1[i].freeze();
1948         }
1949         checkFrozen(ZA1, "ZA1(frozen)");
1950 
1951         // Test zones - initially frozen
1952         final TimeZone[] ZA2 = {
1953             TimeZone.GMT_ZONE,
1954             TimeZone.UNKNOWN_ZONE,
1955             TimeZone.getFrozenTimeZone("America/Los_Angeles"),
1956             new SimpleTimeZone(3600000, "frz_stz").freeze(),
1957             new RuleBasedTimeZone("frz_rbtz", new InitialTimeZoneRule("frz_rbtz0", 3600000, 0)).freeze(),
1958             VTimeZone.create("Asia/Tokyo").freeze(),
1959         };
1960 
1961         checkFrozen(ZA2, "ZA2");
1962         TimeZone[] ZA2_thawed = new TimeZone[ZA2.length];
1963         // create thawed clone
1964         for (int i = 0; i < ZA2_thawed.length; i++) {
1965             ZA2_thawed[i] = ZA2[i].cloneAsThawed();
1966         }
1967         checkThawed(ZA2_thawed, "ZA2(thawed)");
1968 
1969     }
1970 
checkThawed(TimeZone[] thawedZones, String zaName)1971     private void checkThawed(TimeZone[] thawedZones, String zaName) {
1972         for (int i = 0; i < thawedZones.length; i++) {
1973             if (thawedZones[i].isFrozen()) {
1974                 errln("Fail: " + zaName + "[" + i + "] is frozen.");
1975             }
1976 
1977             // clone
1978             TimeZone copy = (TimeZone)thawedZones[i].clone();
1979             if (thawedZones[i] == copy || !thawedZones[i].equals(copy)) {
1980                 errln("Fail: " + zaName + "[" + i + "] - clone does not work.");
1981             }
1982 
1983             // cloneAsThawed
1984             TimeZone thawed = thawedZones[i].cloneAsThawed();
1985             if (thawed.isFrozen() || !thawedZones[i].equals(thawed)) {
1986                 errln("Fail: " + zaName + "[" + i + "] - cloneAsThawed does not work.");
1987             }
1988 
1989             // setID
1990             try {
1991                 String newID = "foo";
1992                 thawedZones[i].setID(newID);
1993                 if (!thawedZones[i].getID().equals(newID)) {
1994                     errln("Fail: " + zaName + "[" + i + "] - setID(\"" + newID + "\") does not work.");
1995                 }
1996             } catch (UnsupportedOperationException e) {
1997                 errln("Fail: " + zaName + "[" + i + "] - setID throws UnsupportedOperationException.");
1998             }
1999 
2000             // setRawOffset
2001             if (!(thawedZones[i] instanceof RuleBasedTimeZone)) {    // RuleBasedTimeZone does not supprot setRawOffset
2002                 try {
2003                     int newOffset = -3600000;
2004                     thawedZones[i].setRawOffset(newOffset);
2005                     if (thawedZones[i].getRawOffset() != newOffset) {
2006                         errln("Fail: " + zaName + "[" + i + "] - setRawOffset(" + newOffset + ") does not work.");
2007                     }
2008                 } catch (UnsupportedOperationException e) {
2009                     errln("Fail: " + zaName + "[" + i + "] - setRawOffset throws UnsupportedOperationException.");
2010                 }
2011             }
2012 
2013             if (thawedZones[i] instanceof SimpleTimeZone) {
2014                 SimpleTimeZone stz = (SimpleTimeZone)thawedZones[i];
2015                 // setDSTSavings
2016                 try {
2017                     int newDSTSavings = 1800000;
2018                     stz.setDSTSavings(newDSTSavings);
2019                     if (stz.getDSTSavings() != newDSTSavings) {
2020                         errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setDSTSavings(" + newDSTSavings + ") does not work.");
2021                     }
2022                 } catch (UnsupportedOperationException e) {
2023                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setDSTSavings throws UnsupportedOperationException.");
2024                 }
2025                 // setStartRule
2026                 try {
2027                     stz.setStartRule(Calendar.JANUARY, -1, Calendar.SUNDAY, 0);
2028                 } catch (UnsupportedOperationException e) {
2029                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setStartRule throws UnsupportedOperationException.");
2030                 }
2031                 // setEndRule
2032                 try {
2033                     stz.setEndRule(Calendar.DECEMBER, 1, Calendar.SUNDAY, 0);
2034                 } catch (UnsupportedOperationException e) {
2035                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setEndRule throws UnsupportedOperationException.");
2036                 }
2037                 // setStartYear
2038                 try {
2039                     stz.setStartYear(2000);
2040                 } catch (UnsupportedOperationException e) {
2041                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setStartYear throws UnsupportedOperationException.");
2042                 }
2043             } else if (thawedZones[i] instanceof RuleBasedTimeZone) {
2044                 RuleBasedTimeZone rbtz = (RuleBasedTimeZone)thawedZones[i];
2045                 // addTransitionRule
2046                 try {
2047                     TimeArrayTimeZoneRule tr1 = new TimeArrayTimeZoneRule("tr1", 7200000, 0, new long[] {0}, DateTimeRule.UTC_TIME);
2048                     rbtz.addTransitionRule(tr1);
2049                 } catch (UnsupportedOperationException e) {
2050                     errln("Fail: (RuleBasedTimeZone)" + zaName + "[" + i + "] - addTransitionRule throws UnsupportedOperationException.");
2051                 }
2052             } else if (thawedZones[i] instanceof VTimeZone) {
2053                 VTimeZone vtz = (VTimeZone)thawedZones[i];
2054                 // setTZURL
2055                 try {
2056                     String tzUrl = "http://icu-project.org/timezone";
2057                     vtz.setTZURL(tzUrl);
2058                     if (!vtz.getTZURL().equals(tzUrl)) {
2059                         errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setTZURL does not work.");
2060                     }
2061                 } catch (UnsupportedOperationException e) {
2062                     errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setTZURL throws UnsupportedOperationException.");
2063                 }
2064                 // setLastModified
2065                 try {
2066                     Date d = new Date();
2067                     vtz.setLastModified(d);
2068                     if (!vtz.getLastModified().equals(d)) {
2069                         errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setLastModified does not work.");
2070                     }
2071                 } catch (UnsupportedOperationException e) {
2072                     errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setLastModified throws UnsupportedOperationException.");
2073                 }
2074             }
2075         }
2076     }
2077 
checkFrozen(TimeZone[] frozenZones, String zaName)2078     private void checkFrozen(TimeZone[] frozenZones, String zaName) {
2079         for (int i = 0; i < frozenZones.length; i++) {
2080             if (!frozenZones[i].isFrozen()) {
2081                 errln("Fail: " + zaName + "[" + i + "] is not frozen.");
2082             }
2083 
2084             // clone
2085             TimeZone copy = (TimeZone)frozenZones[i].clone();
2086             if (frozenZones[i] != copy) {
2087                 errln("Fail: " + zaName + "[" + i + "] - clone does not return the object itself.");
2088             }
2089 
2090             // cloneAsThawed
2091             TimeZone thawed = frozenZones[i].cloneAsThawed();
2092             if (thawed.isFrozen() || !frozenZones[i].equals(thawed)) {
2093                 errln("Fail: " + zaName + "[" + i + "] - cloneAsThawed does not work.");
2094             }
2095 
2096             // setID
2097             try {
2098                 String newID = "foo";
2099                 frozenZones[i].setID(newID);
2100                 errln("Fail: " + zaName + "[" + i + "] - setID must throw UnsupportedOperationException.");
2101             } catch (UnsupportedOperationException e) {
2102                 // OK
2103             }
2104 
2105             // setRawOffset
2106             if (!(frozenZones[i] instanceof RuleBasedTimeZone)) {    // RuleBasedTimeZone does not supprot setRawOffset
2107                 try {
2108                     int newOffset = -3600000;
2109                     frozenZones[i].setRawOffset(newOffset);
2110                     errln("Fail: " + zaName + "[" + i + "] - setRawOffset must throw UnsupportedOperationException.");
2111                 } catch (UnsupportedOperationException e) {
2112                     // OK
2113                 }
2114             }
2115 
2116             if (frozenZones[i] instanceof SimpleTimeZone) {
2117                 SimpleTimeZone stz = (SimpleTimeZone)frozenZones[i];
2118                 // setDSTSavings
2119                 try {
2120                     int newDSTSavings = 1800000;
2121                     stz.setDSTSavings(newDSTSavings);
2122                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setDSTSavings must throw UnsupportedOperationException.");
2123                 } catch (UnsupportedOperationException e) {
2124                     // OK
2125                 }
2126                 // setStartRule
2127                 try {
2128                     stz.setStartRule(Calendar.JANUARY, -1, Calendar.SUNDAY, 0);
2129                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setStartRule must throw UnsupportedOperationException.");
2130                 } catch (UnsupportedOperationException e) {
2131                     // OK
2132                 }
2133                 // setEndRule
2134                 try {
2135                     stz.setEndRule(Calendar.DECEMBER, 1, Calendar.SUNDAY, 0);
2136                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setEndRule must throw UnsupportedOperationException.");
2137                 } catch (UnsupportedOperationException e) {
2138                     // OK
2139                 }
2140                 // setStartYear
2141                 try {
2142                     stz.setStartYear(2000);
2143                     errln("Fail: (SimpleTimeZone)" + zaName + "[" + i + "] - setStartYear must throw UnsupportedOperationException.");
2144                 } catch (UnsupportedOperationException e) {
2145                     // OK
2146                 }
2147             } else if (frozenZones[i] instanceof RuleBasedTimeZone) {
2148                 RuleBasedTimeZone rbtz = (RuleBasedTimeZone)frozenZones[i];
2149                 // addTransitionRule
2150                 try {
2151                     TimeArrayTimeZoneRule tr1 = new TimeArrayTimeZoneRule("tr1", 7200000, 0, new long[] {0}, DateTimeRule.UTC_TIME);
2152                     rbtz.addTransitionRule(tr1);
2153                     errln("Fail: (RuleBasedTimeZone)" + zaName + "[" + i + "] - addTransitionRule must throw UnsupportedOperationException.");
2154                 } catch (UnsupportedOperationException e) {
2155                     // OK
2156                 }
2157             } else if (frozenZones[i] instanceof VTimeZone) {
2158                 VTimeZone vtz = (VTimeZone)frozenZones[i];
2159                 // setTZURL
2160                 try {
2161                     String tzUrl = "http://icu-project.org/timezone";
2162                     vtz.setTZURL(tzUrl);
2163                     errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setTZURL must throw UnsupportedOperationException.");
2164                 } catch (UnsupportedOperationException e) {
2165                     // OK
2166                 }
2167                 // setLastModified
2168                 try {
2169                     Date d = new Date();
2170                     vtz.setLastModified(d);
2171                     errln("Fail: (VTimeZone)" + zaName + "[" + i + "] - setLastModified must throw UnsupportedOperationException.");
2172                 } catch (UnsupportedOperationException e) {
2173                     // OK
2174                 }
2175             }
2176         }
2177     }
2178 
2179     @Test
TestObservesDaylightTime()2180     public void TestObservesDaylightTime() {
2181         boolean observesDaylight;
2182         long current = System.currentTimeMillis();
2183 
2184         String[] tzids = TimeZone.getAvailableIDs();
2185         for (String tzid : tzids) {
2186             // OlsonTimeZone
2187             TimeZone tz = TimeZone.getTimeZone(tzid, TimeZone.TIMEZONE_ICU);
2188             observesDaylight = tz.observesDaylightTime();
2189             if (observesDaylight != isDaylightTimeAvailable(tz, current)) {
2190                 errln("Fail: [OlsonTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid);
2191             }
2192 
2193             // RuleBasedTimeZone
2194             RuleBasedTimeZone rbtz = createRBTZ((BasicTimeZone)tz, current);
2195             boolean observesDaylightRBTZ = rbtz.observesDaylightTime();
2196             if (observesDaylightRBTZ != isDaylightTimeAvailable(rbtz, current)) {
2197                 errln("Fail: [RuleBasedTimeZone] observesDaylightTime() returned " + observesDaylightRBTZ + " for " + rbtz.getID());
2198             } else if (observesDaylight != observesDaylightRBTZ) {
2199                 errln("Fail: RuleBasedTimeZone " + rbtz.getID() + " returns " + observesDaylightRBTZ + ", but different from match OlsonTimeZone");
2200             }
2201 
2202             // JavaTimeZone
2203             tz = TimeZone.getTimeZone(tzid, TimeZone.TIMEZONE_JDK);
2204             observesDaylight = tz.observesDaylightTime();
2205             if (observesDaylight != isDaylightTimeAvailable(tz, current)) {
2206                 errln("Fail: [JavaTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid);
2207             }
2208 
2209             // VTimeZone
2210             tz = VTimeZone.getTimeZone(tzid);
2211             observesDaylight = tz.observesDaylightTime();
2212             if (observesDaylight != isDaylightTimeAvailable(tz, current)) {
2213                 errln("Fail: [VTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + tzid);
2214             }
2215         }
2216 
2217         // SimpleTimeZone
2218         SimpleTimeZone[] stzs = {
2219             new SimpleTimeZone(0, "STZ0"),
2220             new SimpleTimeZone(-5*60*60*1000, "STZ-5D", Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000,
2221                     Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*60*60*1000),
2222         };
2223         for (SimpleTimeZone stz : stzs) {
2224             observesDaylight = stz.observesDaylightTime();
2225             if (observesDaylight != isDaylightTimeAvailable(stz, current)) {
2226                 errln("Fail: [SimpleTimeZone] observesDaylightTime() returned " + observesDaylight + " for " + stz.getID());
2227             }
2228         }
2229     }
2230 
2231     @Test
Test11619_UnrecognizedTimeZoneID()2232     public void Test11619_UnrecognizedTimeZoneID() {
2233         VTimeZone vzone = VTimeZone.create("ABadTimeZoneId");
2234         TestFmwk.assertNull("", vzone);
2235     }
2236 
isDaylightTimeAvailable(TimeZone tz, long start)2237     private static boolean isDaylightTimeAvailable(TimeZone tz, long start) {
2238         if (tz.inDaylightTime(new Date(start))) {
2239             return true;
2240         }
2241 
2242         long date;
2243         if (tz instanceof BasicTimeZone) {
2244             BasicTimeZone btz = (BasicTimeZone)tz;
2245             // check future transitions, up to 100
2246             date = start;
2247             for (int i = 0; i < 100; i++) {
2248                 TimeZoneTransition tzt = btz.getNextTransition(date, false);
2249                 if (tzt == null) {
2250                     // no more transitions
2251                     break;
2252                 }
2253                 if (tzt.getTo().getDSTSavings() != 0) {
2254                     return true;
2255                 }
2256                 date = tzt.getTime();
2257             }
2258         } else {
2259             // check future times by incrementing 30 days, up to 200 times (about 16 years)
2260             final long inc = 30L * 24 * 60 * 60 * 1000;
2261             int[] offsets = new int[2];
2262             date = start + inc;
2263             for (int i = 0; i < 200; i++, date += inc) {
2264                 tz.getOffset(date, false, offsets);
2265                 if (offsets[1] != 0) {
2266                     return true;
2267                 }
2268             }
2269         }
2270         return false;
2271     }
2272 
createRBTZ(BasicTimeZone btz, long start)2273     private static RuleBasedTimeZone createRBTZ(BasicTimeZone btz, long start) {
2274         TimeZoneRule[] rules = btz.getTimeZoneRules(start);
2275         RuleBasedTimeZone rbtz = new RuleBasedTimeZone("RBTZ:btz.getID()", (InitialTimeZoneRule)rules[0]);
2276         for (int i = 1; i < rules.length; i++) {
2277             rbtz.addTransitionRule(rules[i]);
2278         }
2279         return rbtz;
2280      }
2281 
2282     @Test
TestGetWindowsID()2283     public void TestGetWindowsID() {
2284         String[][] TESTDATA = {
2285             {"America/New_York",        "Eastern Standard Time"},
2286             {"America/Montreal",        "Eastern Standard Time"},
2287             {"America/Los_Angeles",     "Pacific Standard Time"},
2288             {"America/Vancouver",       "Pacific Standard Time"},
2289             {"Asia/Shanghai",           "China Standard Time"},
2290             {"Asia/Chongqing",          "China Standard Time"},
2291             {"America/Indianapolis",    "US Eastern Standard Time"},            // CLDR canonical name
2292             {"America/Indiana/Indianapolis",    "US Eastern Standard Time"},    // tzdb canonical name
2293             {"Asia/Khandyga",           "Yakutsk Standard Time"},
2294             {"Australia/Eucla",         "Aus Central W. Standard Time"}, // Now Windows does have a mapping
2295             {"Bogus",                   null},
2296         };
2297 
2298         for (String[] data : TESTDATA) {
2299             String winID = TimeZone.getWindowsID(data[0]);
2300             assertEquals("Fail: ID=" + data[0], data[1], winID);
2301         }
2302     }
2303 
2304     @Test
TestGetIDForWindowsID()2305     public void TestGetIDForWindowsID() {
2306         final String[][] TESTDATA = {
2307             {"Eastern Standard Time",   null,   "America/New_York"},
2308             {"Eastern Standard Time",   "US",   "America/New_York"},
2309             {"Eastern Standard Time",   "CA",   "America/Toronto"},
2310             {"Eastern Standard Time",   "CN",   "America/New_York"},
2311             {"China Standard Time",     null,   "Asia/Shanghai"},
2312             {"China Standard Time",     "CN",   "Asia/Shanghai"},
2313             {"China Standard Time",     "HK",   "Asia/Hong_Kong"},
2314             {"Mid-Atlantic Standard Time",  null,   null}, // No tz database mapping
2315             {"Bogus",                   null,   null},
2316         };
2317 
2318         for (String[] data : TESTDATA) {
2319             String id = TimeZone.getIDForWindowsID(data[0], data[1]);
2320             assertEquals("Fail: Windows ID=" + data[0] + ", Region=" + data[1],
2321                     data[2], id);
2322         }
2323     }
2324 
2325     @Test
TestTimeZoneAdapterEquals()2326     public void TestTimeZoneAdapterEquals() {
2327         String idChicago = "America/Chicago";
2328         TimeZone icuChicago = TimeZone.getTimeZone(idChicago);
2329         TimeZone icuChicago2 = TimeZone.getTimeZone(idChicago);
2330         java.util.TimeZone icuChicagoWrapped = TimeZoneAdapter.wrap(icuChicago);
2331         java.util.TimeZone icuChicagoWrapped2 = TimeZoneAdapter.wrap(icuChicago2);
2332 
2333         assertFalse("Compare TimeZone and TimeZoneAdapter", icuChicago.equals(icuChicagoWrapped));
2334         assertFalse("Compare TimeZoneAdapter with TimeZone", icuChicagoWrapped.equals(icuChicago));
2335         assertTrue("Compare two TimeZoneAdapters", icuChicagoWrapped.equals(icuChicagoWrapped2));
2336     }
2337 }
2338 
2339 //eof
2340