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