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