Home | History | Annotate | Download | only in intltest
      1 /***********************************************************************
      2  * COPYRIGHT:
      3  * Copyright (c) 1997-2010, 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 "tztest.h"
     18 #include "cmemory.h"
     19 #include "putilimp.h"
     20 #include "cstring.h"
     21 #include "olsontz.h"
     22 
     23 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
     24 
     25 #define CASE(id,test) case id:                               \
     26                           name = #test;                      \
     27                           if (exec) {                        \
     28                               logln(#test "---"); logln(""); \
     29                               test();                        \
     30                           }                                  \
     31                           break
     32 
     33 // *****************************************************************************
     34 // class TimeZoneTest
     35 // *****************************************************************************
     36 
     37 // TODO: We should probably read following data at runtime, so we can update
     38 // the these values every release with necessary data changes.
     39 const int32_t TimeZoneTest::REFERENCE_YEAR = 2009;
     40 const char * TimeZoneTest::REFERENCE_DATA_VERSION = "2009d";
     41 
     42 void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     43 {
     44     if (exec) logln("TestSuite TestTimeZone");
     45     switch (index) {
     46         CASE(0, TestPRTOffset);
     47         CASE(1, TestVariousAPI518);
     48         CASE(2, TestGetAvailableIDs913);
     49         CASE(3, TestGenericAPI);
     50         CASE(4, TestRuleAPI);
     51         CASE(5, TestShortZoneIDs);
     52         CASE(6, TestCustomParse);
     53         CASE(7, TestDisplayName);
     54         CASE(8, TestDSTSavings);
     55         CASE(9, TestAlternateRules);
     56         CASE(10,TestCountries);
     57         CASE(11,TestHistorical);
     58         CASE(12,TestEquivalentIDs);
     59         CASE(13, TestAliasedNames);
     60         CASE(14, TestFractionalDST);
     61         CASE(15, TestFebruary);
     62         CASE(16, TestCanonicalID);
     63         CASE(17, TestDisplayNamesMeta);
     64        default: name = ""; break;
     65     }
     66 }
     67 
     68 const int32_t TimeZoneTest::millisPerHour = 3600000;
     69 
     70 // ---------------------------------------------------------------------------------
     71 
     72 /**
     73  * Generic API testing for API coverage.
     74  */
     75 void
     76 TimeZoneTest::TestGenericAPI()
     77 {
     78     UnicodeString id("NewGMT");
     79     int32_t offset = 12345;
     80 
     81     SimpleTimeZone *zone = new SimpleTimeZone(offset, id);
     82     if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE");
     83 
     84     TimeZone* zoneclone = zone->clone();
     85     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
     86     zoneclone->setID("abc");
     87     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
     88     delete zoneclone;
     89 
     90     zoneclone = zone->clone();
     91     if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed");
     92     zoneclone->setRawOffset(45678);
     93     if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed");
     94 
     95     SimpleTimeZone copy(*zone);
     96     if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed");
     97     copy = *(SimpleTimeZone*)zoneclone;
     98     if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed");
     99 
    100     TimeZone* saveDefault = TimeZone::createDefault();
    101     logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id));
    102     TimeZone* pstZone = TimeZone::createTimeZone("America/Los_Angeles");
    103 
    104     logln("call uprv_timezone() which uses the host");
    105     logln("to get the difference in seconds between coordinated universal");
    106     logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)");
    107 
    108     int32_t tzoffset = uprv_timezone();
    109     logln(UnicodeString("Value returned from uprv_timezone = ") + tzoffset);
    110     // Invert sign because UNIX semantics are backwards
    111     if (tzoffset < 0)
    112         tzoffset = -tzoffset;
    113     if ((*saveDefault == *pstZone) && (tzoffset != 28800)) {
    114         errln("FAIL: t_timezone may be incorrect.  It is not 28800");
    115     }
    116 
    117     if ((tzoffset % 900) != 0) {
    118 #ifdef U_DARWIN
    119         /*
    120          * Ticket: 6364 (mow - 090522)
    121          * On MacOSX 10.5.3 and up, this test fails when TZ is set to certain timezones (e.g. Africa/Dar_es_Salaam)
    122          * Their GMT offset is not a multiple of 15 minutes. This is a MacOSX issue and is not a problem with ICU.
    123          */
    124         infoln("[WARNING] FAIL: t_timezone may be incorrect. It is not a multiple of 15min. It is %d (Note: This is a known issue on MacOSX)", tzoffset);
    125 #else
    126         errln("FAIL: t_timezone may be incorrect. It is not a multiple of 15min. It is %d", tzoffset);
    127 #endif
    128     }
    129 
    130     TimeZone::adoptDefault(zone);
    131     TimeZone* defaultzone = TimeZone::createDefault();
    132     if (defaultzone == zone ||
    133         !(*defaultzone == *zone))
    134         errln("FAIL: createDefault failed");
    135     TimeZone::adoptDefault(saveDefault);
    136     delete defaultzone;
    137     delete zoneclone;
    138     delete pstZone;
    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     s_length = s->count(ec);
    419     for (i = 0; i < s_length;++i) {
    420         if (i > 0) *buf += ", ";
    421         if ((i & 1) == 0) {
    422             *buf += *s->snext(ec);
    423         } else {
    424             *buf += UnicodeString(s->next(NULL, ec), "");
    425         }
    426 
    427         if((i % 5) == 4) {
    428             // replace s with a clone of itself
    429             StringEnumeration *s2 = s->clone();
    430             if(s2 == NULL || s_length != s2->count(ec)) {
    431                 errln("TimezoneEnumeration.clone() failed");
    432             } else {
    433                 delete s;
    434                 s = s2;
    435             }
    436         }
    437     }
    438     *buf += " };";
    439     logln(*buf);
    440 
    441     /* Confirm that the following zones can be retrieved: The first
    442      * zone, the last zone, and one in-between.  This tests the binary
    443      * search through the system zone data.
    444      */
    445     s->reset(ec);
    446     int32_t middle = s_length/2;
    447     for (i=0; i<s_length; ++i) {
    448         const UnicodeString* id = s->snext(ec);
    449         if (i==0 || i==middle || i==(s_length-1)) {
    450         TimeZone *z = TimeZone::createTimeZone(*id);
    451         if (z == 0) {
    452             errln(UnicodeString("FAIL: createTimeZone(") +
    453                   *id + ") -> 0");
    454         } else if (z->getID(str) != *id) {
    455             errln(UnicodeString("FAIL: createTimeZone(") +
    456                   *id + ") -> zone " + str);
    457         } else {
    458             logln(UnicodeString("OK: createTimeZone(") +
    459                   *id + ") succeeded");
    460         }
    461         delete z;
    462         }
    463     }
    464     delete s;
    465 
    466     buf->truncate(0);
    467     *buf += "TimeZone::createEnumeration(GMT+01:00) = { ";
    468 
    469     s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR);
    470     s_length = s->count(ec);
    471     for (i = 0; i < s_length;++i) {
    472         if (i > 0) *buf += ", ";
    473         *buf += *s->snext(ec);
    474     }
    475     delete s;
    476     *buf += " };";
    477     logln(*buf);
    478 
    479 
    480     buf->truncate(0);
    481     *buf += "TimeZone::createEnumeration(US) = { ";
    482 
    483     s = TimeZone::createEnumeration("US");
    484     s_length = s->count(ec);
    485     for (i = 0; i < s_length;++i) {
    486         if (i > 0) *buf += ", ";
    487         *buf += *s->snext(ec);
    488     }
    489     *buf += " };";
    490     logln(*buf);
    491 
    492     TimeZone *tz = TimeZone::createTimeZone("PST");
    493     if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str));
    494     else errln("FAIL: getTimeZone(PST) = null");
    495     delete tz;
    496     tz = TimeZone::createTimeZone("America/Los_Angeles");
    497     if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str));
    498     else errln("FAIL: getTimeZone(PST) = null");
    499     delete tz;
    500 
    501     // @bug 4096694
    502     tz = TimeZone::createTimeZone("NON_EXISTENT");
    503     UnicodeString temp;
    504     if (tz == 0)
    505         errln("FAIL: getTimeZone(NON_EXISTENT) = null");
    506     else if (tz->getID(temp) != "GMT")
    507         errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp);
    508     delete tz;
    509 
    510     delete buf;
    511     delete s;
    512 }
    513 
    514 
    515 /**
    516  * NOTE: As of ICU 2.8, this test confirms that the "tz.alias"
    517  * file, used to build ICU alias zones, is working.  It also
    518  * looks at some genuine Olson compatibility IDs. [aliu]
    519  *
    520  * This test is problematic. It should really just confirm that
    521  * the list of compatibility zone IDs exist and are somewhat
    522  * meaningful (that is, they aren't all aliases of GMT). It goes a
    523  * bit further -- it hard-codes expectations about zone behavior,
    524  * when in fact zones are redefined quite frequently. ICU's build
    525  * process means that it is easy to update ICU to contain the
    526  * latest Olson zone data, but if a zone tested here changes, then
    527  * this test will fail.  I have updated the test for 1999j data,
    528  * but further updates will probably be required. Note that some
    529  * of the concerts listed below no longer apply -- in particular,
    530  * we do NOT overwrite real UNIX zones with 3-letter IDs. There
    531  * are two points of overlap as of 1999j: MET and EET. These are
    532  * both real UNIX zones, so we just use the official
    533  * definition. This test has been updated to reflect this.
    534  * 12/3/99 aliu
    535  *
    536  * Added tests for additional zones and aliases from the icuzones file.
    537  * Markus Scherer 2006-nov-06
    538  *
    539  * [srl - from java - 7/5/1998]
    540  * @bug 4130885
    541  * Certain short zone IDs, used since 1.1.x, are incorrect.
    542  *
    543  * The worst of these is:
    544  *
    545  * "CAT" (Central African Time) should be GMT+2:00, but instead returns a
    546  * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST,
    547  * or AZOST, depending on which zone is meant, but in no case is it CAT.
    548  *
    549  * Other wrong zone IDs:
    550  *
    551  * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time,
    552  * GMT-5:00. European Central time is abbreviated CEST.
    553  *
    554  * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time,
    555  * GMT-11:00. Solomon Island time is SBT.
    556  *
    557  * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for
    558  * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST.
    559  *
    560  * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in
    561  * another bug.] It should be "AKST". AST is Atlantic Standard Time,
    562  * GMT-4:00.
    563  *
    564  * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time,
    565  * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct
    566  * from MST with daylight savings.
    567  *
    568  * In addition to these problems, a number of zones are FAKE. That is, they
    569  * don't match what people use in the real world.
    570  *
    571  * FAKE zones:
    572  *
    573  * EET (should be EEST)
    574  * ART (should be EEST)
    575  * MET (should be IRST)
    576  * NET (should be AMST)
    577  * PLT (should be PKT)
    578  * BST (should be BDT)
    579  * VST (should be ICT)
    580  * CTT (should be CST) +
    581  * ACT (should be CST) +
    582  * AET (should be EST) +
    583  * MIT (should be WST) +
    584  * IET (should be EST) +
    585  * PRT (should be AST) +
    586  * CNT (should be NST)
    587  * AGT (should be ARST)
    588  * BET (should be EST) +
    589  *
    590  * + A zone with the correct name already exists and means something
    591  * else. E.g., EST usually indicates the US Eastern zone, so it cannot be
    592  * used for Brazil (BET).
    593  */
    594 void TimeZoneTest::TestShortZoneIDs()
    595 {
    596     UErrorCode status = U_ZERO_ERROR;
    597 
    598     // This test case is tzdata version sensitive.
    599     UBool isNonReferenceTzdataVersion = FALSE;
    600     const char *tzdataVer = TimeZone::getTZDataVersion(status);
    601     if (failure(status, "getTZDataVersion")) return;
    602     if (uprv_strcmp(tzdataVer, TimeZoneTest::REFERENCE_DATA_VERSION) != 0) {
    603         // Note: We want to display a warning message here if
    604         // REFERENCE_DATA_VERSION is out of date - so we
    605         // do not forget to update the value before GA.
    606         isNonReferenceTzdataVersion = TRUE;
    607         logln(UnicodeString("Warning: Active tzdata version (") + tzdataVer +
    608             ") does not match the reference tzdata version ("
    609             + REFERENCE_DATA_VERSION + ") for this test case data.");
    610     }
    611 
    612     // Note: useDaylightTime returns true if DST is observed
    613     // in the time zone in the current calendar year.  The test
    614     // data is valid for the date after the reference year below.
    615     // If system clock is before the year, some test cases may
    616     // fail.
    617     GregorianCalendar cal(*TimeZone::getGMT(), status);
    618     if (failure(status, "GregorianCalendar")) return;
    619     cal.set(TimeZoneTest::REFERENCE_YEAR, UCAL_JANUARY, 2); // day 2 in GMT
    620 
    621     UBool isDateBeforeReferenceYear = ucal_getNow() < cal.getTime(status);
    622     if (failure(status, "Calendar::getTime")) return;
    623     if (isDateBeforeReferenceYear) {
    624         logln("Warning: Past time is set to the system clock.  Some test cases may not return expected results.");
    625     }
    626 
    627     int32_t i;
    628     // Create a small struct to hold the array
    629     struct
    630     {
    631         const char *id;
    632         int32_t    offset;
    633         UBool      daylight;
    634     }
    635     kReferenceList [] =
    636     {
    637         {"MIT", -660, FALSE},
    638         {"HST", -600, FALSE},
    639         {"AST", -540, TRUE},
    640         {"PST", -480, TRUE},
    641         {"PNT", -420, FALSE},
    642         {"MST", -420, FALSE}, // updated Aug 2003 aliu
    643         {"CST", -360, TRUE},
    644         {"IET", -300, TRUE},  // updated Jan 2006 srl
    645         {"EST", -300, FALSE}, // updated Aug 2003 aliu
    646         {"PRT", -240, FALSE},
    647         {"CNT", -210, TRUE},
    648         {"AGT", -180, TRUE}, // updated by tzdata2007k
    649         {"BET", -180, TRUE},
    650         {"GMT", 0, FALSE},
    651         {"UTC", 0, FALSE}, // ** srl: seems broken in C++
    652         {"ECT", 60, TRUE},
    653         {"MET", 60, TRUE}, // updated 12/3/99 aliu
    654         {"ART", 120, TRUE},
    655         {"EET", 120, TRUE},
    656         {"CAT", 120, FALSE}, // Africa/Harare
    657         {"EAT", 180, FALSE},
    658         {"NET", 240, TRUE}, // updated 12/3/99 aliu
    659         {"PLT", 300, FALSE}, // updated by 2008c - no DST after 2008
    660         {"IST", 330, FALSE},
    661         {"BST", 360, FALSE},
    662         {"VST", 420, FALSE},
    663         {"CTT", 480, FALSE}, // updated Aug 2003 aliu
    664         {"JST", 540, FALSE},
    665         {"ACT", 570, FALSE}, // updated Aug 2003 aliu
    666         {"AET", 600, TRUE},
    667         {"SST", 660, FALSE},
    668         {"NST", 720, TRUE}, // Pacific/Auckland
    669 
    670         // From icuzones:
    671         {"Etc/Unknown", 0, FALSE},
    672 
    673         {"SystemV/AST4ADT", -240, TRUE},
    674         {"SystemV/EST5EDT", -300, TRUE},
    675         {"SystemV/CST6CDT", -360, TRUE},
    676         {"SystemV/MST7MDT", -420, TRUE},
    677         {"SystemV/PST8PDT", -480, TRUE},
    678         {"SystemV/YST9YDT", -540, TRUE},
    679         {"SystemV/AST4", -240, FALSE},
    680         {"SystemV/EST5", -300, FALSE},
    681         {"SystemV/CST6", -360, FALSE},
    682         {"SystemV/MST7", -420, FALSE},
    683         {"SystemV/PST8", -480, FALSE},
    684         {"SystemV/YST9", -540, FALSE},
    685         {"SystemV/HST10", -600, FALSE},
    686 
    687         {"",0,FALSE}
    688     };
    689 
    690     for(i=0;kReferenceList[i].id[0];i++) {
    691         UnicodeString itsID(kReferenceList[i].id);
    692         UBool ok = TRUE;
    693         // Check existence.
    694         TimeZone *tz = TimeZone::createTimeZone(itsID);
    695         if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) {
    696             errln("FAIL: Time Zone " + itsID + " does not exist!");
    697             continue;
    698         }
    699 
    700         // Check daylight usage.
    701         UBool usesDaylight = tz->useDaylightTime();
    702         if (usesDaylight != kReferenceList[i].daylight) {
    703             if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
    704                 logln("Warning: Time Zone " + itsID + " use daylight is " +
    705                       (usesDaylight?"TRUE":"FALSE") +
    706                       " but it should be " +
    707                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
    708             } else {
    709                 errln("FAIL: Time Zone " + itsID + " use daylight is " +
    710                       (usesDaylight?"TRUE":"FALSE") +
    711                       " but it should be " +
    712                       ((kReferenceList[i].daylight)?"TRUE":"FALSE"));
    713             }
    714             ok = FALSE;
    715         }
    716 
    717         // Check offset
    718         int32_t offsetInMinutes = tz->getRawOffset()/60000;
    719         if (offsetInMinutes != kReferenceList[i].offset) {
    720             if (isNonReferenceTzdataVersion || isDateBeforeReferenceYear) {
    721                 logln("FAIL: Time Zone " + itsID + " raw offset is " +
    722                       offsetInMinutes +
    723                       " but it should be " + kReferenceList[i].offset);
    724             } else {
    725                 errln("FAIL: Time Zone " + itsID + " raw offset is " +
    726                       offsetInMinutes +
    727                       " but it should be " + kReferenceList[i].offset);
    728             }
    729             ok = FALSE;
    730         }
    731 
    732         if (ok) {
    733             logln("OK: " + itsID +
    734                   " useDaylightTime() & getRawOffset() as expected");
    735         }
    736         delete tz;
    737     }
    738 
    739 
    740     // OK now test compat
    741     logln("Testing for compatibility zones");
    742 
    743     const char* compatibilityMap[] = {
    744         // This list is copied from tz.alias.  If tz.alias
    745         // changes, this list must be updated.  Current as of Mar 2007
    746         "ACT", "Australia/Darwin",
    747         "AET", "Australia/Sydney",
    748         "AGT", "America/Buenos_Aires",
    749         "ART", "Africa/Cairo",
    750         "AST", "America/Anchorage",
    751         "BET", "America/Sao_Paulo",
    752         "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca
    753         "CAT", "Africa/Harare",
    754         "CNT", "America/St_Johns",
    755         "CST", "America/Chicago",
    756         "CTT", "Asia/Shanghai",
    757         "EAT", "Africa/Addis_Ababa",
    758         "ECT", "Europe/Paris",
    759         // EET Europe/Istanbul # EET is a standard UNIX zone
    760         // "EST", "America/New_York", # Defined as -05:00
    761         // "HST", "Pacific/Honolulu", # Defined as -10:00
    762         "IET", "America/Indianapolis",
    763         "IST", "Asia/Calcutta",
    764         "JST", "Asia/Tokyo",
    765         // MET Asia/Tehran # MET is a standard UNIX zone
    766         "MIT", "Pacific/Apia",
    767         // "MST", "America/Denver", # Defined as -07:00
    768         "NET", "Asia/Yerevan",
    769         "NST", "Pacific/Auckland",
    770         "PLT", "Asia/Karachi",
    771         "PNT", "America/Phoenix",
    772         "PRT", "America/Puerto_Rico",
    773         "PST", "America/Los_Angeles",
    774         "SST", "Pacific/Guadalcanal",
    775         "UTC", "Etc/GMT",
    776         "VST", "Asia/Saigon",
    777          "","",""
    778     };
    779 
    780     for (i=0;*compatibilityMap[i];i+=2) {
    781         UnicodeString itsID;
    782 
    783         const char *zone1 = compatibilityMap[i];
    784         const char *zone2 = compatibilityMap[i+1];
    785 
    786         TimeZone *tz1 = TimeZone::createTimeZone(zone1);
    787         TimeZone *tz2 = TimeZone::createTimeZone(zone2);
    788 
    789         if (!tz1) {
    790             errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1);
    791         }
    792         if (!tz2) {
    793             errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2);
    794         }
    795 
    796         if (tz1 && tz2) {
    797             // make NAME same so comparison will only look at the rest
    798             tz2->setID(tz1->getID(itsID));
    799 
    800             if (*tz1 != *tz2) {
    801                 errln("FAIL: " + UnicodeString(zone1) +
    802                       " != " + UnicodeString(zone2));
    803             } else {
    804                 logln("OK: " + UnicodeString(zone1) +
    805                       " == " + UnicodeString(zone2));
    806             }
    807         }
    808 
    809         delete tz1;
    810         delete tz2;
    811     }
    812 }
    813 
    814 
    815 /**
    816  * Utility function for TestCustomParse
    817  */
    818 UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) {
    819     rv.remove();
    820     UChar sign = 0x002B;
    821     if (offset < 0) {
    822         sign = 0x002D;
    823         offset = -offset;
    824     }
    825 
    826     int32_t s = offset % 60;
    827     offset /= 60;
    828     int32_t m = offset % 60;
    829     int32_t h = offset / 60;
    830 
    831     rv += (UChar)(sign);
    832     if (h >= 10) {
    833         rv += (UChar)(0x0030 + (h/10));
    834     } else {
    835         rv += (UChar)0x0030;
    836     }
    837     rv += (UChar)(0x0030 + (h%10));
    838 
    839     rv += (UChar)0x003A; /* ':' */
    840     if (m >= 10) {
    841         rv += (UChar)(0x0030 + (m/10));
    842     } else {
    843         rv += (UChar)0x0030;
    844     }
    845     rv += (UChar)(0x0030 + (m%10));
    846 
    847     if (s) {
    848         rv += (UChar)0x003A; /* ':' */
    849         if (s >= 10) {
    850             rv += (UChar)(0x0030 + (s/10));
    851         } else {
    852             rv += (UChar)0x0030;
    853         }
    854         rv += (UChar)(0x0030 + (s%10));
    855     }
    856     return rv;
    857 }
    858 
    859 /**
    860  * Utility function for TestCustomParse, generating time zone ID
    861  * string for the give offset.
    862  */
    863 UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) {
    864     rv.remove();
    865     UChar sign = 0x002B;
    866     if (offset < 0) {
    867         sign = 0x002D;
    868         offset = -offset;
    869     }
    870 
    871     int32_t s = offset % 60;
    872     offset /= 60;
    873     int32_t m = offset % 60;
    874     int32_t h = offset / 60;
    875 
    876     rv += "GMT";
    877     rv += (UChar)(sign);
    878     if (h >= 10) {
    879         rv += (UChar)(0x0030 + (h/10));
    880     } else {
    881         rv += (UChar)0x0030;
    882     }
    883     rv += (UChar)(0x0030 + (h%10));
    884     rv += (UChar)0x003A;
    885     if (m >= 10) {
    886         rv += (UChar)(0x0030 + (m/10));
    887     } else {
    888         rv += (UChar)0x0030;
    889     }
    890     rv += (UChar)(0x0030 + (m%10));
    891 
    892     if (s) {
    893         rv += (UChar)0x003A;
    894         if (s >= 10) {
    895             rv += (UChar)(0x0030 + (s/10));
    896         } else {
    897             rv += (UChar)0x0030;
    898         }
    899         rv += (UChar)(0x0030 + (s%10));
    900     }
    901     return rv;
    902 }
    903 
    904 /**
    905  * As part of the VM fix (see CCC approved RFE 4028006, bug
    906  * 4044013), TimeZone.getTimeZone() has been modified to recognize
    907  * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and
    908  * GMT[+-]hh.  Test this behavior here.
    909  *
    910  * @bug 4044013
    911  */
    912 void TimeZoneTest::TestCustomParse()
    913 {
    914     int32_t i;
    915     const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be.
    916 
    917     struct
    918     {
    919         const char *customId;
    920         int32_t expectedOffset;
    921     }
    922     kData[] =
    923     {
    924         // ID        Expected offset in seconds
    925         {"GMT",       kUnparseable},   //Isn't custom. [returns normal GMT]
    926         {"GMT-YOUR.AD.HERE", kUnparseable},
    927         {"GMT0",      kUnparseable},
    928         {"GMT+0",     (0)},
    929         {"GMT+1",     (1*60*60)},
    930         {"GMT-0030",  (-30*60)},
    931         {"GMT+15:99", kUnparseable},
    932         {"GMT+",      kUnparseable},
    933         {"GMT-",      kUnparseable},
    934         {"GMT+0:",    kUnparseable},
    935         {"GMT-:",     kUnparseable},
    936         {"GMT-YOUR.AD.HERE",    kUnparseable},
    937         {"GMT+0010",  (10*60)}, // Interpret this as 00:10
    938         {"GMT-10",    (-10*60*60)},
    939         {"GMT+30",    kUnparseable},
    940         {"GMT-3:30",  (-(3*60+30)*60)},
    941         {"GMT-230",   (-(2*60+30)*60)},
    942         {"GMT+05:13:05",    ((5*60+13)*60+5)},
    943         {"GMT-71023",       (-((7*60+10)*60+23))},
    944         {"GMT+01:23:45:67", kUnparseable},
    945         {"GMT+01:234",      kUnparseable},
    946         {"GMT-2:31:123",    kUnparseable},
    947         {"GMT+3:75",        kUnparseable},
    948         {"GMT-01010101",    kUnparseable},
    949         {0,           0}
    950     };
    951 
    952     for (i=0; kData[i].customId != 0; i++) {
    953         UnicodeString id(kData[i].customId);
    954         int32_t exp = kData[i].expectedOffset;
    955         TimeZone *zone = TimeZone::createTimeZone(id);
    956         UnicodeString   itsID, temp;
    957 
    958         if (zone->getDynamicClassID() == OlsonTimeZone::getStaticClassID()) {
    959             logln(id + " -> Olson time zone");
    960         } else {
    961             zone->getID(itsID);
    962             int32_t ioffset = zone->getRawOffset()/1000;
    963             UnicodeString offset, expectedID;
    964             formatOffset(ioffset, offset);
    965             formatTZID(ioffset, expectedID);
    966             logln(id + " -> " + itsID + " " + offset);
    967             if (exp == kUnparseable && itsID != "GMT") {
    968                 errln("Expected parse failure for " + id +
    969                       ", got offset of " + offset +
    970                       ", id " + itsID);
    971             }
    972             // JDK 1.3 creates custom zones with the ID "Custom"
    973             // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00"
    974             // ICU creates custom zones with IDs of the form "GMT+02:00"
    975             else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) {
    976                 dataerrln("Expected offset of " + formatOffset(exp, temp) +
    977                       ", id " + expectedID +
    978                       ", for " + id +
    979                       ", got offset of " + offset +
    980                       ", id " + itsID);
    981             }
    982         }
    983         delete zone;
    984     }
    985 }
    986 
    987 void
    988 TimeZoneTest::TestAliasedNames()
    989 {
    990     struct {
    991         const char *from;
    992         const char *to;
    993     } kData[] = {
    994         /* Generated by org.unicode.cldr.tool.CountItems */
    995 
    996         /* zoneID, canonical zoneID */
    997         {"Africa/Timbuktu", "Africa/Bamako"},
    998         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"},
    999         {"America/Argentina/Catamarca", "America/Catamarca"},
   1000         {"America/Argentina/ComodRivadavia", "America/Catamarca"},
   1001         {"America/Argentina/Cordoba", "America/Cordoba"},
   1002         {"America/Argentina/Jujuy", "America/Jujuy"},
   1003         {"America/Argentina/Mendoza", "America/Mendoza"},
   1004         {"America/Atka", "America/Adak"},
   1005         {"America/Ensenada", "America/Tijuana"},
   1006         {"America/Fort_Wayne", "America/Indiana/Indianapolis"},
   1007         {"America/Indianapolis", "America/Indiana/Indianapolis"},
   1008         {"America/Knox_IN", "America/Indiana/Knox"},
   1009         {"America/Louisville", "America/Kentucky/Louisville"},
   1010         {"America/Porto_Acre", "America/Rio_Branco"},
   1011         {"America/Rosario", "America/Cordoba"},
   1012         {"America/Virgin", "America/St_Thomas"},
   1013         {"Asia/Ashkhabad", "Asia/Ashgabat"},
   1014         {"Asia/Chungking", "Asia/Chongqing"},
   1015         {"Asia/Dacca", "Asia/Dhaka"},
   1016         {"Asia/Istanbul", "Europe/Istanbul"},
   1017         {"Asia/Macao", "Asia/Macau"},
   1018         {"Asia/Tel_Aviv", "Asia/Jerusalem"},
   1019         {"Asia/Thimbu", "Asia/Thimphu"},
   1020         {"Asia/Ujung_Pandang", "Asia/Makassar"},
   1021         {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"},
   1022         {"Australia/ACT", "Australia/Sydney"},
   1023         {"Australia/Canberra", "Australia/Sydney"},
   1024         {"Australia/LHI", "Australia/Lord_Howe"},
   1025         {"Australia/NSW", "Australia/Sydney"},
   1026         {"Australia/North", "Australia/Darwin"},
   1027         {"Australia/Queensland", "Australia/Brisbane"},
   1028         {"Australia/South", "Australia/Adelaide"},
   1029         {"Australia/Tasmania", "Australia/Hobart"},
   1030         {"Australia/Victoria", "Australia/Melbourne"},
   1031         {"Australia/West", "Australia/Perth"},
   1032         {"Australia/Yancowinna", "Australia/Broken_Hill"},
   1033         {"Brazil/Acre", "America/Rio_Branco"},
   1034         {"Brazil/DeNoronha", "America/Noronha"},
   1035         {"Brazil/East", "America/Sao_Paulo"},
   1036         {"Brazil/West", "America/Manaus"},
   1037         {"Canada/Atlantic", "America/Halifax"},
   1038         {"Canada/Central", "America/Winnipeg"},
   1039         {"Canada/East-Saskatchewan", "America/Regina"},
   1040         {"Canada/Eastern", "America/Toronto"},
   1041         {"Canada/Mountain", "America/Edmonton"},
   1042         {"Canada/Newfoundland", "America/St_Johns"},
   1043         {"Canada/Pacific", "America/Vancouver"},
   1044         {"Canada/Saskatchewan", "America/Regina"},
   1045         {"Canada/Yukon", "America/Whitehorse"},
   1046         {"Chile/Continental", "America/Santiago"},
   1047         {"Chile/EasterIsland", "Pacific/Easter"},
   1048         {"Cuba", "America/Havana"},
   1049         {"Egypt", "Africa/Cairo"},
   1050         {"Eire", "Europe/Dublin"},
   1051         {"Etc/GMT+0", "Etc/GMT"},
   1052         {"Etc/GMT-0", "Etc/GMT"},
   1053         {"Etc/GMT0", "Etc/GMT"},
   1054         {"Etc/Greenwich", "Etc/GMT"},
   1055         {"Etc/UCT", "Etc/GMT"},
   1056         {"Etc/UTC", "Etc/GMT"},
   1057         {"Etc/Universal", "Etc/GMT"},
   1058         {"Etc/Zulu", "Etc/GMT"},
   1059         {"Europe/Belfast", "Europe/London"},
   1060         {"Europe/Nicosia", "Asia/Nicosia"},
   1061         {"Europe/Tiraspol", "Europe/Chisinau"},
   1062         {"GB", "Europe/London"},
   1063         {"GB-Eire", "Europe/London"},
   1064         {"GMT", "Etc/GMT"},
   1065         {"GMT+0", "Etc/GMT"},
   1066         {"GMT-0", "Etc/GMT"},
   1067         {"GMT0", "Etc/GMT"},
   1068         {"Greenwich", "Etc/GMT"},
   1069         {"Hongkong", "Asia/Hong_Kong"},
   1070         {"Iceland", "Atlantic/Reykjavik"},
   1071         {"Iran", "Asia/Tehran"},
   1072         {"Israel", "Asia/Jerusalem"},
   1073         {"Jamaica", "America/Jamaica"},
   1074         {"Japan", "Asia/Tokyo"},
   1075         {"Kwajalein", "Pacific/Kwajalein"},
   1076         {"Libya", "Africa/Tripoli"},
   1077         {"Mexico/BajaNorte", "America/Tijuana"},
   1078         {"Mexico/BajaSur", "America/Mazatlan"},
   1079         {"Mexico/General", "America/Mexico_City"},
   1080         {"NZ", "Pacific/Auckland"},
   1081         {"NZ-CHAT", "Pacific/Chatham"},
   1082         {"Navajo", "America/Shiprock"},
   1083         {"PRC", "Asia/Shanghai"},
   1084         {"Pacific/Samoa", "Pacific/Pago_Pago"},
   1085         {"Pacific/Yap", "Pacific/Truk"},
   1086         {"Poland", "Europe/Warsaw"},
   1087         {"Portugal", "Europe/Lisbon"},
   1088         {"ROC", "Asia/Taipei"},
   1089         {"ROK", "Asia/Seoul"},
   1090         {"Singapore", "Asia/Singapore"},
   1091         {"Turkey", "Europe/Istanbul"},
   1092         {"UCT", "Etc/GMT"},
   1093         {"US/Alaska", "America/Anchorage"},
   1094         {"US/Aleutian", "America/Adak"},
   1095         {"US/Arizona", "America/Phoenix"},
   1096         {"US/Central", "America/Chicago"},
   1097         {"US/East-Indiana", "America/Indiana/Indianapolis"},
   1098         {"US/Eastern", "America/New_York"},
   1099         {"US/Hawaii", "Pacific/Honolulu"},
   1100         {"US/Indiana-Starke", "America/Indiana/Knox"},
   1101         {"US/Michigan", "America/Detroit"},
   1102         {"US/Mountain", "America/Denver"},
   1103         {"US/Pacific", "America/Los_Angeles"},
   1104         {"US/Pacific-New", "America/Los_Angeles"},
   1105         {"US/Samoa", "Pacific/Pago_Pago"},
   1106         {"UTC", "Etc/GMT"},
   1107         {"Universal", "Etc/GMT"},
   1108         {"W-SU", "Europe/Moscow"},
   1109         {"Zulu", "Etc/GMT"},
   1110         /* Total: 113 */
   1111 
   1112     };
   1113 
   1114     TimeZone::EDisplayType styles[] = { TimeZone::SHORT, TimeZone::LONG };
   1115     UBool useDst[] = { FALSE, TRUE };
   1116     int32_t noLoc = uloc_countAvailable();
   1117 
   1118     int32_t i, j, k, loc;
   1119     UnicodeString fromName, toName;
   1120     TimeZone *from = NULL, *to = NULL;
   1121     for(i = 0; i < (int32_t)(sizeof(kData)/sizeof(kData[0])); i++) {
   1122         from = TimeZone::createTimeZone(kData[i].from);
   1123         to = TimeZone::createTimeZone(kData[i].to);
   1124         if(!from->hasSameRules(*to)) {
   1125             errln("different at %i\n", i);
   1126         }
   1127         if(!quick) {
   1128             for(loc = 0; loc < noLoc; loc++) {
   1129                 const char* locale = uloc_getAvailable(loc);
   1130                 for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) {
   1131                     for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); k++) {
   1132                         fromName.remove();
   1133                         toName.remove();
   1134                         from->getDisplayName(useDst[k], styles[j],locale, fromName);
   1135                         to->getDisplayName(useDst[k], styles[j], locale, toName);
   1136                         if(fromName.compare(toName) != 0) {
   1137                             errln("Fail: Expected "+toName+" but got " + prettify(fromName)
   1138                                 + " for locale: " + locale + " index: "+ loc
   1139                                 + " to id "+ kData[i].to
   1140                                 + " from id " + kData[i].from);
   1141                         }
   1142                     }
   1143                 }
   1144             }
   1145         } else {
   1146             fromName.remove();
   1147             toName.remove();
   1148             from->getDisplayName(fromName);
   1149             to->getDisplayName(toName);
   1150             if(fromName.compare(toName) != 0) {
   1151                 errln("Fail: Expected "+toName+" but got " + fromName);
   1152             }
   1153         }
   1154         delete from;
   1155         delete to;
   1156     }
   1157 }
   1158 
   1159 /**
   1160  * Test the basic functionality of the getDisplayName() API.
   1161  *
   1162  * @bug 4112869
   1163  * @bug 4028006
   1164  *
   1165  * See also API change request A41.
   1166  *
   1167  * 4/21/98 - make smarter, so the test works if the ext resources
   1168  * are present or not.
   1169  */
   1170 void
   1171 TimeZoneTest::TestDisplayName()
   1172 {
   1173     UErrorCode status = U_ZERO_ERROR;
   1174     int32_t i;
   1175     TimeZone *zone = TimeZone::createTimeZone("PST");
   1176     UnicodeString name;
   1177     zone->getDisplayName(Locale::getEnglish(), name);
   1178     logln("PST->" + name);
   1179     if (name.compare("Pacific Standard Time") != 0)
   1180         dataerrln("Fail: Expected \"Pacific Standard Time\" but got " + name);
   1181 
   1182     //*****************************************************************
   1183     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1184     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1185     // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1186     //*****************************************************************
   1187     struct
   1188     {
   1189         UBool useDst;
   1190         TimeZone::EDisplayType style;
   1191         const char *expect;
   1192     } kData[] = {
   1193         {FALSE, TimeZone::SHORT, "PST"},
   1194         {TRUE,  TimeZone::SHORT, "PDT"},
   1195         {FALSE, TimeZone::LONG,  "Pacific Standard Time"},
   1196         {TRUE,  TimeZone::LONG,  "Pacific Daylight Time"},
   1197 
   1198         {FALSE, TimeZone::SHORT_GENERIC, "PT"},
   1199         {TRUE,  TimeZone::SHORT_GENERIC, "PT"},
   1200         {FALSE, TimeZone::LONG_GENERIC,  "Pacific Time"},
   1201         {TRUE,  TimeZone::LONG_GENERIC,  "Pacific Time"},
   1202 
   1203         {FALSE, TimeZone::SHORT_GMT, "-0800"},
   1204         {TRUE,  TimeZone::SHORT_GMT, "-0700"},
   1205         {FALSE, TimeZone::LONG_GMT,  "GMT-08:00"},
   1206         {TRUE,  TimeZone::LONG_GMT,  "GMT-07:00"},
   1207 
   1208         {FALSE, TimeZone::SHORT_COMMONLY_USED, "PST"},
   1209         {TRUE,  TimeZone::SHORT_COMMONLY_USED, "PDT"},
   1210         {FALSE, TimeZone::GENERIC_LOCATION,  "United States (Los Angeles)"},
   1211         {TRUE,  TimeZone::GENERIC_LOCATION,  "United States (Los Angeles)"},
   1212 
   1213         {FALSE, TimeZone::LONG, ""}
   1214     };
   1215 
   1216     for (i=0; kData[i].expect[0] != '\0'; i++)
   1217     {
   1218         name.remove();
   1219         name = zone->getDisplayName(kData[i].useDst,
   1220                                    kData[i].style,
   1221                                    Locale::getEnglish(), name);
   1222         if (name.compare(kData[i].expect) != 0)
   1223             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
   1224         logln("PST [with options]->" + name);
   1225     }
   1226     for (i=0; kData[i].expect[0] != '\0'; i++)
   1227     {
   1228         name.remove();
   1229         name = zone->getDisplayName(kData[i].useDst,
   1230                                    kData[i].style, name);
   1231         if (name.compare(kData[i].expect) != 0)
   1232             dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name);
   1233         logln("PST [with options]->" + name);
   1234     }
   1235 
   1236 
   1237     // Make sure that we don't display the DST name by constructing a fake
   1238     // PST zone that has DST all year long.
   1239     SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST");
   1240 
   1241     zone2->setStartRule(UCAL_JANUARY, 1, 0, 0, status);
   1242     zone2->setEndRule(UCAL_DECEMBER, 31, 0, 0, status);
   1243 
   1244     UnicodeString inDaylight;
   1245     if (zone2->inDaylightTime(UDate(0), status)) {
   1246         inDaylight = UnicodeString("TRUE");
   1247     } else {
   1248         inDaylight = UnicodeString("FALSE");
   1249     }
   1250     logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight );
   1251     if(U_FAILURE(status))
   1252     {
   1253         dataerrln("Some sort of error..." + UnicodeString(u_errorName(status))); // REVISIT
   1254     }
   1255     name.remove();
   1256     name = zone2->getDisplayName(Locale::getEnglish(),name);
   1257     logln("Modified PST->" + name);
   1258     if (name.compare("Pacific Standard Time") != 0)
   1259         dataerrln("Fail: Expected \"Pacific Standard Time\"");
   1260 
   1261     // Make sure we get the default display format for Locales
   1262     // with no display name data.
   1263     Locale mt_MT("mt_MT");
   1264     name.remove();
   1265     name = zone->getDisplayName(mt_MT,name);
   1266     //*****************************************************************
   1267     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1268     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1269     // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES
   1270     //*****************************************************************
   1271     logln("PST(mt_MT)->" + name);
   1272 
   1273     // *** REVISIT SRL how in the world do I check this? looks java specific.
   1274     // Now be smart -- check to see if zh resource is even present.
   1275     // If not, we expect the en fallback behavior.
   1276     ResourceBundle enRB(NULL,
   1277                             Locale::getEnglish(), status);
   1278     if(U_FAILURE(status))
   1279         dataerrln("Couldn't get ResourceBundle for en - %s", u_errorName(status));
   1280 
   1281     ResourceBundle mtRB(NULL,
   1282                          mt_MT, status);
   1283     //if(U_FAILURE(status))
   1284     //    errln("Couldn't get ResourceBundle for mt_MT");
   1285 
   1286     UBool noZH = U_FAILURE(status);
   1287 
   1288     if (noZH) {
   1289         logln("Warning: Not testing the mt_MT behavior because resource is absent");
   1290         if (name != "Pacific Standard Time")
   1291             dataerrln("Fail: Expected Pacific Standard Time");
   1292     }
   1293 
   1294 
   1295     if      (name.compare("GMT-08:00") &&
   1296              name.compare("GMT-8:00") &&
   1297              name.compare("GMT-0800") &&
   1298              name.compare("GMT-800")) {
   1299       dataerrln(UnicodeString("Fail: Expected GMT-08:00 or something similar for PST in mt_MT but got ") + name );
   1300         dataerrln("************************************************************");
   1301         dataerrln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED");
   1302         dataerrln("************************************************************");
   1303     }
   1304 
   1305     // Now try a non-existent zone
   1306     delete zone2;
   1307     zone2 = new SimpleTimeZone(90*60*1000, "xyzzy");
   1308     name.remove();
   1309     name = zone2->getDisplayName(Locale::getEnglish(),name);
   1310     logln("GMT+90min->" + name);
   1311     if (name.compare("GMT+01:30") &&
   1312         name.compare("GMT+1:30") &&
   1313         name.compare("GMT+0130") &&
   1314         name.compare("GMT+130"))
   1315         dataerrln("Fail: Expected GMT+01:30 or something similar");
   1316     name.truncate(0);
   1317     zone2->getDisplayName(name);
   1318     logln("GMT+90min->" + name);
   1319     if (name.compare("GMT+01:30") &&
   1320         name.compare("GMT+1:30") &&
   1321         name.compare("GMT+0130") &&
   1322         name.compare("GMT+130"))
   1323         dataerrln("Fail: Expected GMT+01:30 or something similar");
   1324     // clean up
   1325     delete zone;
   1326     delete zone2;
   1327 }
   1328 
   1329 /**
   1330  * @bug 4107276
   1331  */
   1332 void
   1333 TimeZoneTest::TestDSTSavings()
   1334 {
   1335     UErrorCode status = U_ZERO_ERROR;
   1336     // It might be better to find a way to integrate this test into the main TimeZone
   1337     // tests above, but I don't have time to figure out how to do this (or if it's
   1338     // even really a good idea).  Let's consider that a future.  --rtg 1/27/98
   1339     SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest",
   1340                                            UCAL_MARCH, 1, 0, 0, UCAL_SEPTEMBER, 1, 0, 0,
   1341                                            (int32_t)(0.5 * U_MILLIS_PER_HOUR), status);
   1342     if(U_FAILURE(status))
   1343         errln("couldn't create TimeZone");
   1344 
   1345     if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR)
   1346         errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) +
   1347               " hours instead of -5 hours.");
   1348     if (!tz->useDaylightTime())
   1349         errln("Test time zone should use DST but claims it doesn't.");
   1350     if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR)
   1351         errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() /
   1352                                                              U_MILLIS_PER_HOUR) + " hours instead.");
   1353 
   1354     int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
   1355                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1356     if (offset != -5 * U_MILLIS_PER_HOUR)
   1357         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
   1358               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1359 
   1360     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
   1361                           10 * U_MILLIS_PER_HOUR,status);
   1362     if (offset != -4.5 * U_MILLIS_PER_HOUR)
   1363         errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ")
   1364               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1365 
   1366     tz->setDSTSavings(U_MILLIS_PER_HOUR, status);
   1367     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1,
   1368                           UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1369     if (offset != -5 * U_MILLIS_PER_HOUR)
   1370         errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ")
   1371               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1372 
   1373     offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY,
   1374                           10 * U_MILLIS_PER_HOUR,status);
   1375     if (offset != -4 * U_MILLIS_PER_HOUR)
   1376         errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ")
   1377               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1378 
   1379     delete tz;
   1380 }
   1381 
   1382 /**
   1383  * @bug 4107570
   1384  */
   1385 void
   1386 TimeZoneTest::TestAlternateRules()
   1387 {
   1388     // Like TestDSTSavings, this test should probably be integrated somehow with the main
   1389     // test at the top of this class, but I didn't have time to figure out how to do that.
   1390     //                      --rtg 1/28/98
   1391 
   1392     SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest");
   1393 
   1394     // test the day-of-month API
   1395     UErrorCode status = U_ZERO_ERROR;
   1396     tz.setStartRule(UCAL_MARCH, 10, 12 * U_MILLIS_PER_HOUR, status);
   1397     if(U_FAILURE(status))
   1398         errln("tz.setStartRule failed");
   1399     tz.setEndRule(UCAL_OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status);
   1400     if(U_FAILURE(status))
   1401         errln("tz.setStartRule failed");
   1402 
   1403     int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 5,
   1404                               UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status);
   1405     if (offset != -5 * U_MILLIS_PER_HOUR)
   1406         errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ")
   1407               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1408 
   1409     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 15,
   1410                           UCAL_SUNDAY, 10 * millisPerHour,status);
   1411     if (offset != -4 * U_MILLIS_PER_HOUR)
   1412         errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ")
   1413               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1414 
   1415     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
   1416                           UCAL_THURSDAY, 10 * millisPerHour,status);
   1417     if (offset != -4 * U_MILLIS_PER_HOUR)
   1418         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")              + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1419 
   1420     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 25,
   1421                           UCAL_SUNDAY, 10 * millisPerHour,status);
   1422     if (offset != -5 * U_MILLIS_PER_HOUR)
   1423         errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ")
   1424               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1425 
   1426     // test the day-of-week-after-day-in-month API
   1427     tz.setStartRule(UCAL_MARCH, 10, UCAL_FRIDAY, 12 * millisPerHour, TRUE, status);
   1428     if(U_FAILURE(status))
   1429         errln("tz.setStartRule failed");
   1430     tz.setEndRule(UCAL_OCTOBER, 20, UCAL_FRIDAY, 12 * millisPerHour, FALSE, status);
   1431     if(U_FAILURE(status))
   1432         errln("tz.setStartRule failed");
   1433 
   1434     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 11,
   1435                           UCAL_WEDNESDAY, 10 * millisPerHour,status);
   1436     if (offset != -5 * U_MILLIS_PER_HOUR)
   1437         errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ")
   1438               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1439 
   1440     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 14,
   1441                           UCAL_SATURDAY, 10 * millisPerHour,status);
   1442     if (offset != -4 * U_MILLIS_PER_HOUR)
   1443         errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ")
   1444               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1445 
   1446     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15,
   1447                           UCAL_THURSDAY, 10 * millisPerHour,status);
   1448     if (offset != -4 * U_MILLIS_PER_HOUR)
   1449         errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ")
   1450               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1451 
   1452     offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 17,
   1453                           UCAL_SATURDAY, 10 * millisPerHour,status);
   1454     if (offset != -5 * U_MILLIS_PER_HOUR)
   1455         errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ")
   1456               + (offset / U_MILLIS_PER_HOUR) + " hours.");
   1457 }
   1458 
   1459 void TimeZoneTest::TestFractionalDST() {
   1460     const char* tzName = "Australia/Lord_Howe"; // 30 min offset
   1461     TimeZone* tz_icu = TimeZone::createTimeZone(tzName);
   1462 	int dst_icu = tz_icu->getDSTSavings();
   1463     UnicodeString id;
   1464     int32_t expected = 1800000;
   1465 	if (expected != dst_icu) {
   1466 	    dataerrln(UnicodeString("java reports dst savings of ") + expected +
   1467 	        " but icu reports " + dst_icu +
   1468 	        " for tz " + tz_icu->getID(id));
   1469 	} else {
   1470 	    logln(UnicodeString("both java and icu report dst savings of ") + expected + " for tz " + tz_icu->getID(id));
   1471 	}
   1472     delete tz_icu;
   1473 }
   1474 
   1475 /**
   1476  * Test country code support.  Jitterbug 776.
   1477  */
   1478 void TimeZoneTest::TestCountries() {
   1479     // Make sure America/Los_Angeles is in the "US" group, and
   1480     // Asia/Tokyo isn't.  Vice versa for the "JP" group.
   1481     UErrorCode ec = U_ZERO_ERROR;
   1482     int32_t n;
   1483     StringEnumeration* s = TimeZone::createEnumeration("US");
   1484     n = s->count(ec);
   1485     UBool la = FALSE, tokyo = FALSE;
   1486     UnicodeString laZone("America/Los_Angeles", "");
   1487     UnicodeString tokyoZone("Asia/Tokyo", "");
   1488     int32_t i;
   1489 
   1490     if (s == NULL || n <= 0) {
   1491         dataerrln("FAIL: TimeZone::createEnumeration() returned nothing");
   1492         return;
   1493     }
   1494     for (i=0; i<n; ++i) {
   1495         const UnicodeString* id = s->snext(ec);
   1496         if (*id == (laZone)) {
   1497             la = TRUE;
   1498         }
   1499         if (*id == (tokyoZone)) {
   1500             tokyo = TRUE;
   1501         }
   1502     }
   1503     if (!la || tokyo) {
   1504         errln("FAIL: " + laZone + " in US = " + la);
   1505         errln("FAIL: " + tokyoZone + " in US = " + tokyo);
   1506     }
   1507     delete s;
   1508 
   1509     s = TimeZone::createEnumeration("JP");
   1510     n = s->count(ec);
   1511     la = FALSE; tokyo = FALSE;
   1512 
   1513     for (i=0; i<n; ++i) {
   1514         const UnicodeString* id = s->snext(ec);
   1515         if (*id == (laZone)) {
   1516             la = TRUE;
   1517         }
   1518         if (*id == (tokyoZone)) {
   1519             tokyo = TRUE;
   1520         }
   1521     }
   1522     if (la || !tokyo) {
   1523         errln("FAIL: " + laZone + " in JP = " + la);
   1524         errln("FAIL: " + tokyoZone + " in JP = " + tokyo);
   1525     }
   1526     StringEnumeration* s1 = TimeZone::createEnumeration("US");
   1527     StringEnumeration* s2 = TimeZone::createEnumeration("US");
   1528     for(i=0;i<n;++i){
   1529         const UnicodeString* id1 = s1->snext(ec);
   1530         if(id1==NULL || U_FAILURE(ec)){
   1531             errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
   1532         }
   1533         TimeZone* tz1 = TimeZone::createTimeZone(*id1);
   1534         for(int j=0; j<n;++j){
   1535             const UnicodeString* id2 = s2->snext(ec);
   1536             if(id2==NULL || U_FAILURE(ec)){
   1537                 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i);
   1538             }
   1539             TimeZone* tz2 = TimeZone::createTimeZone(*id2);
   1540             if(tz1->hasSameRules(*tz2)){
   1541                 logln("ID1 : " + *id1+" == ID2 : " +*id2);
   1542             }
   1543             delete tz2;
   1544         }
   1545         delete tz1;
   1546     }
   1547     delete s1;
   1548     delete s2;
   1549     delete s;
   1550 }
   1551 
   1552 void TimeZoneTest::TestHistorical() {
   1553     const int32_t H = U_MILLIS_PER_HOUR;
   1554     struct {
   1555         const char* id;
   1556         int32_t time; // epoch seconds
   1557         int32_t offset; // total offset (millis)
   1558     } DATA[] = {
   1559         // Add transition points (before/after) as desired to test historical
   1560         // behavior.
   1561         {"America/Los_Angeles", 638963999, -8*H}, // Sun Apr 01 01:59:59 GMT-08:00 1990
   1562         {"America/Los_Angeles", 638964000, -7*H}, // Sun Apr 01 03:00:00 GMT-07:00 1990
   1563         {"America/Los_Angeles", 657104399, -7*H}, // Sun Oct 28 01:59:59 GMT-07:00 1990
   1564         {"America/Los_Angeles", 657104400, -8*H}, // Sun Oct 28 01:00:00 GMT-08:00 1990
   1565         {"America/Goose_Bay", -116445601, -4*H}, // Sun Apr 24 01:59:59 GMT-04:00 1966
   1566         {"America/Goose_Bay", -116445600, -3*H}, // Sun Apr 24 03:00:00 GMT-03:00 1966
   1567         {"America/Goose_Bay", -100119601, -3*H}, // Sun Oct 30 01:59:59 GMT-03:00 1966
   1568         {"America/Goose_Bay", -100119600, -4*H}, // Sun Oct 30 01:00:00 GMT-04:00 1966
   1569         {"America/Goose_Bay", -84391201, -4*H}, // Sun Apr 30 01:59:59 GMT-04:00 1967
   1570         {"America/Goose_Bay", -84391200, -3*H}, // Sun Apr 30 03:00:00 GMT-03:00 1967
   1571         {"America/Goose_Bay", -68670001, -3*H}, // Sun Oct 29 01:59:59 GMT-03:00 1967
   1572         {"America/Goose_Bay", -68670000, -4*H}, // Sun Oct 29 01:00:00 GMT-04:00 1967
   1573         {0, 0, 0}
   1574     };
   1575 
   1576     for (int32_t i=0; DATA[i].id!=0; ++i) {
   1577         const char* id = DATA[i].id;
   1578         TimeZone *tz = TimeZone::createTimeZone(id);
   1579         UnicodeString s;
   1580         if (tz == 0) {
   1581             errln("FAIL: Cannot create %s", id);
   1582         } else if (tz->getID(s) != UnicodeString(id)) {
   1583             dataerrln((UnicodeString)"FAIL: createTimeZone(" + id + ") => " + s);
   1584         } else {
   1585             UErrorCode ec = U_ZERO_ERROR;
   1586             int32_t raw, dst;
   1587             UDate when = (double) DATA[i].time * U_MILLIS_PER_SECOND;
   1588             tz->getOffset(when, FALSE, raw, dst, ec);
   1589             if (U_FAILURE(ec)) {
   1590                 errln("FAIL: getOffset");
   1591             } else if ((raw+dst) != DATA[i].offset) {
   1592                 errln((UnicodeString)"FAIL: " + DATA[i].id + ".getOffset(" +
   1593                       //when + " = " +
   1594                       dateToString(when) + ") => " +
   1595                       raw + ", " + dst);
   1596             } else {
   1597                 logln((UnicodeString)"Ok: " + DATA[i].id + ".getOffset(" +
   1598                       //when + " = " +
   1599                       dateToString(when) + ") => " +
   1600                       raw + ", " + dst);
   1601             }
   1602         }
   1603         delete tz;
   1604     }
   1605 }
   1606 
   1607 void TimeZoneTest::TestEquivalentIDs() {
   1608     int32_t n = TimeZone::countEquivalentIDs("PST");
   1609     if (n < 2) {
   1610         dataerrln((UnicodeString)"FAIL: countEquivalentIDs(PST) = " + n);
   1611     } else {
   1612         UBool sawLA = FALSE;
   1613         for (int32_t i=0; i<n; ++i) {
   1614             UnicodeString id = TimeZone::getEquivalentID("PST", i);
   1615             logln((UnicodeString)"" + i + " : " + id);
   1616             if (id == UnicodeString("America/Los_Angeles")) {
   1617                 sawLA = TRUE;
   1618             }
   1619         }
   1620         if (!sawLA) {
   1621             errln("FAIL: America/Los_Angeles should be in the list");
   1622         }
   1623     }
   1624 }
   1625 
   1626 // Test that a transition at the end of February is handled correctly.
   1627 void TimeZoneTest::TestFebruary() {
   1628     UErrorCode status = U_ZERO_ERROR;
   1629 
   1630     // Time zone with daylight savings time from the first Sunday in November
   1631     // to the last Sunday in February.
   1632     // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n.
   1633     //
   1634     // Note: In tzdata2007h, the rule had changed, so no actual zones uses
   1635     // lastSun in Feb anymore.
   1636     SimpleTimeZone tz1(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
   1637                        UNICODE_STRING("nov-feb", 7),
   1638                        UCAL_NOVEMBER, 1, UCAL_SUNDAY,   // start: November, first, Sunday
   1639                        0,                               //        midnight wall time
   1640                        UCAL_FEBRUARY, -1, UCAL_SUNDAY,  // end:   February, last, Sunday
   1641                        0,                               //        midnight wall time
   1642                        status);
   1643     if (U_FAILURE(status)) {
   1644         errln("Unable to create the SimpleTimeZone(nov-feb): %s", u_errorName(status));
   1645         return;
   1646     }
   1647 
   1648     // Now hardcode the same rules as for Brazil in tzdata 2006n, so that
   1649     // we cover the intended code even when in the future zoneinfo hardcodes
   1650     // these transition dates.
   1651     SimpleTimeZone tz2(-3 * U_MILLIS_PER_HOUR,          // raw offset: 3h before (west of) GMT
   1652                        UNICODE_STRING("nov-feb2", 8),
   1653                        UCAL_NOVEMBER, 1, -UCAL_SUNDAY,  // start: November, 1 or after, Sunday
   1654                        0,                               //        midnight wall time
   1655                        UCAL_FEBRUARY, -29, -UCAL_SUNDAY,// end:   February, 29 or before, Sunday
   1656                        0,                               //        midnight wall time
   1657                        status);
   1658     if (U_FAILURE(status)) {
   1659         errln("Unable to create the SimpleTimeZone(nov-feb2): %s", u_errorName(status));
   1660         return;
   1661     }
   1662 
   1663     // Gregorian calendar with the UTC time zone for getting sample test date/times.
   1664     GregorianCalendar gc(*TimeZone::getGMT(), status);
   1665     if (U_FAILURE(status)) {
   1666         dataerrln("Unable to create the UTC calendar: %s", u_errorName(status));
   1667         return;
   1668     }
   1669 
   1670     struct {
   1671         // UTC time.
   1672         int32_t year, month, day, hour, minute, second;
   1673         // Expected time zone offset in hours after GMT (negative=before GMT).
   1674         int32_t offsetHours;
   1675     } data[] = {
   1676         { 2006, UCAL_NOVEMBER,  5, 02, 59, 59, -3 },
   1677         { 2006, UCAL_NOVEMBER,  5, 03, 00, 00, -2 },
   1678         { 2007, UCAL_FEBRUARY, 25, 01, 59, 59, -2 },
   1679         { 2007, UCAL_FEBRUARY, 25, 02, 00, 00, -3 },
   1680 
   1681         { 2007, UCAL_NOVEMBER,  4, 02, 59, 59, -3 },
   1682         { 2007, UCAL_NOVEMBER,  4, 03, 00, 00, -2 },
   1683         { 2008, UCAL_FEBRUARY, 24, 01, 59, 59, -2 },
   1684         { 2008, UCAL_FEBRUARY, 24, 02, 00, 00, -3 },
   1685 
   1686         { 2008, UCAL_NOVEMBER,  2, 02, 59, 59, -3 },
   1687         { 2008, UCAL_NOVEMBER,  2, 03, 00, 00, -2 },
   1688         { 2009, UCAL_FEBRUARY, 22, 01, 59, 59, -2 },
   1689         { 2009, UCAL_FEBRUARY, 22, 02, 00, 00, -3 },
   1690 
   1691         { 2009, UCAL_NOVEMBER,  1, 02, 59, 59, -3 },
   1692         { 2009, UCAL_NOVEMBER,  1, 03, 00, 00, -2 },
   1693         { 2010, UCAL_FEBRUARY, 28, 01, 59, 59, -2 },
   1694         { 2010, UCAL_FEBRUARY, 28, 02, 00, 00, -3 }
   1695     };
   1696 
   1697     TimeZone *timezones[] = { &tz1, &tz2 };
   1698 
   1699     TimeZone *tz;
   1700     UDate dt;
   1701     int32_t t, i, raw, dst;
   1702     for (t = 0; t < LENGTHOF(timezones); ++t) {
   1703         tz = timezones[t];
   1704         for (i = 0; i < LENGTHOF(data); ++i) {
   1705             gc.set(data[i].year, data[i].month, data[i].day,
   1706                    data[i].hour, data[i].minute, data[i].second);
   1707             dt = gc.getTime(status);
   1708             if (U_FAILURE(status)) {
   1709                 errln("test case %d.%d: bad date/time %04d-%02d-%02d %02d:%02d:%02d",
   1710                       t, i,
   1711                       data[i].year, data[i].month + 1, data[i].day,
   1712                       data[i].hour, data[i].minute, data[i].second);
   1713                 status = U_ZERO_ERROR;
   1714                 continue;
   1715             }
   1716             tz->getOffset(dt, FALSE, raw, dst, status);
   1717             if (U_FAILURE(status)) {
   1718                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) fails: %s",
   1719                       t, i,
   1720                       data[i].year, data[i].month + 1, data[i].day,
   1721                       data[i].hour, data[i].minute, data[i].second,
   1722                       u_errorName(status));
   1723                 status = U_ZERO_ERROR;
   1724             } else if ((raw + dst) != data[i].offsetHours * U_MILLIS_PER_HOUR) {
   1725                 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d",
   1726                       t, i,
   1727                       data[i].year, data[i].month + 1, data[i].day,
   1728                       data[i].hour, data[i].minute, data[i].second,
   1729                       raw, dst, data[i].offsetHours * U_MILLIS_PER_HOUR);
   1730             }
   1731         }
   1732     }
   1733 }
   1734 void TimeZoneTest::TestCanonicalID() {
   1735 
   1736     // Some canonical IDs in CLDR are defined as "Link"
   1737     // in Olson tzdata.
   1738     static const struct {
   1739         const char *alias;
   1740         const char *zone;
   1741     } excluded1[] = {
   1742         {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata
   1743         {"America/Marigot", "America/Guadeloupe"},
   1744         {"America/St_Barthelemy", "America/Guadeloupe"},
   1745         {"Antarctica/South_Pole", "Antarctica/McMurdo"},
   1746         {"Atlantic/Jan_Mayen", "Europe/Oslo"},
   1747         {"Arctic/Longyearbyen", "Europe/Oslo"},
   1748         {"Europe/Guernsey", "Europe/London"},
   1749         {"Europe/Isle_of_Man", "Europe/London"},
   1750         {"Europe/Jersey", "Europe/London"},
   1751         {"Europe/Ljubljana", "Europe/Belgrade"},
   1752         {"Europe/Podgorica", "Europe/Belgrade"},
   1753         {"Europe/Sarajevo", "Europe/Belgrade"},
   1754         {"Europe/Skopje", "Europe/Belgrade"},
   1755         {"Europe/Zagreb", "Europe/Belgrade"},
   1756         {"Europe/Bratislava", "Europe/Prague"},
   1757         {"Europe/Mariehamn", "Europe/Helsinki"},
   1758         {"Europe/San_Marino", "Europe/Rome"},
   1759         {"Europe/Vatican", "Europe/Rome"},
   1760         {0, 0}
   1761     };
   1762 
   1763     // Following IDs are aliases of Etc/GMT in CLDR,
   1764     // but Olson tzdata has 3 independent definitions
   1765     // for Etc/GMT, Etc/UTC, Etc/UCT.
   1766     // Until we merge them into one equivalent group
   1767     // in zoneinfo.res, we exclude them in the test
   1768     // below.
   1769     static const char* excluded2[] = {
   1770         "Etc/UCT", "UCT",
   1771         "Etc/UTC", "UTC",
   1772         "Etc/Universal", "Universal",
   1773         "Etc/Zulu", "Zulu", 0
   1774     };
   1775 
   1776     // Walk through equivalency groups
   1777     UErrorCode ec = U_ZERO_ERROR;
   1778     int32_t s_length, i, j, k;
   1779     StringEnumeration* s = TimeZone::createEnumeration();
   1780     UnicodeString canonicalID, tmpCanonical;
   1781     s_length = s->count(ec);
   1782     for (i = 0; i < s_length;++i) {
   1783         const UnicodeString *tzid = s->snext(ec);
   1784         int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid);
   1785         if (nEquiv == 0) {
   1786             continue;
   1787         }
   1788         UBool bFoundCanonical = FALSE;
   1789         // Make sure getCanonicalID returns the exact same result
   1790         // for all entries within a same equivalency group with some
   1791         // exceptions listed in exluded1.
   1792         // Also, one of them must be canonical id.
   1793         for (j = 0; j < nEquiv; j++) {
   1794             UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j);
   1795             TimeZone::getCanonicalID(tmp, tmpCanonical, ec);
   1796             if (U_FAILURE(ec)) {
   1797                 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed.");
   1798                 ec = U_ZERO_ERROR;
   1799                 continue;
   1800             }
   1801             // Some exceptional cases
   1802             for (k = 0; excluded1[k].alias != 0; k++) {
   1803                 if (tmpCanonical == excluded1[k].alias) {
   1804                     tmpCanonical = excluded1[k].zone;
   1805                     break;
   1806                 }
   1807             }
   1808             if (j == 0) {
   1809                 canonicalID = tmpCanonical;
   1810             } else if (canonicalID != tmpCanonical) {
   1811                 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID);
   1812             }
   1813 
   1814             if (canonicalID == tmp) {
   1815                 bFoundCanonical = TRUE;
   1816             }
   1817         }
   1818         // At least one ID in an equvalency group must match the
   1819         // canonicalID
   1820         if (bFoundCanonical == FALSE) {
   1821             // test exclusion because of differences between Olson tzdata and CLDR
   1822             UBool isExcluded = FALSE;
   1823             for (k = 0; excluded2[k] != 0; k++) {
   1824                 if (*tzid == UnicodeString(excluded2[k])) {
   1825                     isExcluded = TRUE;
   1826                     break;
   1827                 }
   1828             }
   1829             if (isExcluded) {
   1830                 continue;
   1831             }
   1832             errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID);
   1833         }
   1834     }
   1835     delete s;
   1836 
   1837     // Testing some special cases
   1838     static const struct {
   1839         const char *id;
   1840         const char *expected;
   1841         UBool isSystem;
   1842     } data[] = {
   1843         {"GMT-03", "GMT-03:00", FALSE},
   1844         {"GMT+4", "GMT+04:00", FALSE},
   1845         {"GMT-055", "GMT-00:55", FALSE},
   1846         {"GMT+430", "GMT+04:30", FALSE},
   1847         {"GMT-12:15", "GMT-12:15", FALSE},
   1848         {"GMT-091015", "GMT-09:10:15", FALSE},
   1849         {"GMT+1:90", 0, FALSE},
   1850         {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE},
   1851         {"bogus", 0, FALSE},
   1852         {"", 0, FALSE},
   1853         {0, 0, FALSE}
   1854     };
   1855 
   1856     UBool isSystemID;
   1857     for (i = 0; data[i].id != 0; i++) {
   1858         TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec);
   1859         if (U_FAILURE(ec)) {
   1860             if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) {
   1861                 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   1862                     + "\") returned status U_ILLEGAL_ARGUMENT_ERROR");
   1863             }
   1864             ec = U_ZERO_ERROR;
   1865             continue;
   1866         }
   1867         if (canonicalID != data[i].expected) {
   1868             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   1869                 + "\") returned " + canonicalID + " - expected: " + data[i].expected);
   1870         }
   1871         if (isSystemID != data[i].isSystem) {
   1872             dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id
   1873                 + "\") set " + isSystemID + " to isSystemID");
   1874         }
   1875     }
   1876 }
   1877 
   1878 //
   1879 //  Test Display Names, choosing zones and lcoales where there are multiple
   1880 //                      meta-zones defined.
   1881 //
   1882 static struct   {
   1883     const char            *zoneName;
   1884     const char            *localeName;
   1885     UBool                  summerTime;
   1886     TimeZone::EDisplayType style;
   1887     const char            *expectedDisplayName; }
   1888  zoneDisplayTestData [] =  {
   1889      //  zone id         locale   summer   format          expected display name
   1890       {"Europe/London",     "en", FALSE, TimeZone::SHORT, "GMT"},
   1891       {"Europe/London",     "en", FALSE, TimeZone::LONG,  "Greenwich Mean Time"},
   1892       {"Europe/London",     "en", TRUE,  TimeZone::SHORT, "GMT+01:00" /*"BST"*/},
   1893       {"Europe/London",     "en", TRUE,  TimeZone::LONG,  "British Summer Time"},
   1894 
   1895       {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"},
   1896       {"America/Anchorage", "en", FALSE, TimeZone::LONG,  "Alaska Standard Time"},
   1897       {"America/Anchorage", "en", TRUE,  TimeZone::SHORT, "AKDT"},
   1898       {"America/Anchorage", "en", TRUE,  TimeZone::LONG,  "Alaska Daylight Time"},
   1899 
   1900       // Southern Hemisphere, all data from meta:Australia_Western
   1901       {"Australia/Perth",   "en", FALSE, TimeZone::SHORT, "GMT+08:00"/*"AWST"*/},
   1902       {"Australia/Perth",   "en", FALSE, TimeZone::LONG,  "Australian Western Standard Time"},
   1903       {"Australia/Perth",   "en", TRUE,  TimeZone::SHORT, "GMT+09:00"/*"AWDT"*/},
   1904       {"Australia/Perth",   "en", TRUE,  TimeZone::LONG,  "Australian Western Daylight Time"},
   1905 
   1906       {"America/Sao_Paulo",  "en", FALSE, TimeZone::SHORT, "GMT-03:00"/*"BRT"*/},
   1907       {"America/Sao_Paulo",  "en", FALSE, TimeZone::LONG,  "Brasilia Time"},
   1908       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::SHORT, "GMT-02:00"/*"BRST"*/},
   1909       {"America/Sao_Paulo",  "en", TRUE,  TimeZone::LONG,  "Brasilia Summer Time"},
   1910 
   1911       // No Summer Time, but had it before 1983.
   1912       {"Pacific/Honolulu",   "en", FALSE, TimeZone::SHORT, "HST"},
   1913       {"Pacific/Honolulu",   "en", FALSE, TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
   1914       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::SHORT, "HST"},
   1915       {"Pacific/Honolulu",   "en", TRUE,  TimeZone::LONG,  "Hawaii-Aleutian Standard Time"},
   1916 
   1917       // Northern, has Summer, not commonly used.
   1918       {"Europe/Helsinki",    "en", FALSE, TimeZone::SHORT, "GMT+02:00"/*"EET"*/},
   1919       {"Europe/Helsinki",    "en", FALSE, TimeZone::LONG,  "Eastern European Time"},
   1920       {"Europe/Helsinki",    "en", TRUE,  TimeZone::SHORT, "GMT+03:00"/*"EEST"*/},
   1921       {"Europe/Helsinki",    "en", true,  TimeZone::LONG,  "Eastern European Summer Time"},
   1922       {NULL, NULL, FALSE, TimeZone::SHORT, NULL}   // NULL values terminate list
   1923     };
   1924 
   1925 void TimeZoneTest::TestDisplayNamesMeta() {
   1926     UErrorCode status = U_ZERO_ERROR;
   1927     GregorianCalendar cal(*TimeZone::getGMT(), status);
   1928     if (failure(status, "GregorianCalendar", TRUE)) return;
   1929 
   1930     UBool isReferenceYear = TRUE;
   1931     if (cal.get(UCAL_YEAR, status) != TimeZoneTest::REFERENCE_YEAR) {
   1932         isReferenceYear = FALSE;
   1933     }
   1934 
   1935     UBool sawAnError = FALSE;
   1936     for (int testNum   = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) {
   1937         Locale locale  = Locale::createFromName(zoneDisplayTestData[testNum].localeName);
   1938         TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName);
   1939         UnicodeString displayName;
   1940         zone->getDisplayName(zoneDisplayTestData[testNum].summerTime,
   1941                              zoneDisplayTestData[testNum].style,
   1942                              locale,
   1943                              displayName);
   1944         if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) {
   1945             char  name[100];
   1946             UErrorCode status = U_ZERO_ERROR;
   1947             displayName.extract(name, 100, NULL, status);
   1948             if (isReferenceYear) {
   1949                 sawAnError = TRUE;
   1950                 dataerrln("Incorrect time zone display name.  zone = \"%s\",\n"
   1951                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
   1952                       "   Expected \"%s\", "
   1953                       "   Got \"%s\"\n   Error: %s", zoneDisplayTestData[testNum].zoneName,
   1954                                          zoneDisplayTestData[testNum].localeName,
   1955                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
   1956                                             "SHORT" : "LONG",
   1957                                          zoneDisplayTestData[testNum].summerTime,
   1958                                          zoneDisplayTestData[testNum].expectedDisplayName,
   1959                                          name,
   1960                                          u_errorName(status));
   1961             } else {
   1962                 logln("Incorrect time zone display name.  zone = \"%s\",\n"
   1963                       "   locale = \"%s\",   style = %s,  Summertime = %d\n"
   1964                       "   Expected \"%s\", "
   1965                       "   Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName,
   1966                                          zoneDisplayTestData[testNum].localeName,
   1967                                          zoneDisplayTestData[testNum].style==TimeZone::SHORT ?
   1968                                             "SHORT" : "LONG",
   1969                                          zoneDisplayTestData[testNum].summerTime,
   1970                                          zoneDisplayTestData[testNum].expectedDisplayName,
   1971                                          name);
   1972             }
   1973         }
   1974         delete zone;
   1975     }
   1976     if (sawAnError) {
   1977         dataerrln("***Note: Errors could be the result of changes to zoneStrings locale data");
   1978     }
   1979 }
   1980 
   1981 #endif /* #if !UCONFIG_NO_FORMATTING */
   1982