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