Home | History | Annotate | Download | only in intltest
      1 /***********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1997-2014, International Business Machines Corporation
      4  * and others. All Rights Reserved.
      5  ***********************************************************************/
      6 
      7 #include "unicode/utypes.h"
      8 
      9 #if !UCONFIG_NO_FORMATTING
     10 
     11 #include "unicode/timezone.h"
     12 #include "unicode/simpletz.h"
     13 #include "unicode/calendar.h"
     14 #include "unicode/gregocal.h"
     15 #include "unicode/resbund.h"
     16 #include "unicode/strenum.h"
     17 #include "unicode/uversion.h"
     18 #include "tztest.h"
     19 #include "cmemory.h"
     20 #include "putilimp.h"
     21 #include "cstring.h"
     22 #include "olsontz.h"
     23 
     24 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
     25 
     26 #define CASE(id,test) case id:                               \
     27                           name = #test;                      \
     28                           if (exec) {                        \
     29                               logln(#test "---"); logln(""); \
     30                               test();                        \
     31                           }                                  \
     32                           break
     33 
     34 // *****************************************************************************
     35 // class TimeZoneTest
     36 // *****************************************************************************
     37 
     38 // Some test case data is current date/tzdata version sensitive and producing errors
     39 // when year/rule are changed. Although we want to keep our eyes on test failures
     40 // caused by tzdata changes while development, keep maintaining test data in maintenance
     41 // stream is a little bit hassle. ICU 49 or later versions are using minor version field
     42 // to indicate a development build (0) or official release build (others). For development
     43 // builds, a test failure triggers an error, while release builds only report them in
     44 // verbose mode with logln.
     45 static UBool isDevelopmentBuild = (U_ICU_VERSION_MINOR_NUM == 0);
     46 
     47 void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     48 {
     49     if (exec) {
     50         logln("TestSuite TestTimeZone");
     51     }
     52     TESTCASE_AUTO_BEGIN;
     53     TESTCASE_AUTO(TestPRTOffset);
     54     TESTCASE_AUTO(TestVariousAPI518);
     55     TESTCASE_AUTO(TestGetAvailableIDs913);
     56     TESTCASE_AUTO(TestGenericAPI);
     57     TESTCASE_AUTO(TestRuleAPI);
     58     TESTCASE_AUTO(TestShortZoneIDs);
     59     TESTCASE_AUTO(TestCustomParse);
     60     TESTCASE_AUTO(TestDisplayName);
     61     TESTCASE_AUTO(TestDSTSavings);
     62     TESTCASE_AUTO(TestAlternateRules);
     63     TESTCASE_AUTO(TestCountries);
     64     TESTCASE_AUTO(TestHistorical);
     65     TESTCASE_AUTO(TestEquivalentIDs);
     66     TESTCASE_AUTO(TestAliasedNames);
     67     TESTCASE_AUTO(TestFractionalDST);
     68     TESTCASE_AUTO(TestFebruary);
     69     TESTCASE_AUTO(TestCanonicalIDAPI);
     70     TESTCASE_AUTO(TestCanonicalID);
     71     TESTCASE_AUTO(TestDisplayNamesMeta);
     72     TESTCASE_AUTO(TestGetRegion);
     73     TESTCASE_AUTO(TestGetAvailableIDsNew);
     74     TESTCASE_AUTO(TestGetUnknown);
     75     TESTCASE_AUTO(TestGetWindowsID);
     76     TESTCASE_AUTO(TestGetIDForWindowsID);
     77     TESTCASE_AUTO_END;
     78 }
     79 
     80 const int32_t TimeZoneTest::millisPerHour = 3600000;
     81 
     82 // ---------------------------------------------------------------------------------
     83 
     84 /**
     85  * Generic API testing for API coverage.
     86  */
     87 void
     88 TimeZoneTest::TestGenericAPI()
     89 {
     90     UnicodeString id("NewGMT");
     91     int32_t offset = 12345;
     92 
     93     SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
     94     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
     95 
     96     TimeZone* zoneclone = zone->clone();
     97     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
     98     zoneclone->setID("abc");
     99     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
    100     delete zoneclone;
    101 
    102     zoneclone = zone->clone();
    103     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
    104     zoneclone->setRawOffset(45678);
    105     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
    106 
    107     SimpleTimeZone copy(*zone);
    108     if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
    109     copy = *(SimpleTimeZone*)zoneclone;
    110     if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
    111 
    112     TimeZone* saveDefault = TimeZone::createDefault();
    113     logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id));
    114 
    115     TimeZone::adoptDefault(zone);
    116     TimeZone* defaultzone = TimeZone::createDefault();
    117     if (defaultzone == zone ||
    118         !(*defaultzone == *zone))
    119         errln("FAIL: createDefault failed");
    120     TimeZone::adoptDefault(saveDefault);
    121     delete defaultzone;
    122     delete zoneclone;
    123 
    124     logln("call uprv_timezone() which uses the host");
    125     logln("to get the difference in seconds between coordinated universal");
    126     logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
    127 
    128     int32_t tzoffset = uprv_timezone();
    129     if ((tzoffset % 900) != 0) {
    130         /*
    131          * Ticket#6364 and #7648
    132          * A few time zones are using GMT offests not a multiple of 15 minutes.
    133          * Therefore, we should not interpret such case as an error.
    134          * We downgrade this from errln to infoln. When we see this message,
    135          * we should examine if it is ignorable or not.
    136          */
    137         infoln("WARNING: t_timezone may be incorrect. It is not a multiple of 15min.", tzoffset);
    138     }
    139 
    140     UErrorCode status = U_ZERO_ERROR;
    141     const char* tzver = TimeZone::getTZDataVersion(status);
    142     if (U_FAILURE(status)) {
    143         errcheckln(status, "FAIL: getTZDataVersion failed - %s", u_errorName(status));
    144     } else if (uprv_strlen(tzver) != 5 /* 4 digits + 1 letter */) {
    145         errln((UnicodeString)"FAIL: getTZDataVersion returned " + tzver);
    146     } else {
    147         logln((UnicodeString)"tzdata version: " + tzver);
    148     }
    149 }
    150 
    151 // ---------------------------------------------------------------------------------
    152 
    153 /**
    154  * Test the setStartRule/setEndRule API calls.
    155  */
    156 void
    157 TimeZoneTest::TestRuleAPI()
    158 {
    159     UErrorCode status = U_ZERO_ERROR;
    160 
    161     UDate offset = 60*60*1000*1.75; // Pick a weird offset
    162     SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone");
    163     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
    164 
    165     // Establish our expected transition times.  Do this with a non-DST
    166     // calendar with the (above) declared local offset.
    167     GregorianCalendar *gc = new GregorianCalendar(*zone, status);
    168     if (failure(status, "new GregorianCalendar", TRUE)) return;
    169     gc->clear();
    170     gc->set(1990, UCAL_MARCH, 1);
    171     UDate marchOneStd = gc->getTime(status); // Local Std time midnight
    172     gc->clear();
    173     gc->set(1990, UCAL_JULY, 1);
    174     UDate julyOneStd = gc->getTime(status); // Local Std time midnight
    175     if (failure(status, "GregorianCalendar::getTime")) return;
    176 
    177     // Starting and ending hours, WALL TIME
    178     int32_t startHour = (int32_t)(2.25 * 3600000);
    179     int32_t endHour   = (int32_t)(3.5  * 3600000);
    180 
    181     zone->setStartRule(UCAL_MARCH, 1, 0, startHour, status);
    182     zone->setEndRule  (UCAL_JULY,  1, 0, endHour, status);
    183 
    184     delete gc;
    185     gc = new GregorianCalendar(*zone, status);
    186     if (failure(status, "new GregorianCalendar")) return;
    187 
    188     UDate marchOne = marchOneStd + startHour;
    189     UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time
    190 
    191     UDate expMarchOne = 636251400000.0;
    192     if (marchOne != expMarchOne)
    193     {
    194         errln((UnicodeString)"FAIL: Expected start computed as " + marchOne +
    195           " = " + dateToString(marchOne));
    196         logln((UnicodeString)"      Should be                  " + expMarchOne +
    197           " = " + dateToString(expMarchOne));
    198     }
    199 
    200     UDate expJulyOne = 646793100000.0;
    201     if (julyOne != expJulyOne)
    202     {
    203         errln((UnicodeString)"FAIL: Expected start computed as " + julyOne +
    204           " = " + dateToString(julyOne));
    205         logln((UnicodeString)"      Should be                  " + expJulyOne +
    206           " = " + dateToString(expJulyOne));
    207     }
    208 
    209     testUsingBinarySearch(*zone, date(90, UCAL_JANUARY, 1), date(90, UCAL_JUNE, 15), marchOne);
    210     testUsingBinarySearch(*zone, date(90, UCAL_JUNE, 1), date(90, UCAL_DECEMBER, 31), julyOne);
    211 
    212     if (zone->inDaylightTime(marchOne - 1000, status) ||
    213         !zone->inDaylightTime(marchOne, status))
    214         errln("FAIL: Start rule broken");
    215     if (!zone->inDaylightTime(julyOne - 1000, status) ||
    216         zone->inDaylightTime(julyOne, status))
    217         errln("FAIL: End rule broken");
    218 
    219     zone->setStartYear(1991);
    220     if (zone->inDaylightTime(marchOne, status) ||
    221         zone->inDaylightTime(julyOne - 1000, status))
    222         errln("FAIL: Start year broken");
    223 
    224     failure(status, "TestRuleAPI");
    225     delete gc;
    226     delete zone;
    227 }
    228 
    229 void
    230 TimeZoneTest::findTransition(const TimeZone& tz,
    231                              UDate min, UDate max) {
    232     UErrorCode ec = U_ZERO_ERROR;
    233     UnicodeString id,s;
    234     UBool startsInDST = tz.inDaylightTime(min, ec);
    235     if (failure(ec, "TimeZone::inDaylightTime")) return;
    236     if (tz.inDaylightTime(max, ec) == startsInDST) {
    237         logln("Error: " + tz.getID(id) + ".inDaylightTime(" + dateToString(min) + ") = " + (startsInDST?"TRUE":"FALSE") +
    238               ", inDaylightTime(" + dateToString(max) + ") = " + (startsInDST?"TRUE":"FALSE"));
    239         return;
    240     }
    241     if (failure(ec, "TimeZone::inDaylightTime")) return;
    242     while ((max - min) > INTERVAL) {
    243         UDate mid = (min + max) / 2;
    244         if (tz.inDaylightTime(mid, ec) == startsInDST) {
    245             min = mid;
    246         } else {
    247             max = mid;
    248         }
    249         if (failure(ec, "TimeZone::inDaylightTime")) return;
    250     }
    251     min = 1000.0 * uprv_floor(min/1000.0);
    252     max = 1000.0 * uprv_floor(max/1000.0);
    253     logln(tz.getID(id) + " Before: " + min/1000 + " = " +
    254           dateToString(min,s,tz));
    255     logln(tz.getID(id) + " After:  " + max/1000 + " = " +
    256           dateToString(max,s,tz));
    257 }
    258 
    259 void
    260 TimeZoneTest::testUsingBinarySearch(const TimeZone& tz,
    261                                     UDate min, UDate max,
    262                                     UDate expectedBoundary)
    263 {
    264     UErrorCode status = U_ZERO_ERROR;
    265     UBool startsInDST = tz.inDaylightTime(min, status);
    266     if (failure(status, "TimeZone::inDaylightTime")) return;
    267     if (tz.inDaylightTime(max, status) == startsInDST) {
    268         logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE"));
    269         return;
    270     }
    271     if (failure(status, "TimeZone::inDaylightTime")) return;
    272     while ((max - min) > INTERVAL) {
    273         UDate mid = (min + max) / 2;
    274         if (tz.inDaylightTime(mid, status) == startsInDST) {
    275             min = mid;
    276         } else {
    277             max = mid;
    278         }
    279         if (failure(status, "TimeZone::inDaylightTime")) return;
    280     }
    281     logln(UnicodeString("Binary Search Before: ") + uprv_floor(0.5 + min) + " = " + dateToString(min));
    282     logln(UnicodeString("Binary Search After:  ") + uprv_floor(0.5 + max) + " = " + dateToString(max));
    283     UDate mindelta = expectedBoundary - min;
    284     UDate maxdelta = max - expectedBoundary;
    285     if (mindelta >= 0 &&
    286         mindelta <= INTERVAL &&
    287         maxdelta >= 0 &&
    288         maxdelta <= INTERVAL)
    289         logln(UnicodeString("PASS: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
    290     else
    291         errln(UnicodeString("FAIL: Expected bdry:  ") + expectedBoundary + " = " + dateToString(expectedBoundary));
    292 }
    293 
    294 const UDate TimeZoneTest::INTERVAL = 100;
    295 
    296 // ---------------------------------------------------------------------------------
    297 
    298 // -------------------------------------
    299 
    300 /**
    301  * Test the offset of the PRT timezone.
    302  */
    303 void
    304 TimeZoneTest::TestPRTOffset()
    305 {
    306     TimeZone* tz = TimeZone::createTimeZone("PRT");
    307     if (tz == 0) {
    308         errln("FAIL: TimeZone(PRT) is null");
    309     }
    310     else {
    311       int32_t expectedHour = -4;
    312       double expectedOffset = (((double)expectedHour) * millisPerHour);
    313       double foundOffset = tz->getRawOffset();
    314       int32_t foundHour = (int32_t)foundOffset / millisPerHour;
    315       if (expectedOffset != foundOffset) {
    316         dataerrln("FAIL: Offset for PRT should be %d, found %d", expectedHour, foundHour);
    317       } else {
    318         logln("PASS: Offset for PRT should be %d, found %d", expectedHour, foundHour);
    319       }
    320     }
    321     delete tz;
    322 }
    323 
    324 // -------------------------------------
    325 
    326 /**
    327  * Regress a specific bug with a sequence of API calls.
    328  */
    329 void
    330 TimeZoneTest::TestVariousAPI518()
    331 {
    332     UErrorCode status = U_ZERO_ERROR;
    333     TimeZone* time_zone = TimeZone::createTimeZone("PST");
    334     UDate d = date(97, UCAL_APRIL, 30);
    335     UnicodeString str;
    336     logln("The timezone is " + time_zone->getID(str));
    337     if (!time_zone->inDaylightTime(d, status)) dataerrln("FAIL: inDaylightTime returned FALSE");
    338     if (failure(status, "TimeZone::inDaylightTime", TRUE)) return;
    339     if (!time_zone->useDaylightTime()) dataerrln("FAIL: useDaylightTime returned FALSE");
    340     if (time_zone->getRawOffset() != - 8 * millisPerHour) dataerrln("FAIL: getRawOffset returned wrong value");
    341     GregorianCalendar *gc = new GregorianCalendar(status);
    342     if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; }
    343     gc->setTime(d, status);
    344     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; }
    345     if (time_zone->getOffset(gc->AD, gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status),
    346         gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status), 0, status) != - 7 * millisPerHour)
    347         dataerrln("FAIL: getOffset returned wrong value");
    348     if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; }
    349     delete gc;
    350     delete time_zone;
    351 }
    352 
    353 // -------------------------------------
    354 
    355 /**
    356  * Test the call which retrieves the available IDs.
    357  */
    358 void
    359 TimeZoneTest::TestGetAvailableIDs913()
    360 {
    361     UErrorCode ec = U_ZERO_ERROR;
    362     int32_t i;
    363 
    364 #ifdef U_USE_TIMEZONE_OBSOLETE_2_8
    365     // Test legacy API -- remove these tests when the corresponding API goes away (duh)
    366     int32_t numIDs = -1;
    367     const UnicodeString** ids = TimeZone::createAvailableIDs(numIDs);
    368     if (ids == 0 || numIDs < 1) {
    369         errln("FAIL: createAvailableIDs()");
    370     } else {
    371         UnicodeString buf("TimeZone::createAvailableIDs() = { ");
    372         for(i=0; i<numIDs; ++i) {
    373             if (i) buf.append(", ");
    374             buf.append(*ids[i]);
    375         }
    376         buf.append(" } ");
    377         logln(buf + numIDs);
    378         // we own the array; the caller owns the contained strings (yuck)
    379         uprv_free(ids);
    380     }
    381 
    382     numIDs = -1;
    383     ids = TimeZone::createAvailableIDs(-8*U_MILLIS_PER_HOUR, numIDs);
    384     if (ids == 0 || numIDs < 1) {
    385         errln("FAIL: createAvailableIDs(-8:00)");
    386     } else {
    387         UnicodeString buf("TimeZone::createAvailableIDs(-8:00) = { ");
    388         for(i=0; i<numIDs; ++i) {
    389             if (i) buf.append(", ");
    390             buf.append(*ids[i]);
    391         }
    392         buf.append(" } ");
    393         logln(buf + numIDs);
    394         // we own the array; the caller owns the contained strings (yuck)
    395         uprv_free(ids);
    396     }
    397     numIDs = -1;
    398     ids = TimeZone::createAvailableIDs("US", numIDs);
    399     if (ids == 0 || numIDs < 1) {
    400       errln("FAIL: createAvailableIDs(US) ids=%d, numIDs=%d", ids, numIDs);
    401     } else {
    402         UnicodeString buf("TimeZone::createAvailableIDs(US) = { ");
    403         for(i=0; i<numIDs; ++i) {
    404             if (i) buf.append(", ");
    405             buf.append(*ids[i]);
    406         }
    407         buf.append(" } ");
    408         logln(buf + numIDs);
    409         // we own the array; the caller owns the contained strings (yuck)
    410         uprv_free(ids);
    411     }
    412 #endif
    413 
    414     UnicodeString str;
    415     UnicodeString *buf = new UnicodeString("TimeZone::createEnumeration() = { ");
    416     int32_t s_length;
    417     StringEnumeration* s = TimeZone::createEnumeration();
    418     if (s == NULL) {
    419         dataerrln("Unable to create TimeZone enumeration");
    420         return;
    421     }
    422     s_length = s->count(ec);
    423     for (i = 0; i < s_length;++i) {
    424         if (i > 0) *buf += ", ";
    425         if ((i & 1) == 0) {
    426             *buf += *s->snext(ec);
    427         } else {
    428             *buf += UnicodeString(s->next(NULL, ec), "");
    429         }
    430 
    431         if((i % 5) == 4) {
    432             // replace s with a clone of itself
    433             StringEnumeration *s2 = s->clone();
    434             if(s2 == NULL || s_length != s2->count(ec)) {
    435                 errln("TimezoneEnumeration.clone() failed");
    436             } else {
    437                 delete s;
    438                 s = s2;
    439             }
    440         }
    441     }
    442     *buf += " };";
    443     logln(*buf);
    444 
    445     /* Confirm that the following zones can be retrieved: The first
    446      * zone, the last zone, and one in-between.  This tests the binary
    447      * search through the system zone data.
    448      */
    449     s->reset(ec);
    450     int32_t middle = s_length/2;
    451     for (i=0; i<s_length; ++i) {
    452         const UnicodeString* id = s->snext(ec);
    453         if (i==0 || i==middle || i==(s_length-1)) {
    454         TimeZone *z = TimeZone::createTimeZone(*id);
    455         if (z == 0) {
    456             errln(UnicodeString("FAIL: createTimeZone(") +
    457                   *id + ") -> 0");
    458         } else if (z->getID(str) != *id) {
    459             errln(UnicodeString("FAIL: createTimeZone(") +
    460                   *id + ") -> zone " + str);
    461         } else {
    462             logln(UnicodeString("OK: createTimeZone(") +
    463                   *id + ") succeeded");
    464         }
    465         delete z;
    466         }
    467     }
    468     delete s;
    469 
    470     buf->truncate(0);
    471     *buf += "TimeZone::createEnumeration(GMT+01:00) = { ";
    472 
    473     s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR);
    474     s_length = s->count(ec);
    475     for (i = 0; i < s_length;++i) {
    476         if (i > 0) *buf += ", ";
    477         *buf += *s->snext(ec);
    478     }
    479     delete s;
    480     *buf += " };";
    481     logln(*buf);
    482 
    483 
    484     buf->truncate(0);
    485     *buf += "TimeZone::createEnumeration(US) = { ";
    486 
    487     s = TimeZone::createEnumeration("US");
    488     s_length = s->count(ec);
    489     for (i = 0; i < s_length;++i) {
    490         if (i > 0) *buf += ", ";
    491         *buf += *s->snext(ec);
    492     }
    493     *buf += " };";
    494     logln(*buf);
    495 
    496     TimeZone *tz = TimeZone::createTimeZone("PST");
    497     if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
    498     else errln("FAIL: getTimeZone(PST) = null");
    499     delete tz;
    500     tz = TimeZone::createTimeZone("America/Los_Angeles");
    501     if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
    502     else errln("FAIL: getTimeZone(PST) = null");
    503     delete tz;
    504 
    505     // @bug 4096694
    506     tz = TimeZone::createTimeZone("NON_EXISTENT");
    507     UnicodeString temp;
    508     if (tz == 0)
    509         errln("FAIL: getTimeZone(NON_EXISTENT) = null");
    510     else if (tz->getID(temp) != UCAL_UNKNOWN_ZONE_ID)
    511         errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
    512     delete tz;
    513 
    514     delete buf;
    515     delete s;
    516 }
    517 
    518 void
    519 TimeZoneTest::TestGetAvailableIDsNew()
    520 {
    521     UErrorCode ec = U_ZERO_ERROR;
    522     StringEnumeration *any, *canonical, *canonicalLoc;
    523     StringEnumeration *any_US, *canonical_US, *canonicalLoc_US;
    524     StringEnumeration *any_W5, *any_CA_W5;
    525     StringEnumeration *any_US_E14;
    526     int32_t rawOffset;
    527     const UnicodeString *id1, *id2;
    528     UnicodeString canonicalID;
    529     UBool isSystemID;
    530     char region[4];
    531     int32_t zoneCount;
    532 
    533     any = canonical = canonicalLoc = any_US = canonical_US = canonicalLoc_US = any_W5 = any_CA_W5 = any_US_E14 = NULL;
    534 
    535     any = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec);
    536     if (U_FAILURE(ec)) {
    537         dataerrln("Failed to create enumration for ANY");
    538         goto cleanup;
    539     }
    540 
    541     canonical = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, ec);
    542     if (U_FAILURE(ec)) {
    543         errln("Failed to create enumration for CANONICAL");
    544         goto cleanup;
    545     }
    546 
    547     canonicalLoc = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, NULL, ec);
    548     if (U_FAILURE(ec)) {
    549         errln("Failed to create enumration for CANONICALLOC");
    550         goto cleanup;
    551     }
    552 
    553     any_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", NULL, ec);
    554     if (U_FAILURE(ec)) {
    555         errln("Failed to create enumration for ANY_US");
    556         goto cleanup;
    557     }
    558 
    559     canonical_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, "US", NULL, ec);
    560     if (U_FAILURE(ec)) {
    561         errln("Failed to create enumration for CANONICAL_US");
    562         goto cleanup;
    563     }
    564 
    565     canonicalLoc_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "US", NULL, ec);
    566     if (U_FAILURE(ec)) {
    567         errln("Failed to create enumration for CANONICALLOC_US");
    568         goto cleanup;
    569     }
    570 
    571     rawOffset = (-5)*60*60*1000;
    572     any_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec);
    573     if (U_FAILURE(ec)) {
    574         errln("Failed to create enumration for ANY_W5");
    575         goto cleanup;
    576     }
    577 
    578     any_CA_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "CA", &rawOffset, ec);
    579     if (U_FAILURE(ec)) {
    580         errln("Failed to create enumration for ANY_CA_W5");
    581         goto cleanup;
    582     }
    583 
    584     rawOffset = 14*60*60*1000;
    585     any_US_E14 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &rawOffset, ec);
    586     if (U_FAILURE(ec)) {
    587         errln("Failed to create enumration for ANY_US_E14");
    588         goto cleanup;
    589     }
    590 
    591     checkContainsAll(any, "ANY", canonical, "CANONICAL");
    592     checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC");
    593 
    594     checkContainsAll(any, "ANY", any_US, "ANY_US");
    595     checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US");
    596     checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US");
    597 
    598     checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US");
    599     checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US");
    600 
    601     checkContainsAll(any, "ANY", any_W5, "ANY_W5");
    602     checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5");
    603 
    604     // And ID in any set, but not in canonical set must not be a canonical ID
    605     any->reset(ec);
    606     while ((id1 = any->snext(ec)) != NULL) {
    607         UBool found = FALSE;
    608         canonical->reset(ec);
    609         while ((id2 = canonical->snext(ec)) != NULL) {
    610             if (*id1 == *id2) {
    611                 found = TRUE;
    612                 break;
    613             }
    614         }
    615         if (U_FAILURE(ec)) {
    616             break;
    617         }
    618         if (!found) {
    619             TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
    620             if (U_FAILURE(ec)) {
    621                 break;
    622             }
    623             if (*id1 == canonicalID) {
    624                 errln((UnicodeString)"FAIL: canonicalID [" + *id1 + "] is not in CANONICAL");
    625             }
    626             if (!isSystemID) {
    627                 errln((UnicodeString)"FAIL: ANY contains non-system ID: " + *id1);
    628             }
    629         }
    630     }
    631     if (U_FAILURE(ec)) {
    632         errln("Error checking IDs in ANY, but not in CANONICAL");
    633         ec = U_ZERO_ERROR;
    634     }
    635 
    636     // canonical set must contains only canonical IDs
    637     canonical->reset(ec);
    638     while ((id1 = canonical->snext(ec)) != NULL) {
    639         TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec);
    640         if (U_FAILURE(ec)) {
    641             break;
    642         }
    643         if (*id1 != canonicalID) {
    644             errln((UnicodeString)"FAIL: CANONICAL contains non-canonical ID: " + *id1);
    645         }
    646         if (!isSystemID) {
    647             errln((UnicodeString)"FAILE: CANONICAL contains non-system ID: " + *id1);
    648         }
    649     }
    650     if (U_FAILURE(ec)) {
    651         errln("Error checking IDs in CANONICAL");
    652         ec = U_ZERO_ERROR;
    653     }
    654 
    655     // canonicalLoc set must contain only canonical location IDs
    656     canonicalLoc->reset(ec);
    657     while ((id1 = canonicalLoc->snext(ec)) != NULL) {
    658         TimeZone::getRegion(*id1, region, sizeof(region), ec);
    659         if (U_FAILURE(ec)) {
    660             break;
    661         }
    662         if (uprv_strcmp(region, "001") == 0) {
    663             errln((UnicodeString)"FAIL: CANONICALLOC contains non location zone: " + *id1);
    664         }
    665     }
    666     if (U_FAILURE(ec)) {
    667         errln("Error checking IDs in CANONICALLOC");
    668         ec = U_ZERO_ERROR;
    669     }
    670 
    671     // any_US must contain only US zones
    672     any_US->reset(ec);
    673     while ((id1 = any_US->snext(ec)) != NULL) {
    674         TimeZone::getRegion(*id1, region, sizeof(region), ec);
    675         if (U_FAILURE(ec)) {
    676             break;
    677         }
    678         if (uprv_strcmp(region, "US") != 0) {
    679             errln((UnicodeString)"FAIL: ANY_US contains non-US zone ID: " + *id1);
    680         }
    681     }
    682     if (U_FAILURE(ec)) {
    683         errln("Error checking IDs in ANY_US");
    684         ec = U_ZERO_ERROR;
    685     }
    686 
    687     // any_W5 must contain only GMT-05:00 zones
    688     any_W5->reset(ec);
    689     while ((id1 = any_W5->snext(ec)) != NULL) {
    690         TimeZone *tz = TimeZone::createTimeZone(*id1);
    691         if (tz->getRawOffset() != (-5)*60*60*1000) {
    692             errln((UnicodeString)"FAIL: ANY_W5 contains a zone whose offset is not -05:00: " + *id1);
    693         }
    694         delete tz;
    695     }
    696     if (U_FAILURE(ec)) {
    697         errln("Error checking IDs in ANY_W5");
    698         ec = U_ZERO_ERROR;
    699     }
    700 
    701     // No US zone swith GMT+14:00
    702     zoneCount = any_US_E14->count(ec);
    703     if (U_FAILURE(ec)) {
    704         errln("Error checking IDs in ANY_US_E14");
    705         ec = U_ZERO_ERROR;
    706     } else if (zoneCount != 0) {
    707         errln("FAIL: ANY_US_E14 must be empty");
    708     }
    709 
    710 cleanup:
    711     delete any;
    712     delete canonical;
    713     delete canonicalLoc;
    714     delete any_US;
    715     delete canonical_US;
    716     delete canonicalLoc_US;
    717     delete any_W5;
    718     delete any_CA_W5;
    719     delete any_US_E14;
    720 }
    721 
    722 void
    723 TimeZoneTest::checkContainsAll(StringEnumeration *s1, const char *name1,
    724         StringEnumeration *s2, const char *name2)
    725 {
    726     UErrorCode ec = U_ZERO_ERROR;
    727     const UnicodeString *id1, *id2;
    728 
    729     s2->reset(ec);
    730 
    731     while ((id2 = s2->snext(ec)) != NULL) {
    732         UBool found = FALSE;
    733         s1->reset(ec);
    734         while ((id1 = s1->snext(ec)) != NULL) {
    735             if (*id1 == *id2) {
    736                 found = TRUE;
    737                 break;
    738             }
    739         }
    740         if (!found) {
    741             errln((UnicodeString)"FAIL: " + name1 + "does not contain "
    742                 + *id2 + " in " + name2);
    743         }
    744     }
    745 
    746     if (U_FAILURE(ec)) {
    747         errln((UnicodeString)"Error checkContainsAll for " + name1 + " - " + name2);
    748     }
    749 }
    750 
    751 /**
    752  * NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
    753  * file, used to build ICU alias zones, is working.  It also
    754  * looks at some genuine Olson compatibility IDs. [aliu]
    755  *
    756  * This test is problematic. It should really just confirm that
    757  * the list of compatibility zone IDs exist and are somewhat
    758  * meaningful (that is, they aren't all aliases of GMT). It goes a
    759  * bit further -- it hard-codes expectations about zone behavior,
    760  * when in fact zones are redefined quite frequently. ICU's build
    761  * process means that it is easy to update ICU to contain the
    762  * latest Olson zone data, but if a zone tested here changes, then
    763  * this test will fail.  I have updated the test for 1999j data,
    764  * but further updates will probably be required. Note that some
    765  * of the concerts listed below no longer apply -- in particular,
    766  * we do NOT overwrite real UNIX zones with 3-letter IDs. There
    767  * are two points of overlap as of 1999j: MET and EET. These are
    768  * both real UNIX zones, so we just use the official
    769  * definition. This test has been updated to reflect this.
    770  * 12/3/99 aliu
    771  *
    772  * Added tests for additional zones and aliases from the icuzones file.
    773  * Markus Scherer 2006-nov-06
    774  *
    775  * [srl - from java - 7/5/1998]
    776  * @bug 4130885
    777  * Certain short zone IDs, used since 1.1.x, are incorrect.
    778  *
    779  * The worst of these is:
    780  *
    781  * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
    782  * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
    783  * or AZOST, depending on which zone is meant, but in no case is it CAT.
    784  *
    785  * Other wrong zone IDs:
    786  *
    787  * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
    788  * GMT-5:00. European Central time is abbreviated CEST.
    789  *
    790  * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
    791  * GMT-11:00. Solomon Island time is SBT.
    792  *
    793  * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
    794  * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
    795  *
    796  * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
    797  * another bug.] It should be "AKST". AST is Atlantic Standard Time,
    798  * GMT-4:00.
    799  *
    800  * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
    801  * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
    802  * from MST with daylight savings.
    803  *
    804  * In addition to these problems, a number of zones are FAKE. That is, they
    805  * don't match what people use in the real world.
    806  *
    807  * FAKE zones:
    808  *
    809  * EET (should be EEST)
    810  * ART (should be EEST)
    811  * MET (should be IRST)
    812  * NET (should be AMST)
    813  * PLT (should be PKT)
    814  * BST (should be BDT)
    815  * VST (should be ICT)
    816  * CTT (should be CST) +
    817  * ACT (should be CST) +
    818  * AET (should be EST) +
    819  * MIT (should be WST) +
    820  * IET (should be EST) +
    821  * PRT (should be AST) +
    822  * CNT (should be NST)
    823  * AGT (should be ARST)
    824  * BET (should be EST) +
    825  *
    826  * + A zone with the correct name already exists and means something
    827  * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
    828  * used for Brazil (BET).
    829  */
    830 void TimeZoneTest::TestShortZoneIDs()
    831 {
    832     int32_t i;
    833     // Create a small struct to hold the array
    834     struct
    835     {
    836         const char *id;
    837         int32_t    offset;
    838         UBool      daylight;
    839     }
    840     kReferenceList [] =
    841     {
    842         {"HST", -600, FALSE}, // Olson northamerica -10:00
    843         {"AST", -540, TRUE},  // ICU Link - America/Anchorage
    844         {"PST", -480, TRUE},  // ICU Link - America/Los_Angeles
    845         {"PNT", -420, FALSE}, // ICU Link - America/Phoenix
    846         {"MST", -420, FALSE}, // updated Aug 2003 aliu
    847         {"CST", -360, TRUE},  // Olson northamerica -7:00
    848         {"IET", -300, TRUE},  // ICU Link - America/Indiana/Indianapolis
    849         {"EST", -300, FALSE}, // Olson northamerica -5:00
    850         {"PRT", -240, FALSE}, // ICU Link - America/Puerto_Rico
    851         {"CNT", -210, TRUE},  // ICU Link - America/St_Johns
    852         {"AGT", -180, FALSE}, // ICU Link - America/Argentina/Buenos_Aires
    853         {"BET", -180, TRUE},  // ICU Link - America/Sao_Paulo
    854         {"GMT", 0, FALSE},    // Olson etcetera Link - Etc/GMT
    855         {"UTC", 0, FALSE},    // Olson etcetera 0
    856         {"ECT", 60, TRUE},    // ICU Link - Europe/Paris
    857         {"MET", 60, TRUE},    // Olson europe 1:00 C-Eur
    858         {"CAT", 120, FALSE},  // ICU Link - Africa/Harare
    859         {"ART", 120, FALSE},  // ICU Link - Africa/Cairo
    860         {"EET", 120, TRUE},   // Olson europe 2:00 EU
    861         {"EAT", 180, FALSE},  // ICU Link - Africa/Addis_Ababa
    862         {"NET", 240, FALSE},  // ICU Link - Asia/Yerevan
    863         {"PLT", 300, FALSE},  // ICU Link - Asia/Karachi
    864         {"IST", 330, FALSE},  // ICU Link - Asia/Kolkata
    865         {"BST", 360, FALSE},  // ICU Link - Asia/Dhaka
    866         {"VST", 420, FALSE},  // ICU Link - Asia/Ho_Chi_Minh
    867         {"CTT", 480, FALSE},  // ICU Link - Asia/Shanghai
    868         {"JST", 540, FALSE},  // ICU Link - Asia/Tokyo
    869         {"ACT", 570, FALSE},  // ICU Link - Australia/Darwin
    870         {"AET", 600, TRUE},   // ICU Link - Australia/Sydney
    871         {"SST", 660, FALSE},  // ICU Link - Pacific/Guadalcanal
    872         {"NST", 720, TRUE},   // ICU Link - Pacific/Auckland
    873         {"MIT", 780, TRUE},   // ICU Link - Pacific/Apia
    874 
    875         {"Etc/Unknown", 0, FALSE},  // CLDR
    876 
    877         {"SystemV/AST4ADT", -240, TRUE},
    878         {"SystemV/EST5EDT", -300, TRUE},
    879         {"SystemV/CST6CDT", -360, TRUE},
    880         {"SystemV/MST7MDT", -420, TRUE},
    881         {"SystemV/PST8PDT", -480, TRUE},
    882         {"SystemV/YST9YDT", -540, TRUE},
    883         {"SystemV/AST4", -240, FALSE},
    884         {"SystemV/EST5", -300, FALSE},
    885         {"SystemV/CST6", -360, FALSE},
    886         {"SystemV/MST7", -420, FALSE},
    887         {"SystemV/PST8", -480, FALSE},
    888         {"SystemV/YST9", -540, FALSE},
    889         {"SystemV/HST10", -600, FALSE},
    890 
    891         {"",0,FALSE}
    892     };
    893 
    894     for(i=0;kReferenceList[i].id[0];i++) {
    895         UnicodeString itsID(kReferenceList[i].id);
    896         UBool ok = TRUE;
    897         // Check existence.
    898         TimeZone *tz = TimeZone::createTimeZone(itsID);
    899         if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) {
    900             errln("FAIL: Time Zone " + itsID + " does not exist!");
    901             continue;
    902         }
    903 
    904         // Check daylight usage.
    905         UBool usesDaylight = tz->useDaylightTime();
    906         if (usesDaylight != kReferenceList[i].daylight) {
    907             if (!isDevelopmentBuild) {
    908                 logln("Warning: Time Zone " + itsID + " use daylight is " +
    909                       (usesDaylight?"TRUE":"FALSE") +
    910                       " but it should be " +
    911                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
    912             } else {
    913                 dataerrln("FAIL: Time Zone " + itsID + " use daylight is " +
    914                       (usesDaylight?"TRUE":"FALSE") +
    915                       " but it should be " +
    916                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
    917             }
    918             ok = FALSE;
    919         }
    920 
    921         // Check offset
    922         int32_t offsetInMinutes = tz->getRawOffset()/60000;
    923         if (offsetInMinutes != kReferenceList[i].offset) {
    924             if (!isDevelopmentBuild) {
    925                 logln("FAIL: Time Zone " + itsID + " raw offset is " +
    926                       offsetInMinutes +
    927                       " but it should be " + kReferenceList[i].offset);
    928             } else {
    929                 dataerrln("FAIL: Time Zone " + itsID + " raw offset is " +
    930                       offsetInMinutes +
    931                       " but it should be " + kReferenceList[i].offset);
    932             }
    933             ok = FALSE;
    934         }
    935 
    936         if (ok) {
    937             logln("OK: " + itsID +
    938                   " useDaylightTime() & getRawOffset() as expected");
    939         }
    940         delete tz;
    941     }
    942 
    943 
    944     // OK now test compat
    945     logln("Testing for compatibility zones");
    946 
    947     const char* compatibilityMap[] = {
    948         // This list is copied from tz.alias.  If tz.alias
    949         // changes, this list must be updated.  Current as of Mar 2007
    950         "ACT", "Australia/Darwin",
    951         "AET", "Australia/Sydney",
    952         "AGT", "America/Buenos_Aires",
    953         "ART", "Africa/Cairo",
    954         "AST", "America/Anchorage",
    955         "BET", "America/Sao_Paulo",
    956         "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca
    957         "CAT", "Africa/Harare",
    958         "CNT", "America/St_Johns",
    959         "CST", "America/Chicago",
    960         "CTT", "Asia/Shanghai",
    961         "EAT", "Africa/Addis_Ababa",
    962         "ECT", "Europe/Paris",
    963         // EET Europe/Istanbul # EET is a standard UNIX zone
    964         // "EST", "America/New_York", # Defined as -05:00
    965         // "HST", "Pacific/Honolulu", # Defined as -10:00
    966         "IET", "America/Indianapolis",
    967         "IST", "Asia/Calcutta",
    968         "JST", "Asia/Tokyo",
    969         // MET Asia/Tehran # MET is a standard UNIX zone
    970         "MIT", "Pacific/Apia",
    971         // "MST", "America/Denver", # Defined as -07:00
    972         "NET", "Asia/Yerevan",
    973         "NST", "Pacific/Auckland",
    974         "PLT", "Asia/Karachi",
    975         "PNT", "America/Phoenix",
    976         "PRT", "America/Puerto_Rico",
    977         "PST", "America/Los_Angeles",
    978         "SST", "Pacific/Guadalcanal",
    979         "UTC", "Etc/GMT",
    980         "VST", "Asia/Saigon",
    981          "","",""
    982     };
    983 
    984     for (i=0;*compatibilityMap[i];i+=2) {
    985         UnicodeString itsID;
    986 
    987         const char *zone1 = compatibilityMap[i];
    988         const char *zone2 = compatibilityMap[i+1];
    989 
    990         TimeZone *tz1 = TimeZone::createTimeZone(zone1);
    991         TimeZone *tz2 = TimeZone::createTimeZone(zone2);
    992 
    993         if (!tz1) {
    994             errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1);
    995         }
    996         if (!tz2) {
    997             errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2);
    998         }
    999 
   1000         if (tz1 && tz2) {
   1001             // make NAME same so comparison will only look at the rest
   1002             tz2->setID(tz1->getID(itsID));
   1003 
   1004             if (*tz1 != *tz2) {
   1005                 errln("FAIL: " + UnicodeString(zone1) +
   1006                       " != " + UnicodeString(zone2));
   1007             } else {
   1008                 logln("OK: " + UnicodeString(zone1) +
   1009                       " == " + UnicodeString(zone2));
   1010             }
   1011         }
   1012 
   1013         delete tz1;
   1014         delete tz2;
   1015     }
   1016 }
   1017 
   1018 
   1019 /**
   1020  * Utility function for TestCustomParse
   1021  */
   1022 UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
   1023     rv.remove();
   1024     UChar sign = 0x002B;
   1025     if (offset < 0) {
   1026         sign = 0x002D;
   1027         offset = -offset;
   1028     }
   1029 
   1030     int32_t s = offset % 60;
   1031     offset /= 60;
   1032     int32_t m = offset % 60;
   1033     int32_t h = offset / 60;
   1034 
   1035     rv += (UChar)(sign);
   1036     if (h >= 10) {
   1037         rv += (UChar)(0x0030 + (h/10));
   1038     } else {
   1039         rv += (UChar)0x0030;
   1040     }
   1041     rv += (UChar)(0x0030 + (h%10));
   1042 
   1043     rv += (UChar)0x003A; /* ':' */
   1044     if (m >= 10) {
   1045         rv += (UChar)(0x0030 + (m/10));
   1046     } else {
   1047         rv += (UChar)0x0030;
   1048     }
   1049     rv += (UChar)(0x0030 + (m%10));
   1050 
   1051     if (s) {
   1052         rv += (UChar)0x003A; /* ':' */
   1053         if (s >= 10) {
   1054             rv += (UChar)(0x0030 + (s/10));
   1055         } else {
   1056             rv += (UChar)0x0030;
   1057         }
   1058         rv += (UChar)(0x0030 + (s%10));
   1059     }
   1060     return rv;
   1061 }
   1062 
   1063 /**
   1064  * Utility function for TestCustomParse, generating time zone ID
   1065  * string for the give offset.
   1066  */
   1067 UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
   1068     rv.remove();
   1069     UChar sign = 0x002B;
   1070     if (offset < 0) {
   1071         sign = 0x002D;
   1072         offset = -offset;
   1073     }
   1074 
   1075     int32_t s = offset % 60;
   1076     offset /= 60;
   1077     int32_t m = offset % 60;
   1078     int32_t h = offset / 60;
   1079 
   1080     rv += "GMT";
   1081     rv += (UChar)(sign);
   1082     if (h >= 10) {
   1083         rv += (UChar)(0x0030 + (h/10));
   1084     } else {
   1085         rv += (UChar)0x0030;
   1086     }
   1087     rv += (UChar)(0x0030 + (h%10));
   1088     rv += (UChar)0x003A;
   1089     if (m >= 10) {
   1090         rv += (UChar)(0x0030 + (m/10));
   1091     } else {
   1092         rv += (UChar)0x0030;
   1093     }
   1094     rv += (UChar)(0x0030 + (m%10));
   1095 
   1096     if (s) {
   1097         rv += (UChar)0x003A;
   1098         if (s >= 10) {
   1099             rv += (UChar)(0x0030 + (s/10));
   1100         } else {
   1101             rv += (UChar)0x0030;
   1102         }
   1103         rv += (UChar)(0x0030 + (s%10));
   1104     }
   1105     return rv;
   1106 }
   1107 
   1108 /**
   1109  * As part of the VM fix (see CCC approved RFE 4028006, bug
   1110  * 4044013), TimeZone.getTimeZone() has been modified to recognize
   1111  * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
   1112  * GMT[+-]hh.  Test this behavior here.
   1113  *
   1114  * @bug 4044013
   1115  */
   1116 void TimeZoneTest::TestCustomParse()
   1117 {
   1118     int32_t i;
   1119     const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
   1120 
   1121     struct
   1122     {
   1123         const char *customId;
   1124         int32_t expectedOffset;
   1125     }
   1126     kData[] =
   1127     {
   1128         // ID        Expected offset in seconds
   1129         {"GMT",       kUnparseable},   //Isn't custom. [returns normal GMT]
   1130         {"GMT-YOUR.AD.HERE", kUnparseable},
   1131         {"GMT0",      kUnparseable},
   1132         {"GMT+0",     (0)},
   1133         {"GMT+1",     (1*60*60)},
   1134         {"GMT-0030",  (-30*60)},
   1135         {"GMT+15:99", kUnparseable},
   1136         {"GMT+",      kUnparseable},
   1137         {"GMT-",      kUnparseable},
   1138         {"GMT+0:",    kUnparseable},
   1139         {"GMT-:",     kUnparseable},
   1140         {"GMT-YOUR.AD.HERE",    kUnparseable},
   1141         {"GMT+0010",  (10*60)}, // Interpret this as 00:10
   1142         {"GMT-10",    (-10*60*60)},
   1143         {"GMT+30",    kUnparseable},
   1144         {"GMT-3:30",  (-(3*60+30)*60)},
   1145         {"GMT-230",   (-(2*60+30)*60)},
   1146         {"GMT+05:13:05",    ((5*60+13)*60+5)},
   1147         {"GMT-71023",       (-((7*60+10)*60+23))},
   1148         {"GMT+01:23:45:67", kUnparseable},
   1149         {"GMT+01:234",      kUnparseable},
   1150         {"GMT-2:31:123",    kUnparseable},
   1151         {"GMT+3:75",        kUnparseable},
   1152         {"GMT-01010101",    kUnparseable},
   1153         {0,           0}
   1154     };
   1155 
   1156     for (i=0; kData[i].customId != 0; i++) {
   1157         UnicodeString id(kData[i].customId);
   1158         int32_t exp = kData[i].expectedOffset;
   1159         TimeZone *zone = TimeZone::createTimeZone(id);
   1160         UnicodeString   itsID, temp;
   1161 
   1162         if (dynamic_cast<OlsonTimeZone *>(zone) != NULL) {
   1163             logln(id + " -> Olson time zone");
   1164         } else {
   1165             zone->getID(itsID);
   1166             int32_t ioffset = zone->getRawOffset()/1000;
   1167             UnicodeString offset, expectedID;
   1168             formatOffset(ioffset, offset);
   1169             formatTZID(ioffset, expectedID);
   1170             logln(id + " -> " + itsID + " " + offset);
   1171             if (exp == kUnparseable && itsID != UCAL_UNKNOWN_ZONE_ID) {
   1172                 errln("Expected parse failure for " + id +
   1173                       ", got offset of " + offset +
   1174                       ", id " + itsID);
   1175             }
   1176             // JDK 1.3 creates custom zones with the ID "Custom"
   1177             // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
   1178             // ICU creates custom zones with IDs of the form "GMT+02:00"
   1179             else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
   1180                 dataerrln("Expected offset of " + formatOffset(exp, temp) +
   1181                       ", id " + expectedID +
   1182                       ", for " + id +
   1183                       ", got offset of " + offset +
   1184                       ", id " + itsID);
   1185             }
   1186         }
   1187         delete zone;
   1188     }
   1189 }
   1190 
   1191 void
   1192 TimeZoneTest::TestAliasedNames()
   1193 {
   1194     struct {
   1195         const char *from;
   1196         const char *to;
   1197     } kData[] = {
   1198         /* Generated by org.unicode.cldr.tool.CountItems */
   1199 
   1200         /* zoneID, canonical zoneID */
   1201         {"Africa/Timbuktu", "Africa/Bamako"},
   1202         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"},
   1203         {"America/Argentina/Catamarca", "America/Catamarca"},
   1204         {"America/Argentina/ComodRivadavia", "America/Catamarca"},
   1205         {"America/Argentina/Cordoba", "America/Cordoba"},
   1206         {"America/Argentina/Jujuy", "America/Jujuy"},
   1207         {"America/Argentina/Mendoza", "America/Mendoza"},
   1208         {"America/Atka", "America/Adak"},
   1209         {"America/Ensenada", "America/Tijuana"},
   1210         {"America/Fort_Wayne", "America/Indiana/Indianapolis"},
   1211         {"America/Indianapolis", "America/Indiana/Indianapolis"},
   1212         {"America/Knox_IN", "America/Indiana/Knox"},
   1213         {"America/Louisville", "America/Kentucky/Louisville"},
   1214         {"America/Porto_Acre", "America/Rio_Branco"},
   1215         {"America/Rosario", "America/Cordoba"},
   1216         {"America/Virgin", "America/St_Thomas"},
   1217         {"Asia/Ashkhabad", "Asia/Ashgabat"},
   1218         {"Asia/Chungking", "Asia/Chongqing"},
   1219         {"Asia/Dacca", "Asia/Dhaka"},
   1220         {"Asia/Istanbul", "Europe/Istanbul"},
   1221         {"Asia/Macao", "Asia/Macau"},
   1222         {"Asia/Tel_Aviv", "Asia/Jerusalem"},
   1223         {"Asia/Thimbu", "Asia/Thimphu"},
   1224         {"Asia/Ujung_Pandang", "Asia/Makassar"},
   1225         {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"},
   1226         {"Australia/ACT", "Australia/Sydney"},
   1227         {"Australia/Canberra", "Australia/Sydney"},
   1228         {"Australia/LHI", "Australia/Lord_Howe"},
   1229         {"Australia/NSW", "Australia/Sydney"},
   1230         {"Australia/North", "Australia/Darwin"},
   1231         {"Australia/Queensland", "Australia/Brisbane"},
   1232         {"Australia/South", "Australia/Adelaide"},
   1233         {"Australia/Tasmania", "Australia/Hobart"},
   1234         {"Australia/Victoria", "Australia/Melbourne"},
   1235         {"Australia/West", "Australia/Perth"},
   1236         {"Australia/Yancowinna", "Australia/Broken_Hill"},
   1237         {"Brazil/Acre", "America/Rio_Branco"},
   1238         {"Brazil/DeNoronha", "America/Noronha"},
   1239         {"Brazil/East", "America/Sao_Paulo"},
   1240         {"Brazil/West", "America/Manaus"},
   1241         {"Canada/Atlantic", "America/Halifax"},
   1242         {"Canada/Central", "America/Winnipeg"},
   1243         {"Canada/East-Saskatchewan", "America/Regina"},
   1244         {"Canada/Eastern", "America/Toronto"},
   1245         {"Canada/Mountain", "America/Edmonton"},
   1246         {"Canada/Newfoundland", "America/St_Johns"},
   1247         {"Canada/Pacific", "America/Vancouver"},
   1248         {"Canada/Saskatchewan", "America/Regina"},
   1249         {"Canada/Yukon", "America/Whitehorse"},
   1250         {"Chile/Continental", "America/Santiago"},
   1251         {"Chile/EasterIsland", "Pacific/Easter"},
   1252         {"Cuba", "America/Havana"},
   1253         {"Egypt", "Africa/Cairo"},
   1254         {"Eire", "Europe/Dublin"},
   1255         {"Etc/GMT+0", "Etc/GMT"},
   1256         {"Etc/GMT-0", "Etc/GMT"},
   1257         {"Etc/GMT0", "Etc/GMT"},
   1258         {"Etc/Greenwich", "Etc/GMT"},
   1259         {"Etc/UCT", "Etc/GMT"},
   1260         {"Etc/UTC", "Etc/GMT"},
   1261         {"Etc/Universal", "Etc/GMT"},
   1262         {"Etc/Zulu", "Etc/GMT"},
   1263         {"Europe/Belfast", "Europe/London"},
   1264         {"Europe/Nicosia", "Asia/Nicosia"},
   1265         {"Europe/Tiraspol", "Europe/Chisinau"},
   1266         {"GB", "Europe/London"},
   1267         {"GB-Eire", "Europe/London"},
   1268         {"GMT", "Etc/GMT"},
   1269         {"GMT+0", "Etc/GMT"},
   1270         {"GMT-0", "Etc/GMT"},
   1271         {"GMT0", "Etc/GMT"},
   1272         {"Greenwich", "Etc/GMT"},
   1273         {"Hongkong", "Asia/Hong_Kong"},
   1274         {"Iceland", "Atlantic/Reykjavik"},
   1275         {"Iran", "Asia/Tehran"},
   1276         {"Israel", "Asia/Jerusalem"},
   1277         {"Jamaica", "America/Jamaica"},
   1278         {"Japan", "Asia/Tokyo"},
   1279         {"Kwajalein", "Pacific/Kwajalein"},
   1280         {"Libya", "Africa/Tripoli"},
   1281         {"Mexico/BajaNorte", "America/Tijuana"},
   1282         {"Mexico/BajaSur", "America/Mazatlan"},
   1283         {"Mexico/General", "America/Mexico_City"},
   1284         {"NZ", "Pacific/Auckland"},
   1285         {"NZ-CHAT", "Pacific/Chatham"},
   1286         {"Navajo", "America/Shiprock"},
   1287         {"PRC", "Asia/Shanghai"},
   1288         {"Pacific/Samoa", "Pacific/Pago_Pago"},
   1289         {"Pacific/Yap", "Pacific/Truk"},
   1290         {"Poland", "Europe/Warsaw"},
   1291         {"Portugal", "Europe/Lisbon"},
   1292         {"ROC", "Asia/Taipei"},
   1293         {"ROK", "Asia/Seoul"},
   1294         {"Singapore", "Asia/Singapore"},
   1295         {"Turkey", "Europe/Istanbul"},
   1296         {"UCT", "Etc/GMT"},
   1297         {"US/Alaska", "America/Anchorage"},
   1298         {"US/Aleutian", "America/Adak"},
   1299         {"US/Arizona", "America/Phoenix"},
   1300         {"US/Central", "America/Chicago"},
   1301         {"US/East-Indiana", "America/Indiana/Indianapolis"},
   1302         {"US/Eastern", "America/New_York"},
   1303         {"US/Hawaii", "Pacific/Honolulu"},
   1304         {"US/Indiana-Starke", "America/Indiana/Knox"},
   1305         {"US/Michigan", "America/Detroit"},
   1306         {"US/Mountain", "America/Denver"},
   1307         {"US/Pacific", "America/Los_Angeles"},
   1308         {"US/Pacific-New", "America/Los_Angeles"},
   1309         {"US/Samoa", "Pacific/Pago_Pago"},
   1310         {"UTC", "Etc/GMT"},
   1311         {"Universal", "Etc/GMT"},
   1312         {"W-SU", "Europe/Moscow"},
   1313         {"Zulu", "Etc/GMT"},
   1314         /* Total: 113 */
   1315 
   1316     };
   1317 
   1318     TimeZone::EDisplayType styles[] = { TimeZone::SHORT, TimeZone::LONG };
   1319     UBool useDst[] = { FALSE, TRUE };
   1320     int32_t noLoc = uloc_countAvailable();
   1321 
   1322     int32_t i, j, k, loc;
   1323     UnicodeString fromName, toName;
   1324     TimeZone *from = NULL, *to = NULL;
   1325     for(i = 0; i < (int32_t)(sizeof(kData)/sizeof(kData[0])); i++) {
   1326         from = TimeZone::createTimeZone(kData[i].from);
   1327         to = TimeZone::createTimeZone(kData[i].to);
   1328         if(!from->hasSameRules(*to)) {
   1329             errln("different at %i\n", i);
   1330         }
   1331         if(!quick) {
   1332             for(loc = 0; loc < noLoc; loc++) {
   1333                 const char* locale = uloc_getAvailable(loc);
   1334                 for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {
   1335                     for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); k++) {
   1336                         fromName.remove();
   1337                         toName.remove();
   1338                         from->getDisplayName(useDst[k], styles[j],locale, fromName);
   1339                         to->getDisplayName(useDst[k], styles[j], locale, toName);
   1340                         if(fromName.compare(toName) != 0) {
   1341                             errln("Fail: Expected "+toName+" but got " + prettify(fromName)
   1342                                 + " for locale: " + locale + " index: "+ loc
   1343                                 + " to id "+ kData[i].to
   1344                                 + " from id " + kData[i].from);
   1345                         }
   1346                     }
   1347                 }
   1348             }
   1349         } else {
   1350             fromName.remove();
   1351             toName.remove();
   1352             from->getDisplayName(fromName);
   1353             to->getDisplayName(toName);
   1354             if(fromName.compare(toName) != 0) {
   1355                 errln("Fail: Expected "+toName+" but got " + fromName);
   1356             }
   1357         }
   1358         delete from;
   1359         delete to;
   1360     }
   1361 }
   1362 
   1363 /**
   1364  * Test the basic functionality of the getDisplayName() API.
   1365  *
   1366  * @bug 4112869
   1367  * @bug 4028006
   1368  *
   1369  * See also API change request A41.
   1370  *
   1371  * 4/21/98 - make smarter, so the test works if the ext resources
   1372  * are present or not.
   1373  */
   1374 void
   1375 TimeZoneTest::TestDisplayName()
   1376 {
   1377     UErrorCode status = U_ZERO_ERROR;
   1378     int32_t i;
   1379     TimeZone *zone = TimeZone::createTimeZone("PST");
   1380     UnicodeString name;
   1381     zone->getDisplayName(Locale::getEnglish(), name);
   1382     logln("PST->" + name);
   1383     if (name.compare("Pacific Standard Time") != 0)
   1384         dataerrln("Fail: Expected \"Pacific Standard Time\" but got " + name);
   1385 
   1386     //*****************************************************************
   1387     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1388     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1389     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1390     //*****************************************************************
   1391     struct
   1392     {
   1393         UBool useDst;
   1394         TimeZone::EDisplayType style;
   1395         const char *expect;
   1396     } kData[] = {
   1397         {FALSE, TimeZone::SHORT, "PST"},
   1398         {TRUE,  TimeZone::SHORT, "PDT"},
   1399         {FALSE, TimeZone::LONG,  "Pacific Standard Time"},
   1400         {TRUE,  TimeZone::LONG,  "Pacific Daylight Time"},
   1401 
   1402         {FALSE, TimeZone::SHORT_GENERIC, "PT"},
   1403         {TRUE,  TimeZone::SHORT_GENERIC, "PT"},
   1404         {FALSE, TimeZone::LONG_GENERIC,  "Pacific Time"},
   1405         {TRUE,  TimeZone::LONG_GENERIC,  "Pacific Time"},
   1406 
   1407         {FALSE, TimeZone::SHORT_GMT, "-0800"},
   1408         {TRUE,  TimeZone::SHORT_GMT, "-0700"},
   1409         {FALSE, TimeZone::LONG_GMT,  "GMT-08:00"},
   1410         {TRUE,  TimeZone::LONG_GMT,  "GMT-07:00"},
   1411 
   1412         {FALSE, TimeZone::SHORT_COMMONLY_USED, "PST"},
   1413         {TRUE,  TimeZone::SHORT_COMMONLY_USED, "PDT"},
   1414         {FALSE, TimeZone::GENERIC_LOCATION,  "Los Angeles Time"},
   1415         {TRUE,  TimeZone::GENERIC_LOCATION,  "Los Angeles Time"},
   1416 
   1417         {FALSE, TimeZone::LONG, ""}
   1418     };
   1419 
   1420     for (i=0; kData[i].expect[0] != '\0'; i++)
   1421     {
   1422         name.remove();
   1423         name = zone->getDisplayName(kData[i].useDst,
   1424                                    kData[i].style,
   1425                                    Locale::getEnglish(), name);
   1426         if (name.compare(kData[i].expect) != 0)
   1427             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
   1428         logln("PST [with options]->" + name);
   1429     }
   1430     for (i=0; kData[i].expect[0] != '\0'; i++)
   1431     {
   1432         name.remove();
   1433         name = zone->getDisplayName(kData[i].useDst,
   1434                                    kData[i].style, name);
   1435         if (name.compare(kData[i].expect) != 0)
   1436             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
   1437         logln("PST [with options]->" + name);
   1438     }
   1439 
   1440 
   1441     // Make sure that we don't display the DST name by constructing a fake
   1442     // PST zone that has DST all year long.
   1443     SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST");
   1444 
   1445     zone2->setStartRule(UCAL_JANUARY, 1, 0, 0, status);
   1446     zone2->setEndRule(UCAL_DECEMBER, 31, 0, 0, status);
   1447 
   1448     UnicodeString inDaylight;
   1449     if (zone2->inDaylightTime(UDate(0), status)) {
   1450         inDaylight = UnicodeString("TRUE");
   1451     } else {
   1452         inDaylight = UnicodeString("FALSE");
   1453     }
   1454     logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight );
   1455     if(U_FAILURE(status))
   1456     {
   1457         dataerrln("Some sort of error..." + UnicodeString(u_errorName(status))); // REVISIT
   1458     }
   1459     name.remove();
   1460     name = zone2->getDisplayName(Locale::getEnglish(),name);
   1461     logln("Modified PST->" + name);
   1462     if (name.compare("Pacific Standard Time") != 0)
   1463         dataerrln("Fail: Expected \"Pacific Standard Time\"");
   1464 
   1465     // Make sure we get the default display format for Locales
   1466     // with no display name data.
   1467     Locale mt_MT("mt_MT");
   1468     name.remove();
   1469     name = zone->getDisplayName(mt_MT,name);
   1470     //*****************************************************************
   1471     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1472     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1473     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1474     //*****************************************************************
   1475     logln("PST(mt_MT)->" + name);
   1476 
   1477     // *** REVISIT SRL how in the world do I check this? looks java specific.
   1478     // Now be smart -- check to see if zh resource is even present.
   1479     // If not, we expect the en fallback behavior.
   1480     ResourceBundle enRB(NULL,
   1481                             Locale::getEnglish(), status);
   1482     if(U_FAILURE(status))
   1483         dataerrln("Couldn't get ResourceBundle for en - %s", u_errorName(status));
   1484 
   1485     ResourceBundle mtRB(NULL,
   1486                          mt_MT, status);
   1487     //if(U_FAILURE(status))
   1488     //    errln("Couldn't get ResourceBundle for mt_MT");
   1489 
   1490     UBool noZH = U_FAILURE(status);
   1491 
   1492     if (noZH) {
   1493         logln("Warning: Not testing the mt_MT behavior because resource is absent");
   1494         if (name != "Pacific Standard Time")
   1495             dataerrln("Fail: Expected Pacific Standard Time");
   1496     }
   1497 
   1498 
   1499     if      (name.compare("GMT-08:00") &&
   1500              name.compare("GMT-8:00") &&
   1501              name.compare("GMT-0800") &&
   1502              name.compare("GMT-800")) {
   1503       dataerrln(UnicodeString("Fail: Expected GMT-08:00 or something similar for PST in mt_MT but got ") + name );
   1504         dataerrln("************************************************************");
   1505         dataerrln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
   1506         dataerrln("************************************************************");
   1507     }
   1508 
   1509     // Now try a non-existent zone
   1510     delete zone2;
   1511     zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
   1512     name.remove();
   1513     name = zone2->getDisplayName(Locale::getEnglish(),name);
   1514     logln("GMT+90min->" + name);
   1515     if (name.compare("GMT+01:30") &&
   1516         name.compare("GMT+1:30") &&
   1517         name.compare("GMT+0130") &&
   1518         name.compare("GMT+130"))
   1519         dataerrln("Fail: Expected GMT+01:30 or something similar");
   1520     name.truncate(0);
   1521     zone2->getDisplayName(name);
   1522     logln("GMT+90min->" + name);
   1523     if (name.compare("GMT+01:30") &&
   1524         name.compare("GMT+1:30") &&
   1525         name.compare("GMT+0130") &&
   1526         name.compare("GMT+130"))
   1527         dataerrln("Fail: Expected GMT+01:30 or something similar");
   1528     // clean up
   1529     delete zone;
   1530     delete zone2;
   1531 }
   1532 
   1533 /**
   1534  * @bug 4107276
   1535  */
   1536 void
   1537 TimeZoneTest::TestDSTSavings()
   1538 {
   1539     UErrorCode status = U_ZERO_ERROR;
   1540     // It might be better to find a way to integrate this test into the main TimeZone
   1541     // tests above, but I don't have time to figure out how to do this (or if it's
   1542     // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
   1543     SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest",
   1544                                            UCAL_MARCH, 1, 0, 0, UCAL_SEPTEMBER, 1, 0, 0,
   1545                                            (int32_t)(0.5 * U_MILLIS_PER_HOUR), status);
   1546     if(U_FAILURE(status))
   1547         errln("couldn't create TimeZone");
   1548 
   1549     if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR)
   1550         errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) +
   1551               " hours instead of -5 hours.");
   1552     if (!tz->useDaylightTime())
   1553         errln("Test time zone should use DST but claims it doesn't.");
   1554     if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR)
   1555         errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() /
   1556                                                              U_MILLIS_PER_HOUR) + " hours instead.");
   1557 
   1558     int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
   1559                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1560     if (offset != -5 * U_MILLIS_PER_HOUR)
   1561         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
   1562               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1563 
   1564     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
   1565                           10 * U_MILLIS_PER_HOUR,status);
   1566     if (offset != -4.5 * U_MILLIS_PER_HOUR)
   1567         errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ")
   1568               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1569 
   1570     tz->setDSTSavings(U_MILLIS_PER_HOUR, status);
   1571     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
   1572                           UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1573     if (offset != -5 * U_MILLIS_PER_HOUR)
   1574         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
   1575               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1576 
   1577     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
   1578                           10 * U_MILLIS_PER_HOUR,status);
   1579     if (offset != -4 * U_MILLIS_PER_HOUR)
   1580         errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ")
   1581               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1582 
   1583     delete tz;
   1584 }
   1585 
   1586 /**
   1587  * @bug 4107570
   1588  */
   1589 void
   1590 TimeZoneTest::TestAlternateRules()
   1591 {
   1592     // Like TestDSTSavings, this test should probably be integrated somehow with the main
   1593     // test at the top of this class, but I didn't have time to figure out how to do that.
   1594     //                      --rtg 1/28/98
   1595 
   1596     SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest");
   1597 
   1598     // test the day-of-month API
   1599     UErrorCode status = U_ZERO_ERROR;
   1600     tz.setStartRule(UCAL_MARCH, 10, 12 * U_MILLIS_PER_HOUR, status);
   1601     if(U_FAILURE(status))
   1602         errln("tz.setStartRule failed");
   1603     tz.setEndRule(UCAL_OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status);
   1604     if(U_FAILURE(status))
   1605         errln("tz.setStartRule failed");
   1606 
   1607     int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 5,
   1608                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1609     if (offset != -5 * U_MILLIS_PER_HOUR)
   1610         errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ")
   1611               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1612 
   1613     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 15,
   1614                           UCAL_SUNDAY, 10 * millisPerHour,status);
   1615     if (offset != -4 * U_MILLIS_PER_HOUR)
   1616         errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ")
   1617               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1618 
   1619     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
   1620                           UCAL_THURSDAY, 10 * millisPerHour,status);
   1621     if (offset != -4 * U_MILLIS_PER_HOUR)
   1622         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")              + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1623 
   1624     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 25,
   1625                           UCAL_SUNDAY, 10 * millisPerHour,status);
   1626     if (offset != -5 * U_MILLIS_PER_HOUR)
   1627         errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ")
   1628               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1629 
   1630     // test the day-of-week-after-day-in-month API
   1631     tz.setStartRule(UCAL_MARCH, 10, UCAL_FRIDAY, 12 * millisPerHour, TRUE, status);
   1632     if(U_FAILURE(status))
   1633         errln("tz.setStartRule failed");
   1634     tz.setEndRule(UCAL_OCTOBER, 20, UCAL_FRIDAY, 12 * millisPerHour, FALSE, status);
   1635     if(U_FAILURE(status))
   1636         errln("tz.setStartRule failed");
   1637 
   1638     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 11,
   1639                           UCAL_WEDNESDAY, 10 * millisPerHour,status);
   1640     if (offset != -5 * U_MILLIS_PER_HOUR)
   1641         errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ")
   1642               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1643 
   1644     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 14,
   1645                           UCAL_SATURDAY, 10 * millisPerHour,status);
   1646     if (offset != -4 * U_MILLIS_PER_HOUR)
   1647         errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ")
   1648               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1649 
   1650     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
   1651                           UCAL_THURSDAY, 10 * millisPerHour,status);
   1652     if (offset != -4 * U_MILLIS_PER_HOUR)
   1653         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")
   1654               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1655 
   1656     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 17,
   1657                           UCAL_SATURDAY, 10 * millisPerHour,status);
   1658     if (offset != -5 * U_MILLIS_PER_HOUR)
   1659         errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ")
   1660               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1661 }
   1662 
   1663 void TimeZoneTest::TestFractionalDST() {
   1664     const char* tzName = "Australia/Lord_Howe"; // 30 min offset
   1665     TimeZone* tz_icu = TimeZone::createTimeZone(tzName);
   1666 	int dst_icu = tz_icu->getDSTSavings();
   1667     UnicodeString id;
   1668     int32_t expected = 1800000;
   1669 	if (expected != dst_icu) {
   1670 	    dataerrln(UnicodeString("java reports dst savings of ") + expected +
   1671 	        " but icu reports " + dst_icu +
   1672 	        " for tz " + tz_icu->getID(id));
   1673 	} else {
   1674 	    logln(UnicodeString("both java and icu report dst savings of ") + expected + " for tz " + tz_icu->getID(id));
   1675 	}
   1676     delete tz_icu;
   1677 }
   1678 
   1679 /**
   1680  * Test country code support.  Jitterbug 776.
   1681  */
   1682 void TimeZoneTest::TestCountries() {
   1683     // Make sure America/Los_Angeles is in the "US" group, and
   1684     // Asia/Tokyo isn't.  Vice versa for the "JP" group.
   1685     UErrorCode ec = U_ZERO_ERROR;
   1686     int32_t n;
   1687     StringEnumeration* s = TimeZone::createEnumeration("US");
   1688     if (s == NULL) {
   1689         dataerrln("Unable to create TimeZone enumeration for US");
   1690         return;
   1691     }
   1692     n = s->count(ec);
   1693     UBool la = FALSE, tokyo = FALSE;
   1694     UnicodeString laZone("America/Los_Angeles", "");
   1695     UnicodeString tokyoZone("Asia/Tokyo", "");
   1696     int32_t i;
   1697 
   1698     if (s == NULL || n <= 0) {
   1699         dataerrln("FAIL: TimeZone::createEnumeration() returned nothing");
   1700         return;
   1701     }
   1702     for (i=0; i<n; ++i) {
   1703         const UnicodeString* id = s->snext(ec);
   1704         if (*id == (laZone)) {
   1705             la = TRUE;
   1706         }
   1707         if (*id == (tokyoZone)) {
   1708             tokyo = TRUE;
   1709         }
   1710     }
   1711     if (!la || tokyo) {
   1712         errln("FAIL: " + laZone + " in US = " + la);
   1713         errln("FAIL: " + tokyoZone + " in US = " + tokyo);
   1714     }
   1715     delete s;
   1716 
   1717     s = TimeZone::createEnumeration("JP");
   1718     n = s->count(ec);
   1719     la = FALSE; tokyo = FALSE;
   1720 
   1721     for (i=0; i<n; ++i) {
   1722         const UnicodeString* id = s->snext(ec);
   1723         if (*id == (laZone)) {
   1724             la = TRUE;
   1725         }
   1726         if (*id == (tokyoZone)) {
   1727             tokyo = TRUE;
   1728         }
   1729     }
   1730     if (la || !tokyo) {
   1731         errln("FAIL: " + laZone + " in JP = " + la);
   1732         errln("FAIL: " + tokyoZone + " in JP = " + tokyo);
   1733     }
   1734     StringEnumeration* s1 = TimeZone::createEnumeration("US");
   1735     StringEnumeration* s2 = TimeZone::createEnumeration("US");
   1736     for(i=0;i<n;++i){
   1737         const UnicodeString* id1 = s1->snext(ec);
   1738         if(id1==NULL || U_FAILURE(ec)){
   1739             errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
   1740         }
   1741         TimeZone* tz1 = TimeZone::createTimeZone(*id1);
   1742         for(int j=0; j<n;++j){
   1743             const UnicodeString* id2 = s2->snext(ec);
   1744             if(id2==NULL || U_FAILURE(ec)){
   1745                 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
   1746             }
   1747             TimeZone* tz2 = TimeZone::createTimeZone(*id2);
   1748             if(tz1->hasSameRules(*tz2)){
   1749                 logln("ID1 : " + *id1+" == ID2 : " +*id2);
   1750             }
   1751             delete tz2;
   1752         }
   1753         delete tz1;
   1754     }
   1755     delete s1;
   1756     delete s2;
   1757     delete s;
   1758 }
   1759 
   1760 void TimeZoneTest::TestHistorical() {
   1761     const int32_t H = U_MILLIS_PER_HOUR;
   1762     struct {
   1763         const char* id;
   1764         int32_t time; // epoch seconds
   1765         int32_t offset; // total offset (millis)
   1766     } DATA[] = {
   1767         // Add transition points (before/after) as desired to test historical
   1768         // behavior.
   1769         {"America/Los_Angeles", 638963999, -8*H}, // Sun Apr 01 01:59:59 GMT-08:00 1990
   1770         {"America/Los_Angeles", 638964000, -7*H}, // Sun Apr 01 03:00:00 GMT-07:00 1990
   1771         {"America/Los_Angeles", 657104399, -7*H}, // Sun Oct 28 01:59:59 GMT-07:00 1990
   1772         {"America/Los_Angeles", 657104400, -8*H}, // Sun Oct 28 01:00:00 GMT-08:00 1990
   1773         {"America/Goose_Bay", -116445601, -4*H}, // Sun Apr 24 01:59:59 GMT-04:00 1966
   1774         {"America/Goose_Bay", -116445600, -3*H}, // Sun Apr 24 03:00:00 GMT-03:00 1966
   1775         {"America/Goose_Bay", -100119601, -3*H}, // Sun Oct 30 01:59:59 GMT-03:00 1966
   1776         {"America/Goose_Bay", -100119600, -4*H}, // Sun Oct 30 01:00:00 GMT-04:00 1966
   1777         {"America/Goose_Bay", -84391201, -4*H}, // Sun Apr 30 01:59:59 GMT-04:00 1967
   1778         {"America/Goose_Bay", -84391200, -3*H}, // Sun Apr 30 03:00:00 GMT-03:00 1967
   1779         {"America/Goose_Bay", -68670001, -3*H}, // Sun Oct 29 01:59:59 GMT-03:00 1967
   1780         {"America/Goose_Bay", -68670000, -4*H}, // Sun Oct 29 01:00:00 GMT-04:00 1967
   1781         {0, 0, 0}
   1782     };
   1783 
   1784     for (int32_t i=0; DATA[i].id!=0; ++i) {
   1785         const char* id = DATA[i].id;
   1786         TimeZone *tz = TimeZone::createTimeZone(id);
   1787         UnicodeString s;
   1788         if (tz == 0) {
   1789             errln("FAIL: Cannot create %s", id);
   1790         } else if (tz->getID(s) != UnicodeString(id)) {
   1791             dataerrln((UnicodeString)"FAIL: createTimeZone(" + id + ") => " + s);
   1792         } else {
   1793             UErrorCode ec = U_ZERO_ERROR;
   1794             int32_t raw, dst;
   1795             UDate when = (double) DATA[i].time * U_MILLIS_PER_SECOND;
   1796             tz->getOffset(when, FALSE, raw, dst, ec);
   1797             if (U_FAILURE(ec)) {
   1798                 errln("FAIL: getOffset");
   1799             } else if ((raw+dst) != DATA[i].offset) {
   1800                 errln((UnicodeString)"FAIL: " + DATA[i].id + ".getOffset(" +
   1801                       //when + " = " +
   1802                       dateToString(when) + ") => " +
   1803                       raw + ", " + dst);
   1804             } else {
   1805                 logln((UnicodeString)"Ok: " + DATA[i].id + ".getOffset(" +
   1806                       //when + " = " +
   1807                       dateToString(when) + ") => " +
   1808                       raw + ", " + dst);
   1809             }
   1810         }
   1811         delete tz;
   1812     }
   1813 }
   1814 
   1815 void TimeZoneTest::TestEquivalentIDs() {
   1816     int32_t n = TimeZone::countEquivalentIDs("PST");
   1817     if (n < 2) {
   1818         dataerrln((UnicodeString)"FAIL: countEquivalentIDs(PST) = " + n);
   1819     } else {
   1820         UBool sawLA = FALSE;
   1821         for (int32_t i=0; i<n; ++i) {
   1822             UnicodeString id = TimeZone::getEquivalentID("PST", i);
   1823             logln((UnicodeString)"" + i + " : " + id);
   1824             if (id == UnicodeString("America/Los_Angeles")) {
   1825                 sawLA = TRUE;
   1826             }
   1827         }
   1828         if (!sawLA) {
   1829             errln("FAIL: America/Los_Angeles should be in the list");
   1830         }
   1831     }
   1832 }
   1833 
   1834 // Test that a transition at the end of February is handled correctly.
   1835 void TimeZoneTest::TestFebruary() {
   1836     UErrorCode status = U_ZERO_ERROR;
   1837 
   1838     // Time zone with daylight savings time from the first Sunday in November
   1839     // to the last Sunday in February.
   1840     // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n.
   1841     //
   1842     // Note: In tzdata2007h, the rule had changed, so no actual zones uses
   1843     // lastSun in Feb anymore.
   1844     SimpleTimeZone tz1(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
   1845                        UNICODE_STRING("nov-feb", 7),
   1846                        UCAL_NOVEMBER, 1, UCAL_SUNDAY,   // start: November, first, Sunday
   1847                        0,                               //        midnight wall time
   1848                        UCAL_FEBRUARY, -1, UCAL_SUNDAY,  // end:   February, last, Sunday
   1849                        0,                               //        midnight wall time
   1850                        status);
   1851     if (U_FAILURE(status)) {
   1852         errln("Unable to create the SimpleTimeZone(nov-feb): %s", u_errorName(status));
   1853         return;
   1854     }
   1855 
   1856     // Now hardcode the same rules as for Brazil in tzdata 2006n, so that
   1857     // we cover the intended code even when in the future zoneinfo hardcodes
   1858     // these transition dates.
   1859     SimpleTimeZone tz2(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
   1860                        UNICODE_STRING("nov-feb2", 8),
   1861                        UCAL_NOVEMBER, 1, -UCAL_SUNDAY,  // start: November, 1 or after, Sunday
   1862                        0,                               //        midnight wall time
   1863                        UCAL_FEBRUARY, -29, -UCAL_SUNDAY,// end:   February, 29 or before, Sunday
   1864                        0,                               //        midnight wall time
   1865                        status);
   1866     if (U_FAILURE(status)) {
   1867         errln("Unable to create the SimpleTimeZone(nov-feb2): %s", u_errorName(status));
   1868         return;
   1869     }
   1870 
   1871     // Gregorian calendar with the UTC time zone for getting sample test date/times.
   1872     GregorianCalendar gc(*TimeZone::getGMT(), status);
   1873     if (U_FAILURE(status)) {
   1874         dataerrln("Unable to create the UTC calendar: %s", u_errorName(status));
   1875         return;
   1876     }
   1877 
   1878     struct {
   1879         // UTC time.
   1880         int32_t year, month, day, hour, minute, second;
   1881         // Expected time zone offset in hours after GMT (negative=before GMT).
   1882         int32_t offsetHours;
   1883     } data[] = {
   1884         { 2006, UCAL_NOVEMBER,  5, 02, 59, 59, -3 },
   1885         { 2006, UCAL_NOVEMBER,  5, 03, 00, 00, -2 },
   1886         { 2007, UCAL_FEBRUARY, 25, 01, 59, 59, -2 },
   1887         { 2007, UCAL_FEBRUARY, 25, 02, 00, 00, -3 },
   1888 
   1889         { 2007, UCAL_NOVEMBER,  4, 02, 59, 59, -3 },
   1890         { 2007, UCAL_NOVEMBER,  4, 03, 00, 00, -2 },
   1891         { 2008, UCAL_FEBRUARY, 24, 01, 59, 59, -2 },
   1892         { 2008, UCAL_FEBRUARY, 24, 02, 00, 00, -3 },
   1893 
   1894         { 2008, UCAL_NOVEMBER,  2, 02, 59, 59, -3 },
   1895         { 2008, UCAL_NOVEMBER,  2, 03, 00, 00, -2 },
   1896         { 2009, UCAL_FEBRUARY, 22, 01, 59, 59, -2 },
   1897         { 2009, UCAL_FEBRUARY, 22, 02, 00, 00, -3 },
   1898 
   1899         { 2009, UCAL_NOVEMBER,  1, 02, 59, 59, -3 },
   1900         { 2009, UCAL_NOVEMBER,  1, 03, 00, 00, -2 },
   1901         { 2010, UCAL_FEBRUARY, 28, 01, 59, 59, -2 },
   1902         { 2010, UCAL_FEBRUARY, 28, 02, 00, 00, -3 }
   1903     };
   1904 
   1905     TimeZone *timezones[] = { &tz1, &tz2 };
   1906 
   1907     TimeZone *tz;
   1908     UDate dt;
   1909     int32_t t, i, raw, dst;
   1910     for (t = 0; t < LENGTHOF(timezones); ++t) {
   1911         tz = timezones[t];
   1912         for (i = 0; i < LENGTHOF(data); ++i) {
   1913             gc.set(data[i].year, data[i].month, data[i].day,
   1914                    data[i].hour, data[i].minute, data[i].second);
   1915             dt = gc.getTime(status);
   1916             if (U_FAILURE(status)) {
   1917                 errln("test case %d.%d: bad date/time %04d-%02d-%02d %02d:%02d:%02d",
   1918                       t, i,
   1919                       data[i].year, data[i].month + 1, data[i].day,
   1920                       data[i].hour, data[i].minute, data[i].second);
   1921                 status = U_ZERO_ERROR;
   1922                 continue;
   1923             }
   1924             tz->getOffset(dt, FALSE, raw, dst, status);
   1925             if (U_FAILURE(status)) {
   1926                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) fails: %s",
   1927                       t, i,
   1928                       data[i].year, data[i].month + 1, data[i].day,
   1929                       data[i].hour, data[i].minute, data[i].second,
   1930                       u_errorName(status));
   1931                 status = U_ZERO_ERROR;
   1932             } else if ((raw + dst) != data[i].offsetHours * U_MILLIS_PER_HOUR) {
   1933                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d",
   1934                       t, i,
   1935                       data[i].year, data[i].month + 1, data[i].day,
   1936                       data[i].hour, data[i].minute, data[i].second,
   1937                       raw, dst, data[i].offsetHours * U_MILLIS_PER_HOUR);
   1938             }
   1939         }
   1940     }
   1941 }
   1942 
   1943 void TimeZoneTest::TestCanonicalIDAPI() {
   1944     // Bogus input string.
   1945     UnicodeString bogus;
   1946     bogus.setToBogus();
   1947     UnicodeString canonicalID;
   1948     UErrorCode ec = U_ZERO_ERROR;
   1949     UnicodeString *pResult = &TimeZone::getCanonicalID(bogus, canonicalID, ec);
   1950     assertEquals("TimeZone::getCanonicalID(bogus) should fail", U_ILLEGAL_ARGUMENT_ERROR, ec);
   1951     assertTrue("TimeZone::getCanonicalID(bogus) should return the dest string", pResult == &canonicalID);
   1952 
   1953     // U_FAILURE on input.
   1954     UnicodeString berlin("Europe/Berlin");
   1955     ec = U_MEMORY_ALLOCATION_ERROR;
   1956     pResult = &TimeZone::getCanonicalID(berlin, canonicalID, ec);
   1957     assertEquals("TimeZone::getCanonicalID(failure) should fail", U_MEMORY_ALLOCATION_ERROR, ec);
   1958     assertTrue("TimeZone::getCanonicalID(failure) should return the dest string", pResult == &canonicalID);
   1959 
   1960     // Valid input should un-bogus the dest string.
   1961     canonicalID.setToBogus();
   1962     ec = U_ZERO_ERROR;
   1963     pResult = &TimeZone::getCanonicalID(berlin, canonicalID, ec);
   1964     assertSuccess("TimeZone::getCanonicalID(bogus dest) should succeed", ec, TRUE);
   1965     assertTrue("TimeZone::getCanonicalID(bogus dest) should return the dest string", pResult == &canonicalID);
   1966     assertFalse("TimeZone::getCanonicalID(bogus dest) should un-bogus the dest string", canonicalID.isBogus());
   1967     assertEquals("TimeZone::getCanonicalID(bogus dest) unexpected result", canonicalID, berlin, TRUE);
   1968 }
   1969 
   1970 void TimeZoneTest::TestCanonicalID() {
   1971 
   1972     // Some canonical IDs in CLDR are defined as "Link"
   1973     // in Olson tzdata.
   1974     static const struct {
   1975         const char *alias;
   1976         const char *zone;
   1977     } excluded1[] = {
   1978         {"Africa/Khartoum", "Africa/Juba"},
   1979         {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata
   1980         {"America/Dominica", "America/Anguilla"},
   1981         {"America/Grenada", "America/Anguilla"},
   1982         {"America/Guadeloupe", "America/Anguilla"},
   1983         {"America/Marigot", "America/Anguilla"},
   1984         {"America/Montserrat", "America/Anguilla"},
   1985         {"America/Port_of_Spain", "America/Anguilla"},
   1986         {"America/St_Barthelemy", "America/Anguilla"},
   1987         {"America/St_Kitts", "America/Anguilla"},
   1988         {"America/St_Lucia", "America/Anguilla"},
   1989         {"America/St_Thomas", "America/Anguilla"},
   1990         {"America/St_Vincent", "America/Anguilla"},
   1991         {"America/Tortola", "America/Anguilla"},
   1992         {"America/Virgin", "America/Anguilla"},
   1993         {"America/Curacao", "America/Aruba"},
   1994         {"America/Kralendijk", "America/Aruba"},
   1995         {"America/Lower_Princes", "America/Aruba"},
   1996         {"Antarctica/South_Pole", "Antarctica/McMurdo"},
   1997         {"Atlantic/Jan_Mayen", "Europe/Oslo"},
   1998         {"Arctic/Longyearbyen", "Europe/Oslo"},
   1999         {"Europe/Busingen", "Europe/Zurich"},
   2000         {"Europe/Guernsey", "Europe/London"},
   2001         {"Europe/Isle_of_Man", "Europe/London"},
   2002         {"Europe/Jersey", "Europe/London"},
   2003         {"Europe/Ljubljana", "Europe/Belgrade"},
   2004         {"Europe/Podgorica", "Europe/Belgrade"},
   2005         {"Europe/Sarajevo", "Europe/Belgrade"},
   2006         {"Europe/Skopje", "Europe/Belgrade"},
   2007         {"Europe/Zagreb", "Europe/Belgrade"},
   2008         {"Europe/Bratislava", "Europe/Prague"},
   2009         {"Europe/Mariehamn", "Europe/Helsinki"},
   2010         {"Europe/San_Marino", "Europe/Rome"},
   2011         {"Europe/Vatican", "Europe/Rome"},
   2012         {"Europe/Vaduz", "Europe/Zurich"},
   2013         {"Pacific/Auckland", "Antarctica/McMurdo"},
   2014         {"Pacific/Johnston", "Pacific/Honolulu"},
   2015         {0, 0}
   2016     };
   2017 
   2018     // Following IDs are aliases of Etc/GMT in CLDR,
   2019     // but Olson tzdata has 3 independent definitions
   2020     // for Etc/GMT, Etc/UTC, Etc/UCT.
   2021     // Until we merge them into one equivalent group
   2022     // in zoneinfo.res, we exclude them in the test
   2023     // below.
   2024     static const char* excluded2[] = {
   2025         "Etc/UCT", "UCT",
   2026         "Etc/UTC", "UTC",
   2027         "Etc/Universal", "Universal",
   2028         "Etc/Zulu", "Zulu", 0
   2029     };
   2030 
   2031     // Walk through equivalency groups
   2032     UErrorCode ec = U_ZERO_ERROR;
   2033     int32_t s_length, i, j, k;
   2034     StringEnumeration* s = TimeZone::createEnumeration();
   2035     if (s == NULL) {
   2036         dataerrln("Unable to create TimeZone enumeration");
   2037         return;
   2038     }
   2039     UnicodeString canonicalID, tmpCanonical;
   2040     s_length = s->count(ec);
   2041     for (i = 0; i < s_length;++i) {
   2042         const UnicodeString *tzid = s->snext(ec);
   2043         int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid);
   2044         if (nEquiv == 0) {
   2045             continue;
   2046         }
   2047         UBool bFoundCanonical = FALSE;
   2048         // Make sure getCanonicalID returns the exact same result
   2049         // for all entries within a same equivalency group with some
   2050         // exceptions listed in exluded1.
   2051         // Also, one of them must be canonical id.
   2052         for (j = 0; j < nEquiv; j++) {
   2053             UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j);
   2054             TimeZone::getCanonicalID(tmp, tmpCanonical, ec);
   2055             if (U_FAILURE(ec)) {
   2056                 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed.");
   2057                 ec = U_ZERO_ERROR;
   2058                 continue;
   2059             }
   2060             // Some exceptional cases
   2061             for (k = 0; excluded1[k].alias != 0; k++) {
   2062                 if (tmpCanonical == excluded1[k].alias) {
   2063                     tmpCanonical = excluded1[k].zone;
   2064                     break;
   2065                 }
   2066             }
   2067             if (j == 0) {
   2068                 canonicalID = tmpCanonical;
   2069             } else if (canonicalID != tmpCanonical) {
   2070                 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID);
   2071             }
   2072 
   2073             if (canonicalID == tmp) {
   2074                 bFoundCanonical = TRUE;
   2075             }
   2076         }
   2077         // At least one ID in an equvalency group must match the
   2078         // canonicalID
   2079         if (bFoundCanonical == FALSE) {
   2080             // test exclusion because of differences between Olson tzdata and CLDR
   2081             UBool isExcluded = FALSE;
   2082             for (k = 0; excluded2[k] != 0; k++) {
   2083                 if (*tzid == UnicodeString(excluded2[k])) {
   2084                     isExcluded = TRUE;
   2085                     break;
   2086                 }
   2087             }
   2088             if (isExcluded) {
   2089                 continue;
   2090             }
   2091             errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID);
   2092         }
   2093     }
   2094     delete s;
   2095 
   2096     // Testing some special cases
   2097     static const struct {
   2098         const char *id;
   2099         const char *expected;
   2100         UBool isSystem;
   2101     } data[] = {
   2102         {"GMT-03", "GMT-03:00", FALSE},
   2103         {"GMT+4", "GMT+04:00", FALSE},
   2104         {"GMT-055", "GMT-00:55", FALSE},
   2105         {"GMT+430", "GMT+04:30", FALSE},
   2106         {"GMT-12:15", "GMT-12:15", FALSE},
   2107         {"GMT-091015", "GMT-09:10:15", FALSE},
   2108         {"GMT+1:90", 0, FALSE},
   2109         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
   2110         {"Etc/Unknown", "Etc/Unknown", FALSE},
   2111         {"bogus", 0, FALSE},
   2112         {"", 0, FALSE},
   2113         {"America/Marigot", "America/Marigot", TRUE},     // Olson link, but CLDR canonical (#8953)
   2114         {"Europe/Bratislava", "Europe/Bratislava", TRUE}, // Same as above
   2115         {0, 0, FALSE}
   2116     };
   2117 
   2118     UBool isSystemID;
   2119     for (i = 0; data[i].id != 0; i++) {
   2120         TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec);
   2121         if (U_FAILURE(ec)) {
   2122             if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) {
   2123                 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   2124                     + "\") returned status U_ILLEGAL_ARGUMENT_ERROR");
   2125             }
   2126             ec = U_ZERO_ERROR;
   2127             continue;
   2128         }
   2129         if (canonicalID != data[i].expected) {
   2130             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   2131                 + "\") returned " + canonicalID + " - expected: " + data[i].expected);
   2132         }
   2133         if (isSystemID != data[i].isSystem) {
   2134             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   2135                 + "\") set " + isSystemID + " to isSystemID");
   2136         }
   2137     }
   2138 }
   2139 
   2140 //
   2141 //  Test Display Names, choosing zones and lcoales where there are multiple
   2142 //                      meta-zones defined.
   2143 //
   2144 static struct   {
   2145     const char            *zoneName;
   2146     const char            *localeName;
   2147     UBool                  summerTime;
   2148     TimeZone::EDisplayType style;
   2149     const char            *expectedDisplayName; }
   2150  zoneDisplayTestData [] =  {
   2151      //  zone id         locale   summer   format          expected display name
   2152       {"Europe/London",     "en", FALSE, TimeZone::SHORT, "GMT"},
   2153       {"Europe/London",     "en", FALSE, TimeZone::LONG,  "Greenwich Mean Time"},
   2154       {"Europe/London",     "en", TRUE,  TimeZone::SHORT, "GMT+1" /*"BST"*/},
   2155       {"Europe/London",     "en", TRUE,  TimeZone::LONG,  "British Summer Time"},
   2156 
   2157       {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"},
   2158       {"America/Anchorage", "en", FALSE, TimeZone::LONG,  "Alaska Standard Time"},
   2159       {"America/Anchorage", "en", TRUE,  TimeZone::SHORT, "AKDT"},
   2160       {"America/Anchorage", "en", TRUE,  TimeZone::LONG,  "Alaska Daylight Time"},
   2161 
   2162       // Southern Hemisphere, all data from meta:Australia_Western
   2163       {"Australia/Perth",   "en", FALSE, TimeZone::SHORT, "GMT+8"/*"AWST"*/},
   2164       {"Australia/Perth",   "en", FALSE, TimeZone::LONG,  "Australian Western Standard Time"},
   2165       // Note: Perth does not observe DST currently. When display name is missing,
   2166       // the localized GMT format with the current offset is used even daylight name was
   2167       // requested. See #9350.
   2168       {"Australia/Perth",   "en", TRUE,  TimeZone::SHORT, "GMT+8"/*"AWDT"*/},
   2169       {"Australia/Perth",   "en", TRUE,  TimeZone::LONG,  "Australian Western Daylight Time"},
   2170 
   2171       {"America/Sao_Paulo",  "en", FALSE, TimeZone::SHORT, "GMT-3"/*"BRT"*/},
   2172       {"America/Sao_Paulo",  "en", FALSE, TimeZone::LONG,  "Brasilia Standard Time"},
   2173       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::SHORT, "GMT-2"/*"BRST"*/},
   2174       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::LONG,  "Brasilia Summer Time"},
   2175 
   2176       // No Summer Time, but had it before 1983.
   2177       {"Pacific/Honolulu",   "en", FALSE, TimeZone::SHORT, "HST"},
   2178       {"Pacific/Honolulu",   "en", FALSE, TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
   2179       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::SHORT, "HDT"},
   2180       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::LONG,  "Hawaii-Aleutian Daylight Time"},
   2181 
   2182       // Northern, has Summer, not commonly used.
   2183       {"Europe/Helsinki",    "en", FALSE, TimeZone::SHORT, "GMT+2"/*"EET"*/},
   2184       {"Europe/Helsinki",    "en", FALSE, TimeZone::LONG,  "Eastern European Standard Time"},
   2185       {"Europe/Helsinki",    "en", TRUE,  TimeZone::SHORT, "GMT+3"/*"EEST"*/},
   2186       {"Europe/Helsinki",    "en", TRUE,  TimeZone::LONG,  "Eastern European Summer Time"},
   2187 
   2188       // Repeating the test data for DST.  The test data below trigger the problem reported
   2189       // by Ticket#6644
   2190       {"Europe/London",       "en", TRUE, TimeZone::SHORT, "GMT+1" /*"BST"*/},
   2191       {"Europe/London",       "en", TRUE, TimeZone::LONG,  "British Summer Time"},
   2192 
   2193       {NULL, NULL, FALSE, TimeZone::SHORT, NULL}   // NULL values terminate list
   2194     };
   2195 
   2196 void TimeZoneTest::TestDisplayNamesMeta() {
   2197     UErrorCode status = U_ZERO_ERROR;
   2198     GregorianCalendar cal(*TimeZone::getGMT(), status);
   2199     if (failure(status, "GregorianCalendar", TRUE)) return;
   2200 
   2201     UBool sawAnError = FALSE;
   2202     for (int testNum   = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) {
   2203         Locale locale  = Locale::createFromName(zoneDisplayTestData[testNum].localeName);
   2204         TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName);
   2205         UnicodeString displayName;
   2206         zone->getDisplayName(zoneDisplayTestData[testNum].summerTime,
   2207                              zoneDisplayTestData[testNum].style,
   2208                              locale,
   2209                              displayName);
   2210         if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) {
   2211             char  name[100];
   2212             UErrorCode status = U_ZERO_ERROR;
   2213             displayName.extract(name, 100, NULL, status);
   2214             if (isDevelopmentBuild) {
   2215                 sawAnError = TRUE;
   2216                 dataerrln("Incorrect time zone display name.  zone = \"%s\",\n"
   2217                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
   2218                       "   Expected \"%s\", "
   2219                       "   Got \"%s\"\n   Error: %s", zoneDisplayTestData[testNum].zoneName,
   2220                                          zoneDisplayTestData[testNum].localeName,
   2221                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
   2222                                             "SHORT" : "LONG",
   2223                                          zoneDisplayTestData[testNum].summerTime,
   2224                                          zoneDisplayTestData[testNum].expectedDisplayName,
   2225                                          name,
   2226                                          u_errorName(status));
   2227             } else {
   2228                 logln("Incorrect time zone display name.  zone = \"%s\",\n"
   2229                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
   2230                       "   Expected \"%s\", "
   2231                       "   Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
   2232                                          zoneDisplayTestData[testNum].localeName,
   2233                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
   2234                                             "SHORT" : "LONG",
   2235                                          zoneDisplayTestData[testNum].summerTime,
   2236                                          zoneDisplayTestData[testNum].expectedDisplayName,
   2237                                          name);
   2238             }
   2239         }
   2240         delete zone;
   2241     }
   2242     if (sawAnError) {
   2243         dataerrln("***Note: Errors could be the result of changes to zoneStrings locale data");
   2244     }
   2245 }
   2246 
   2247 void TimeZoneTest::TestGetRegion()
   2248 {
   2249     static const struct {
   2250         const char *id;
   2251         const char *region;
   2252     } data[] = {
   2253         {"America/Los_Angeles",             "US"},
   2254         {"America/Indianapolis",            "US"},  // CLDR canonical, Olson backward
   2255         {"America/Indiana/Indianapolis",    "US"},  // CLDR alias
   2256         {"Mexico/General",                  "MX"},  // Link America/Mexico_City, Olson backward
   2257         {"Etc/UTC",                         "001"},
   2258         {"EST5EDT",                         "001"},
   2259         {"PST",                             "US"},  // Link America/Los_Angeles
   2260         {"Europe/Helsinki",                 "FI"},
   2261         {"Europe/Mariehamn",                "AX"},  // Link Europe/Helsinki, but in zone.tab
   2262         {"Asia/Riyadh",                     "SA"},
   2263         // tz file solar87 was removed from tzdata2013i
   2264         // {"Asia/Riyadh87",                   "001"}, // this should be "SA" actually, but not in zone.tab
   2265         {"Etc/Unknown",                     0},  // CLDR canonical, but not a sysmte zone ID
   2266         {"bogus",                           0},  // bogus
   2267         {"GMT+08:00",                       0},  // a custom ID, not a system zone ID
   2268         {0, 0}
   2269     };
   2270 
   2271     int32_t i;
   2272     char region[4];
   2273     UErrorCode sts;
   2274     for (i = 0; data[i].id; i++) {
   2275         sts = U_ZERO_ERROR;
   2276         TimeZone::getRegion(data[i].id, region, sizeof(region), sts);
   2277         if (U_SUCCESS(sts)) {
   2278             if (data[i].region == 0) {
   2279                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
   2280                     + region + " [expected: U_ILLEGAL_ARGUMENT_ERROR]");
   2281             } else if (uprv_strcmp(region, data[i].region) != 0) {
   2282                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns "
   2283                     + region + " [expected: " + data[i].region + "]");
   2284             }
   2285         } else if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
   2286             if (data[i].region != 0) {
   2287                 dataerrln((UnicodeString)"Fail: getRegion(\"" + data[i].id
   2288                     + "\") returns error status U_ILLEGAL_ARGUMENT_ERROR [expected: "
   2289                     + data[i].region + "]");
   2290             }
   2291         } else {
   2292                 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id
   2293                     + "\") returns an unexpected error status");
   2294         }
   2295     }
   2296 
   2297     // Extra test cases for short buffer
   2298     int32_t len;
   2299     char region2[2];
   2300     sts = U_ZERO_ERROR;
   2301 
   2302     len = TimeZone::getRegion("America/New_York", region2, sizeof(region2), sts);
   2303     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
   2304         dataerrln("Error calling TimeZone::getRegion");
   2305     } else {
   2306         if (sts != U_STRING_NOT_TERMINATED_WARNING) {
   2307             errln("Expected U_STRING_NOT_TERMINATED_WARNING");
   2308         }
   2309         if (len != 2) { // length of "US"
   2310             errln("Incorrect result length");
   2311         }
   2312         if (uprv_strncmp(region2, "US", 2) != 0) {
   2313             errln("Incorrect result");
   2314         }
   2315     }
   2316 
   2317     char region1[1];
   2318     sts = U_ZERO_ERROR;
   2319 
   2320     len = TimeZone::getRegion("America/Chicago", region1, sizeof(region1), sts);
   2321     if (sts == U_ILLEGAL_ARGUMENT_ERROR) {
   2322         dataerrln("Error calling TimeZone::getRegion");
   2323     } else {
   2324         if (sts != U_BUFFER_OVERFLOW_ERROR) {
   2325             errln("Expected U_BUFFER_OVERFLOW_ERROR");
   2326         }
   2327         if (len != 2) { // length of "US"
   2328             errln("Incorrect result length");
   2329         }
   2330     }
   2331 }
   2332 
   2333 void TimeZoneTest::TestGetUnknown() {
   2334     const TimeZone &unknown = TimeZone::getUnknown();
   2335     UnicodeString expectedID = UNICODE_STRING_SIMPLE("Etc/Unknown");
   2336     UnicodeString id;
   2337     assertEquals("getUnknown() wrong ID", expectedID, unknown.getID(id));
   2338     assertTrue("getUnknown() wrong offset", 0 == unknown.getRawOffset());
   2339     assertFalse("getUnknown() uses DST", unknown.useDaylightTime());
   2340 }
   2341 
   2342 void TimeZoneTest::TestGetWindowsID(void) {
   2343     static const struct {
   2344         const char *id;
   2345         const char *winid;
   2346     } TESTDATA[] = {
   2347         {"America/New_York",        "Eastern Standard Time"},
   2348         {"America/Montreal",        "Eastern Standard Time"},
   2349         {"America/Los_Angeles",     "Pacific Standard Time"},
   2350         {"America/Vancouver",       "Pacific Standard Time"},
   2351         {"Asia/Shanghai",           "China Standard Time"},
   2352         {"Asia/Chongqing",          "China Standard Time"},
   2353         {"America/Indianapolis",    "US Eastern Standard Time"},            // CLDR canonical name
   2354         {"America/Indiana/Indianapolis",    "US Eastern Standard Time"},    // tzdb canonical name
   2355         {"Asia/Khandyga",           "Yakutsk Standard Time"},
   2356         {"Australia/Eucla",         ""}, // No Windows ID mapping
   2357         {"Bogus",                   ""},
   2358         {0,                         0},
   2359     };
   2360 
   2361     for (int32_t i = 0; TESTDATA[i].id != 0; i++) {
   2362         UErrorCode sts = U_ZERO_ERROR;
   2363         UnicodeString windowsID;
   2364 
   2365         TimeZone::getWindowsID(UnicodeString(TESTDATA[i].id), windowsID, sts);
   2366         assertSuccess(TESTDATA[i].id, sts);
   2367         assertEquals(TESTDATA[i].id, UnicodeString(TESTDATA[i].winid), windowsID, TRUE);
   2368     }
   2369 }
   2370 
   2371 void TimeZoneTest::TestGetIDForWindowsID(void) {
   2372     static const struct {
   2373         const char *winid;
   2374         const char *region;
   2375         const char *id;
   2376     } TESTDATA[] = {
   2377         {"Eastern Standard Time",   0,      "America/New_York"},
   2378         {"Eastern Standard Time",   "US",   "America/New_York"},
   2379         {"Eastern Standard Time",   "CA",   "America/Toronto"},
   2380         {"Eastern Standard Time",   "CN",   "America/New_York"},
   2381         {"China Standard Time",     0,      "Asia/Shanghai"},
   2382         {"China Standard Time",     "CN",   "Asia/Shanghai"},
   2383         {"China Standard Time",     "HK",   "Asia/Hong_Kong"},
   2384         {"Mid-Atlantic Standard Time",  0,  ""}, // No tz database mapping
   2385         {"Bogus",                   0,      ""},
   2386         {0,                         0,      0},
   2387     };
   2388 
   2389     for (int32_t i = 0; TESTDATA[i].winid != 0; i++) {
   2390         UErrorCode sts = U_ZERO_ERROR;
   2391         UnicodeString id;
   2392 
   2393         TimeZone::getIDForWindowsID(UnicodeString(TESTDATA[i].winid), TESTDATA[i].region,
   2394                                     id, sts);
   2395         assertSuccess(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, sts);
   2396         assertEquals(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, TESTDATA[i].id, id, TRUE);
   2397     }
   2398 }
   2399 
   2400 #endif /* #if !UCONFIG_NO_FORMATTING */
   2401