Home | History | Annotate | Download | only in intltest
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 2007-2015, International Business Machines Corporation and    *
      6 * others. All Rights Reserved.                                                *
      7 *******************************************************************************
      8 */
      9 #include "unicode/utypes.h"
     10 
     11 #if !UCONFIG_NO_FORMATTING
     12 
     13 #include "tzfmttst.h"
     14 
     15 #include "unicode/timezone.h"
     16 #include "unicode/simpletz.h"
     17 #include "unicode/calendar.h"
     18 #include "unicode/strenum.h"
     19 #include "unicode/smpdtfmt.h"
     20 #include "unicode/uchar.h"
     21 #include "unicode/basictz.h"
     22 #include "unicode/tzfmt.h"
     23 #include "unicode/localpointer.h"
     24 #include "unicode/utf16.h"
     25 
     26 #include "cstring.h"
     27 #include "cstr.h"
     28 #include "mutex.h"
     29 #include "simplethread.h"
     30 #include "uassert.h"
     31 #include "zonemeta.h"
     32 
     33 static const char* PATTERNS[] = {
     34     "z",
     35     "zzzz",
     36     "Z",    // equivalent to "xxxx"
     37     "ZZZZ", // equivalent to "OOOO"
     38     "v",
     39     "vvvv",
     40     "O",
     41     "OOOO",
     42     "X",
     43     "XX",
     44     "XXX",
     45     "XXXX",
     46     "XXXXX",
     47     "x",
     48     "xx",
     49     "xxx",
     50     "xxxx",
     51     "xxxxx",
     52     "V",
     53     "VV",
     54     "VVV",
     55     "VVVV"
     56 };
     57 
     58 static const UChar ETC_UNKNOWN[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
     59 
     60 static const UChar ETC_SLASH[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
     61 static const UChar SYSTEMV_SLASH[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
     62 static const UChar RIYADH8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
     63 
     64 static UBool contains(const char** list, const char* str) {
     65     for (int32_t i = 0; list[i]; i++) {
     66         if (uprv_strcmp(list[i], str) == 0) {
     67             return TRUE;
     68         }
     69     }
     70     return FALSE;
     71 }
     72 
     73 void
     74 TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     75 {
     76     if (exec) {
     77         logln("TestSuite TimeZoneFormatTest");
     78     }
     79     switch (index) {
     80         TESTCASE(0, TestTimeZoneRoundTrip);
     81         TESTCASE(1, TestTimeRoundTrip);
     82         TESTCASE(2, TestParse);
     83         TESTCASE(3, TestISOFormat);
     84         TESTCASE(4, TestFormat);
     85         TESTCASE(5, TestFormatTZDBNames);
     86         TESTCASE(6, TestFormatCustomZone);
     87         TESTCASE(7, TestFormatTZDBNamesAllZoneCoverage);
     88     default: name = ""; break;
     89     }
     90 }
     91 
     92 void
     93 TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
     94     UErrorCode status = U_ZERO_ERROR;
     95 
     96     SimpleTimeZone unknownZone(-31415, ETC_UNKNOWN);
     97     int32_t badDstOffset = -1234;
     98     int32_t badZoneOffset = -2345;
     99 
    100     int32_t testDateData[][3] = {
    101         {2007, 1, 15},
    102         {2007, 6, 15},
    103         {1990, 1, 15},
    104         {1990, 6, 15},
    105         {1960, 1, 15},
    106         {1960, 6, 15},
    107     };
    108 
    109     Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString)"UTC"), status);
    110     if (U_FAILURE(status)) {
    111         dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
    112         return;
    113     }
    114 
    115     // Set up rule equivalency test range
    116     UDate low, high;
    117     cal->set(1900, UCAL_JANUARY, 1);
    118     low = cal->getTime(status);
    119     cal->set(2040, UCAL_JANUARY, 1);
    120     high = cal->getTime(status);
    121     if (U_FAILURE(status)) {
    122         errln("getTime failed");
    123         return;
    124     }
    125 
    126     // Set up test dates
    127     UDate DATES[UPRV_LENGTHOF(testDateData)];
    128     const int32_t nDates = UPRV_LENGTHOF(testDateData);
    129     cal->clear();
    130     for (int32_t i = 0; i < nDates; i++) {
    131         cal->set(testDateData[i][0], testDateData[i][1], testDateData[i][2]);
    132         DATES[i] = cal->getTime(status);
    133         if (U_FAILURE(status)) {
    134             errln("getTime failed");
    135             return;
    136         }
    137     }
    138 
    139     // Set up test locales
    140     const Locale testLocales[] = {
    141         Locale("en"),
    142         Locale("en_CA"),
    143         Locale("fr"),
    144         Locale("zh_Hant"),
    145         Locale("fa"),
    146         Locale("ccp")
    147     };
    148 
    149     const Locale *LOCALES;
    150     int32_t nLocales;
    151 
    152     if (quick) {
    153         LOCALES = testLocales;
    154         nLocales = UPRV_LENGTHOF(testLocales);
    155     } else {
    156         LOCALES = Locale::getAvailableLocales(nLocales);
    157     }
    158 
    159     StringEnumeration *tzids = TimeZone::createEnumeration();
    160     int32_t inRaw, inDst;
    161     int32_t outRaw, outDst;
    162 
    163     // Run the roundtrip test
    164     for (int32_t locidx = 0; locidx < nLocales; locidx++) {
    165         UnicodeString localGMTString;
    166         SimpleDateFormat gmtFmt(UnicodeString("ZZZZ"), LOCALES[locidx], status);
    167         if (U_FAILURE(status)) {
    168             dataerrln("Error creating SimpleDateFormat - %s", u_errorName(status));
    169             continue;
    170         }
    171         gmtFmt.setTimeZone(*TimeZone::getGMT());
    172         gmtFmt.format(0.0, localGMTString);
    173 
    174         for (int32_t patidx = 0; patidx < UPRV_LENGTHOF(PATTERNS); patidx++) {
    175             SimpleDateFormat *sdf = new SimpleDateFormat((UnicodeString)PATTERNS[patidx], LOCALES[locidx], status);
    176             if (U_FAILURE(status)) {
    177                 dataerrln((UnicodeString)"new SimpleDateFormat failed for pattern " +
    178                     PATTERNS[patidx] + " for locale " + LOCALES[locidx].getName() + " - " + u_errorName(status));
    179                 status = U_ZERO_ERROR;
    180                 continue;
    181             }
    182 
    183             tzids->reset(status);
    184             const UnicodeString *tzid;
    185             while ((tzid = tzids->snext(status))) {
    186                 TimeZone *tz = TimeZone::createTimeZone(*tzid);
    187 
    188                 for (int32_t datidx = 0; datidx < nDates; datidx++) {
    189                     UnicodeString tzstr;
    190                     FieldPosition fpos(FieldPosition::DONT_CARE);
    191                     // Format
    192                     sdf->setTimeZone(*tz);
    193                     sdf->format(DATES[datidx], tzstr, fpos);
    194 
    195                     // Before parse, set unknown zone to SimpleDateFormat instance
    196                     // just for making sure that it does not depends on the time zone
    197                     // originally set.
    198                     sdf->setTimeZone(unknownZone);
    199 
    200                     // Parse
    201                     ParsePosition pos(0);
    202                     Calendar *outcal = Calendar::createInstance(unknownZone, status);
    203                     if (U_FAILURE(status)) {
    204                         errln("Failed to create an instance of calendar for receiving parse result.");
    205                         status = U_ZERO_ERROR;
    206                         continue;
    207                     }
    208                     outcal->set(UCAL_DST_OFFSET, badDstOffset);
    209                     outcal->set(UCAL_ZONE_OFFSET, badZoneOffset);
    210 
    211                     sdf->parse(tzstr, *outcal, pos);
    212 
    213                     // Check the result
    214                     const TimeZone &outtz = outcal->getTimeZone();
    215                     UnicodeString outtzid;
    216                     outtz.getID(outtzid);
    217 
    218                     tz->getOffset(DATES[datidx], false, inRaw, inDst, status);
    219                     if (U_FAILURE(status)) {
    220                         errln((UnicodeString)"Failed to get offsets from time zone" + *tzid);
    221                         status = U_ZERO_ERROR;
    222                     }
    223                     outtz.getOffset(DATES[datidx], false, outRaw, outDst, status);
    224                     if (U_FAILURE(status)) {
    225                         errln((UnicodeString)"Failed to get offsets from time zone" + outtzid);
    226                         status = U_ZERO_ERROR;
    227                     }
    228 
    229                     if (uprv_strcmp(PATTERNS[patidx], "V") == 0) {
    230                         // Short zone ID - should support roundtrip for canonical CLDR IDs
    231                         UnicodeString canonicalID;
    232                         TimeZone::getCanonicalID(*tzid, canonicalID, status);
    233                         if (U_FAILURE(status)) {
    234                             // Uknown ID - we should not get here
    235                             errln((UnicodeString)"Unknown ID " + *tzid);
    236                             status = U_ZERO_ERROR;
    237                         } else if (outtzid != canonicalID) {
    238                             if (outtzid.compare(ETC_UNKNOWN, -1) == 0) {
    239                                 // Note that some zones like Asia/Riyadh87 does not have
    240                                 // short zone ID and "unk" is used as fallback
    241                                 logln((UnicodeString)"Canonical round trip failed (probably as expected); tz=" + *tzid
    242                                         + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    243                                         + ", time=" + DATES[datidx] + ", str=" + tzstr
    244                                         + ", outtz=" + outtzid);
    245                             } else {
    246                                 errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
    247                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    248                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    249                                     + ", outtz=" + outtzid);
    250                             }
    251                         }
    252                     } else if (uprv_strcmp(PATTERNS[patidx], "VV") == 0) {
    253                         // Zone ID - full roundtrip support
    254                         if (outtzid != *tzid) {
    255                             errln((UnicodeString)"Zone ID round trip failued; tz="  + *tzid
    256                                 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    257                                 + ", time=" + DATES[datidx] + ", str=" + tzstr
    258                                 + ", outtz=" + outtzid);
    259                         }
    260                     } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0 || uprv_strcmp(PATTERNS[patidx], "VVVV") == 0) {
    261                         // Location: time zone rule must be preserved except
    262                         // zones not actually associated with a specific location.
    263                         // Time zones in this category do not have "/" in its ID.
    264                         UnicodeString canonical;
    265                         TimeZone::getCanonicalID(*tzid, canonical, status);
    266                         if (U_FAILURE(status)) {
    267                             // Uknown ID - we should not get here
    268                             errln((UnicodeString)"Unknown ID " + *tzid);
    269                             status = U_ZERO_ERROR;
    270                         } else if (outtzid != canonical) {
    271                             // Canonical ID did not match - check the rules
    272                             if (!((BasicTimeZone*)&outtz)->hasEquivalentTransitions((BasicTimeZone&)*tz, low, high, TRUE, status)) {
    273                                 if (canonical.indexOf((UChar)0x27 /*'/'*/) == -1) {
    274                                     // Exceptional cases, such as CET, EET, MET and WET
    275                                     logln((UnicodeString)"Canonical round trip failed (as expected); tz=" + *tzid
    276                                             + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    277                                             + ", time=" + DATES[datidx] + ", str=" + tzstr
    278                                             + ", outtz=" + outtzid);
    279                                 } else {
    280                                     errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
    281                                         + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    282                                         + ", time=" + DATES[datidx] + ", str=" + tzstr
    283                                         + ", outtz=" + outtzid);
    284                                 }
    285                                 if (U_FAILURE(status)) {
    286                                     errln("hasEquivalentTransitions failed");
    287                                     status = U_ZERO_ERROR;
    288                                 }
    289                             }
    290                         }
    291 
    292                     } else {
    293                         UBool isOffsetFormat = (*PATTERNS[patidx] == 'Z'
    294                                                 || *PATTERNS[patidx] == 'O'
    295                                                 || *PATTERNS[patidx] == 'X'
    296                                                 || *PATTERNS[patidx] == 'x');
    297                         UBool minutesOffset = FALSE;
    298                         if (*PATTERNS[patidx] == 'X' || *PATTERNS[patidx] == 'x') {
    299                             minutesOffset = (uprv_strlen(PATTERNS[patidx]) <= 3);
    300                         }
    301 
    302                         if (!isOffsetFormat) {
    303                             // Check if localized GMT format is used as a fallback of name styles
    304                             int32_t numDigits = 0;
    305                             int32_t idx = 0;
    306                             while (idx < tzstr.length()) {
    307                                 UChar32 cp = tzstr.char32At(idx);
    308                                 if (u_isdigit(cp)) {
    309                                     numDigits++;
    310                                 }
    311                                 idx += U16_LENGTH(cp);
    312                             }
    313                             isOffsetFormat = (numDigits > 0);
    314                         }
    315                         if (isOffsetFormat || tzstr == localGMTString) {
    316                             // Localized GMT or ISO: total offset (raw + dst) must be preserved.
    317                             int32_t inOffset = inRaw + inDst;
    318                             int32_t outOffset = outRaw + outDst;
    319                             int32_t diff = outOffset - inOffset;
    320                             if (minutesOffset) {
    321                                 diff = (diff / 60000) * 60000;
    322                             }
    323                             if (diff != 0) {
    324                                 errln((UnicodeString)"Offset round trip failed; tz=" + *tzid
    325                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    326                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    327                                     + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
    328                             }
    329                         } else {
    330                             // Specific or generic: raw offset must be preserved.
    331                             if (inRaw != outRaw) {
    332                                 errln((UnicodeString)"Raw offset round trip failed; tz=" + *tzid
    333                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    334                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    335                                     + ", inRawOffset=" + inRaw + ", outRawOffset=" + outRaw);
    336                             }
    337                         }
    338                     }
    339                     delete outcal;
    340                 }
    341                 delete tz;
    342             }
    343             delete sdf;
    344         }
    345     }
    346     delete cal;
    347     delete tzids;
    348 }
    349 
    350 // Special exclusions in TestTimeZoneRoundTrip.
    351 // These special cases do not round trip time as designed.
    352 static UBool isSpecialTimeRoundTripCase(const char* loc,
    353                                         const UnicodeString& id,
    354                                         const char* pattern,
    355                                         UDate time) {
    356     struct {
    357         const char* loc;
    358         const char* id;
    359         const char* pattern;
    360         UDate time;
    361     } EXCLUSIONS[] = {
    362         {NULL, "Asia/Chita", "zzzz", 1414252800000.0},
    363         {NULL, "Asia/Chita", "vvvv", 1414252800000.0},
    364         {NULL, "Asia/Srednekolymsk", "zzzz", 1414241999999.0},
    365         {NULL, "Asia/Srednekolymsk", "vvvv", 1414241999999.0},
    366         {NULL, NULL, NULL, U_DATE_MIN}
    367     };
    368 
    369     UBool isExcluded = FALSE;
    370     for (int32_t i = 0; EXCLUSIONS[i].id != NULL; i++) {
    371         if (EXCLUSIONS[i].loc == NULL || uprv_strcmp(loc, EXCLUSIONS[i].loc) == 0) {
    372             if (id.compare(EXCLUSIONS[i].id) == 0) {
    373                 if (EXCLUSIONS[i].pattern == NULL || uprv_strcmp(pattern, EXCLUSIONS[i].pattern) == 0) {
    374                     if (EXCLUSIONS[i].time == U_DATE_MIN || EXCLUSIONS[i].time == time) {
    375                         isExcluded = TRUE;
    376                     }
    377                 }
    378             }
    379         }
    380     }
    381     return isExcluded;
    382 }
    383 
    384 // LocaleData. Somewhat misnamed. For TestTimeZoneRoundTrip, specifies the locales and patterns
    385 //             to be tested, and provides an iterator over these for the multi-threaded test
    386 //             functions to pick up the next combination to be tested.
    387 //
    388 //             A single global instance of this struct is shared among all
    389 //             the test threads.
    390 //
    391 //             "locales" is an array of locales to be tested.
    392 //             PATTERNS (a global) is an array of patterns to be tested for each locale.
    393 //             "localeIndex" and "patternIndex" keep track of the iteration through the above.
    394 //             Each of the parallel test threads calls LocaleData::nextTest() in a loop
    395 //                to find out what to test next. It must be thread safe.
    396 struct LocaleData {
    397     int32_t localeIndex;
    398     int32_t patternIndex;
    399     int32_t testCounts;
    400     UDate times[UPRV_LENGTHOF(PATTERNS)];    // Performance data, Elapsed time for each pattern.
    401     const Locale* locales;
    402     int32_t nLocales;
    403     UDate START_TIME;
    404     UDate END_TIME;
    405     int32_t numDone;
    406 
    407     LocaleData() : localeIndex(0), patternIndex(0), testCounts(0), locales(NULL),
    408                    nLocales(0), START_TIME(0), END_TIME(0), numDone(0) {
    409         for (int i=0; i<UPRV_LENGTHOF(times); i++) {
    410             times[i] = 0;
    411         }
    412     };
    413 
    414     void resetTestIteration() {
    415         localeIndex = -1;
    416         patternIndex = UPRV_LENGTHOF(PATTERNS);
    417         numDone = 0;
    418     }
    419 
    420     UBool nextTest(int32_t &rLocaleIndex, int32_t &rPatternIndex) {
    421         Mutex lock;
    422         if (patternIndex >= UPRV_LENGTHOF(PATTERNS) - 1) {
    423             if (localeIndex >= nLocales - 1) {
    424                 return FALSE;
    425             }
    426             patternIndex = -1;
    427             ++localeIndex;
    428         }
    429         ++patternIndex;
    430         rLocaleIndex = localeIndex;
    431         rPatternIndex = patternIndex;
    432         ++numDone;
    433         return TRUE;
    434     }
    435 
    436     void addTime(UDate amount, int32_t patIdx) {
    437         Mutex lock;
    438         U_ASSERT(patIdx < UPRV_LENGTHOF(PATTERNS));
    439         times[patIdx] += amount;
    440     }
    441 };
    442 
    443 static LocaleData *gLocaleData = NULL;
    444 
    445 void
    446 TimeZoneFormatTest::TestTimeRoundTrip(void) {
    447     UErrorCode status = U_ZERO_ERROR;
    448     LocalPointer <Calendar> cal(Calendar::createInstance(TimeZone::createTimeZone((UnicodeString) "UTC"), status));
    449     if (U_FAILURE(status)) {
    450         dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
    451         return;
    452     }
    453 
    454     const char* testAllProp = getProperty("TimeZoneRoundTripAll");
    455     UBool bTestAll = (testAllProp && uprv_strcmp(testAllProp, "true") == 0);
    456 
    457     UDate START_TIME, END_TIME;
    458     if (bTestAll || !quick) {
    459         cal->set(1900, UCAL_JANUARY, 1);
    460     } else {
    461         cal->set(1999, UCAL_JANUARY, 1);
    462     }
    463     START_TIME = cal->getTime(status);
    464 
    465     cal->set(2022, UCAL_JANUARY, 1);
    466     END_TIME = cal->getTime(status);
    467 
    468     if (U_FAILURE(status)) {
    469         errln("getTime failed");
    470         return;
    471     }
    472 
    473     LocaleData localeData;
    474     gLocaleData = &localeData;
    475 
    476     // Set up test locales
    477     const Locale locales1[] = {Locale("en")};
    478     const Locale locales2[] = {
    479         Locale("ar_EG"), Locale("bg_BG"), Locale("ca_ES"), Locale("da_DK"), Locale("de"),
    480         Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"),
    481         Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"),
    482         Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"),
    483         Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"),
    484         Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"),
    485         Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"),
    486         Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"),
    487         Locale("zh_Hant"), Locale("zh_Hant_TW"), Locale("fa"), Locale("ccp")
    488     };
    489 
    490     if (bTestAll) {
    491         gLocaleData->locales = Locale::getAvailableLocales(gLocaleData->nLocales);
    492     } else if (quick) {
    493         gLocaleData->locales = locales1;
    494         gLocaleData->nLocales = UPRV_LENGTHOF(locales1);
    495     } else {
    496         gLocaleData->locales = locales2;
    497         gLocaleData->nLocales = UPRV_LENGTHOF(locales2);
    498     }
    499 
    500     gLocaleData->START_TIME = START_TIME;
    501     gLocaleData->END_TIME = END_TIME;
    502     gLocaleData->resetTestIteration();
    503 
    504     // start IntlTest.threadCount threads, each running the function RunTimeRoundTripTests().
    505 
    506     ThreadPool<TimeZoneFormatTest> threads(this, threadCount, &TimeZoneFormatTest::RunTimeRoundTripTests);
    507     threads.start();   // Start all threads.
    508     threads.join();    // Wait for all threads to finish.
    509 
    510     UDate total = 0;
    511     logln("### Elapsed time by patterns ###");
    512     for (int32_t i = 0; i < UPRV_LENGTHOF(PATTERNS); i++) {
    513         logln(UnicodeString("") + gLocaleData->times[i] + "ms (" + PATTERNS[i] + ")");
    514         total += gLocaleData->times[i];
    515     }
    516     logln((UnicodeString) "Total: " + total + "ms");
    517     logln((UnicodeString) "Iteration: " + gLocaleData->testCounts);
    518 }
    519 
    520 
    521 // TimeZoneFormatTest::RunTimeRoundTripTests()
    522 //    This function loops, running time zone format round trip test cases until there are no more, then returns.
    523 //    Threading: multiple invocations of this function are started in parallel
    524 //               by TimeZoneFormatTest::TestTimeRoundTrip()
    525 //
    526 void TimeZoneFormatTest::RunTimeRoundTripTests(int32_t threadNumber) {
    527     UErrorCode status = U_ZERO_ERROR;
    528     UBool REALLY_VERBOSE = FALSE;
    529 
    530     // These patterns are ambiguous at DST->STD local time overlap
    531     const char* AMBIGUOUS_DST_DECESSION[] = { "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
    532 
    533     // These patterns are ambiguous at STD->STD/DST->DST local time overlap
    534     const char* AMBIGUOUS_NEGATIVE_SHIFT[] = { "z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
    535 
    536     // These patterns only support integer minutes offset
    537     const char* MINUTES_OFFSET[] = { "X", "XX", "XXX", "x", "xx", "xxx", 0 };
    538 
    539     // Workaround for #6338
    540     //UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
    541     UnicodeString BASEPATTERN("yyyy.MM.dd HH:mm:ss.SSS");
    542 
    543     // timer for performance analysis
    544     UDate timer;
    545     UDate testTimes[4];
    546     UBool expectedRoundTrip[4];
    547     int32_t testLen = 0;
    548 
    549     StringEnumeration *tzids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
    550     if (U_FAILURE(status)) {
    551         if (status == U_MISSING_RESOURCE_ERROR) {
    552             // This error is generally caused by data not being present.
    553             dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status));
    554         } else {
    555             errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status));
    556         }
    557         return;
    558     }
    559 
    560     int32_t locidx = -1;
    561     int32_t patidx = -1;
    562 
    563     while (gLocaleData->nextTest(locidx, patidx)) {
    564 
    565         UnicodeString pattern(BASEPATTERN);
    566         pattern.append(" ").append(PATTERNS[patidx]);
    567         logln("    Thread %d, Locale %s, Pattern %s",
    568                 threadNumber, gLocaleData->locales[locidx].getName(), CStr(pattern)());
    569 
    570         SimpleDateFormat *sdf = new SimpleDateFormat(pattern, gLocaleData->locales[locidx], status);
    571         if (U_FAILURE(status)) {
    572             errcheckln(status, (UnicodeString) "new SimpleDateFormat failed for pattern " +
    573                 pattern + " for locale " + gLocaleData->locales[locidx].getName() + " - " + u_errorName(status));
    574             status = U_ZERO_ERROR;
    575             continue;
    576         }
    577 
    578         UBool minutesOffset = contains(MINUTES_OFFSET, PATTERNS[patidx]);
    579 
    580         tzids->reset(status);
    581         const UnicodeString *tzid;
    582 
    583         timer = Calendar::getNow();
    584 
    585         while ((tzid = tzids->snext(status))) {
    586             if (uprv_strcmp(PATTERNS[patidx], "V") == 0) {
    587                 // Some zones do not have short ID assigned, such as Asia/Riyadh87.
    588                 // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
    589                 // This is expected behavior.
    590                 const UChar* shortZoneID = ZoneMeta::getShortID(*tzid);
    591                 if (shortZoneID == NULL) {
    592                     continue;
    593                 }
    594             } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0) {
    595                 // Some zones are not associated with any region, such as Etc/GMT+8.
    596                 // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location).
    597                 // This is expected behavior.
    598                 if (tzid->indexOf((UChar)0x2F) < 0 || tzid->indexOf(ETC_SLASH, -1, 0) >= 0
    599                     || tzid->indexOf(SYSTEMV_SLASH, -1, 0) >= 0 || tzid->indexOf(RIYADH8, -1, 0) >= 0) {
    600                     continue;
    601                 }
    602             }
    603 
    604             if ((*tzid == "Pacific/Apia" || *tzid == "Pacific/Midway" || *tzid == "Pacific/Pago_Pago")
    605                     && uprv_strcmp(PATTERNS[patidx], "vvvv") == 0
    606                     && logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
    607                 continue;
    608             }
    609 
    610             BasicTimeZone *tz = (BasicTimeZone*) TimeZone::createTimeZone(*tzid);
    611             sdf->setTimeZone(*tz);
    612 
    613             UDate t = gLocaleData->START_TIME;
    614             TimeZoneTransition tzt;
    615             UBool tztAvail = FALSE;
    616             UBool middle = TRUE;
    617 
    618             while (t < gLocaleData->END_TIME) {
    619                 if (!tztAvail) {
    620                     testTimes[0] = t;
    621                     expectedRoundTrip[0] = TRUE;
    622                     testLen = 1;
    623                 } else {
    624                     int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
    625                     int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
    626                     int32_t delta = toOffset - fromOffset;
    627                     if (delta < 0) {
    628                         UBool isDstDecession = tzt.getFrom()->getDSTSavings() > 0 && tzt.getTo()->getDSTSavings() == 0;
    629                         testTimes[0] = t + delta - 1;
    630                         expectedRoundTrip[0] = TRUE;
    631                         testTimes[1] = t + delta;
    632                         expectedRoundTrip[1] = isDstDecession ?
    633                             !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
    634                             !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
    635                         testTimes[2] = t - 1;
    636                         expectedRoundTrip[2] = isDstDecession ?
    637                             !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
    638                             !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
    639                         testTimes[3] = t;
    640                         expectedRoundTrip[3] = TRUE;
    641                         testLen = 4;
    642                     } else {
    643                         testTimes[0] = t - 1;
    644                         expectedRoundTrip[0] = TRUE;
    645                         testTimes[1] = t;
    646                         expectedRoundTrip[1] = TRUE;
    647                         testLen = 2;
    648                     }
    649                 }
    650                 for (int32_t testidx = 0; testidx < testLen; testidx++) {
    651                     if (quick) {
    652                         // reduce regular test time
    653                         if (!expectedRoundTrip[testidx]) {
    654                             continue;
    655                         }
    656                     }
    657 
    658                     {
    659                         Mutex lock;
    660                         gLocaleData->testCounts++;
    661                     }
    662 
    663                     UnicodeString text;
    664                     FieldPosition fpos(FieldPosition::DONT_CARE);
    665                     sdf->format(testTimes[testidx], text, fpos);
    666 
    667                     UDate parsedDate = sdf->parse(text, status);
    668                     if (U_FAILURE(status)) {
    669                         errln((UnicodeString) "Parse failure for text=" + text + ", tzid=" + *tzid + ", locale=" + gLocaleData->locales[locidx].getName()
    670                                 + ", pattern=" + PATTERNS[patidx] + ", time=" + testTimes[testidx]);
    671                         status = U_ZERO_ERROR;
    672                         continue;
    673                     }
    674 
    675                     int32_t timeDiff = (int32_t)(parsedDate - testTimes[testidx]);
    676                     UBool bTimeMatch = minutesOffset ?
    677                         (timeDiff/60000)*60000 == 0 : timeDiff == 0;
    678                     if (!bTimeMatch) {
    679                         UnicodeString msg = (UnicodeString) "Time round trip failed for " + "tzid=" + *tzid
    680                                 + ", locale=" + gLocaleData->locales[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    681                                 + ", text=" + text + ", time=" + testTimes[testidx] + ", restime=" + parsedDate + ", diff=" + (parsedDate - testTimes[testidx]);
    682                         // Timebomb for TZData update
    683                         if (expectedRoundTrip[testidx]
    684                                 && !isSpecialTimeRoundTripCase(gLocaleData->locales[locidx].getName(), *tzid,
    685                                         PATTERNS[patidx], testTimes[testidx])) {
    686                             errln((UnicodeString) "FAIL: " + msg);
    687                         } else if (REALLY_VERBOSE) {
    688                             logln(msg);
    689                         }
    690                     }
    691                 }
    692                 tztAvail = tz->getNextTransition(t, FALSE, tzt);
    693                 if (!tztAvail) {
    694                     break;
    695                 }
    696                 if (middle) {
    697                     // Test the date in the middle of two transitions.
    698                     t += (int64_t) ((tzt.getTime() - t) / 2);
    699                     middle = FALSE;
    700                     tztAvail = FALSE;
    701                 } else {
    702                     t = tzt.getTime();
    703                 }
    704             }
    705             delete tz;
    706         }
    707         UDate elapsedTime = Calendar::getNow() - timer;
    708         gLocaleData->addTime(elapsedTime, patidx);
    709         delete sdf;
    710     }
    711     delete tzids;
    712 }
    713 
    714 
    715 typedef struct {
    716     const char*     text;
    717     int32_t         inPos;
    718     const char*     locale;
    719     UTimeZoneFormatStyle    style;
    720     uint32_t        parseOptions;
    721     const char*     expected;
    722     int32_t         outPos;
    723     UTimeZoneFormatTimeType timeType;
    724 } ParseTestData;
    725 
    726 void
    727 TimeZoneFormatTest::TestParse(void) {
    728     const ParseTestData DATA[] = {
    729         //   text               inPos   locale      style
    730         //      parseOptions                        expected            outPos  timeType
    731             {"Z",               0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    732                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    733 
    734             {"Z",               0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    735                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    736 
    737             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    738                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    739 
    740             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_GENERIC_LOCATION,
    741                 UTZFMT_PARSE_OPTION_NONE,           "Africa/Lusaka",    11,     UTZFMT_TIME_TYPE_UNKNOWN},
    742 
    743             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    744                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "Africa/Lusaka",    11,     UTZFMT_TIME_TYPE_UNKNOWN},
    745 
    746             {"+00:00",          0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    747                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          6,      UTZFMT_TIME_TYPE_UNKNOWN},
    748 
    749             {"-01:30:45",       0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    750                 UTZFMT_PARSE_OPTION_NONE,           "GMT-01:30:45",     9,      UTZFMT_TIME_TYPE_UNKNOWN},
    751 
    752             {"-7",              0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    753                 UTZFMT_PARSE_OPTION_NONE,           "GMT-07:00",        2,      UTZFMT_TIME_TYPE_UNKNOWN},
    754 
    755             {"-2222",           0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    756                 UTZFMT_PARSE_OPTION_NONE,           "GMT-22:22",        5,      UTZFMT_TIME_TYPE_UNKNOWN},
    757 
    758             {"-3333",           0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    759                 UTZFMT_PARSE_OPTION_NONE,           "GMT-03:33",        4,      UTZFMT_TIME_TYPE_UNKNOWN},
    760 
    761             {"XXX+01:30YYY",    3,      "en_US",    UTZFMT_STYLE_LOCALIZED_GMT,
    762                 UTZFMT_PARSE_OPTION_NONE,           "GMT+01:30",        9,      UTZFMT_TIME_TYPE_UNKNOWN},
    763 
    764             {"GMT0",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    765                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          3,      UTZFMT_TIME_TYPE_UNKNOWN},
    766 
    767             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    768                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    769 
    770             {"ESTx",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    771                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    772 
    773             {"EDTx",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    774                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_DAYLIGHT},
    775 
    776             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    777                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    778 
    779             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    780                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    781 
    782             {"EST",             0,      "en_CA",    UTZFMT_STYLE_SPECIFIC_SHORT,
    783                 UTZFMT_PARSE_OPTION_NONE,           "America/Toronto",  3,      UTZFMT_TIME_TYPE_STANDARD},
    784 
    785             {"CST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    786                 UTZFMT_PARSE_OPTION_NONE,           "America/Chicago",  3,      UTZFMT_TIME_TYPE_STANDARD},
    787 
    788             {"CST",             0,      "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    789                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    790 
    791             {"CST",             0,      "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    792                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "America/Chicago",  3,  UTZFMT_TIME_TYPE_STANDARD},
    793 
    794             {"--CST--",           2,    "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    795                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "America/Chicago",  5,  UTZFMT_TIME_TYPE_STANDARD},
    796 
    797             {"CST",             0,      "zh_CN",    UTZFMT_STYLE_SPECIFIC_SHORT,
    798                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Asia/Shanghai",    3,  UTZFMT_TIME_TYPE_STANDARD},
    799 
    800             {"AEST",            0,      "en_AU",    UTZFMT_STYLE_SPECIFIC_SHORT,
    801                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Australia/Sydney", 4,  UTZFMT_TIME_TYPE_STANDARD},
    802 
    803             {"AST",             0,      "ar_SA",    UTZFMT_STYLE_SPECIFIC_SHORT,
    804                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Asia/Riyadh",      3,  UTZFMT_TIME_TYPE_STANDARD},
    805 
    806             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    807                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    808 
    809             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    810                 UTZFMT_PARSE_OPTION_ALL_STYLES,     NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    811 
    812             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    813                 UTZFMT_PARSE_OPTION_ALL_STYLES | UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Aqtobe",  5,  UTZFMT_TIME_TYPE_DAYLIGHT},
    814 
    815             {NULL,              0,      NULL,       UTZFMT_STYLE_GENERIC_LOCATION,
    816                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN}
    817     };
    818 
    819     for (int32_t i = 0; DATA[i].text; i++) {
    820         UErrorCode status = U_ZERO_ERROR;
    821         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
    822         if (U_FAILURE(status)) {
    823             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
    824             continue;
    825         }
    826         UTimeZoneFormatTimeType ttype = UTZFMT_TIME_TYPE_UNKNOWN;
    827         ParsePosition pos(DATA[i].inPos);
    828         TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype);
    829 
    830         UnicodeString errMsg;
    831         if (tz) {
    832             UnicodeString outID;
    833             tz->getID(outID);
    834             if (outID != UnicodeString(DATA[i].expected)) {
    835                 errMsg = (UnicodeString)"Time zone ID: " + outID + " - expected: " + DATA[i].expected;
    836             } else if (pos.getIndex() != DATA[i].outPos) {
    837                 errMsg = (UnicodeString)"Parsed pos: " + pos.getIndex() + " - expected: " + DATA[i].outPos;
    838             } else if (ttype != DATA[i].timeType) {
    839                 errMsg = (UnicodeString)"Time type: " + ttype + " - expected: " + DATA[i].timeType;
    840             }
    841             delete tz;
    842         } else {
    843             if (DATA[i].expected) {
    844                 errMsg = (UnicodeString)"Parse failure - expected: " + DATA[i].expected;
    845             }
    846         }
    847         if (errMsg.length() > 0) {
    848             errln((UnicodeString)"Fail: " + errMsg + " [text=" + DATA[i].text + ", pos=" + DATA[i].inPos + ", style=" + DATA[i].style + "]");
    849         }
    850     }
    851 }
    852 
    853 void
    854 TimeZoneFormatTest::TestISOFormat(void) {
    855     const int32_t OFFSET[] = {
    856         0,          // 0
    857         999,        // 0.999s
    858         -59999,     // -59.999s
    859         60000,      // 1m
    860         -77777,     // -1m 17.777s
    861         1800000,    // 30m
    862         -3600000,   // -1h
    863         36000000,   // 10h
    864         -37800000,  // -10h 30m
    865         -37845000,  // -10h 30m 45s
    866         108000000,  // 30h
    867     };
    868 
    869     const char* ISO_STR[][11] = {
    870         // 0
    871         {
    872             "Z", "Z", "Z", "Z", "Z",
    873             "+00", "+0000", "+00:00", "+0000", "+00:00",
    874             "+0000"
    875         },
    876         // 999
    877         {
    878             "Z", "Z", "Z", "Z", "Z",
    879             "+00", "+0000", "+00:00", "+0000", "+00:00",
    880             "+0000"
    881         },
    882         // -59999
    883         {
    884             "Z", "Z", "Z", "-000059", "-00:00:59",
    885             "+00", "+0000", "+00:00", "-000059", "-00:00:59",
    886             "-000059"
    887         },
    888         // 60000
    889         {
    890             "+0001", "+0001", "+00:01", "+0001", "+00:01",
    891             "+0001", "+0001", "+00:01", "+0001", "+00:01",
    892             "+0001"
    893         },
    894         // -77777
    895         {
    896             "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
    897             "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
    898             "-000117"
    899         },
    900         // 1800000
    901         {
    902             "+0030", "+0030", "+00:30", "+0030", "+00:30",
    903             "+0030", "+0030", "+00:30", "+0030", "+00:30",
    904             "+0030"
    905         },
    906         // -3600000
    907         {
    908             "-01", "-0100", "-01:00", "-0100", "-01:00",
    909             "-01", "-0100", "-01:00", "-0100", "-01:00",
    910             "-0100"
    911         },
    912         // 36000000
    913         {
    914             "+10", "+1000", "+10:00", "+1000", "+10:00",
    915             "+10", "+1000", "+10:00", "+1000", "+10:00",
    916             "+1000"
    917         },
    918         // -37800000
    919         {
    920             "-1030", "-1030", "-10:30", "-1030", "-10:30",
    921             "-1030", "-1030", "-10:30", "-1030", "-10:30",
    922             "-1030"
    923         },
    924         // -37845000
    925         {
    926             "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
    927             "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
    928             "-103045"
    929         },
    930         // 108000000
    931         {
    932             0, 0, 0, 0, 0,
    933             0, 0, 0, 0, 0,
    934             0
    935         }
    936     };
    937 
    938     const char* PATTERN[] = {
    939         "X", "XX", "XXX", "XXXX", "XXXXX",
    940         "x", "xx", "xxx", "xxxx", "xxxxx",
    941         "Z", // equivalent to "xxxx"
    942         0
    943     };
    944 
    945     const int32_t MIN_OFFSET_UNIT[] = {
    946         60000, 60000, 60000, 1000, 1000,
    947         60000, 60000, 60000, 1000, 1000,
    948         1000,
    949     };
    950 
    951     // Formatting
    952     UErrorCode status = U_ZERO_ERROR;
    953     LocalPointer<SimpleDateFormat> sdf(new SimpleDateFormat(status), status);
    954     if (U_FAILURE(status)) {
    955         dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status));
    956         return;
    957     }
    958     UDate d = Calendar::getNow();
    959 
    960     for (uint32_t i = 0; i < UPRV_LENGTHOF(OFFSET); i++) {
    961         SimpleTimeZone* tz = new SimpleTimeZone(OFFSET[i], UnicodeString("Zone Offset:") + OFFSET[i] + "ms");
    962         sdf->adoptTimeZone(tz);
    963         for (int32_t j = 0; PATTERN[j] != 0; j++) {
    964             sdf->applyPattern(UnicodeString(PATTERN[j]));
    965             UnicodeString result;
    966             sdf->format(d, result);
    967 
    968             if (ISO_STR[i][j]) {
    969                 if (result != UnicodeString(ISO_STR[i][j])) {
    970                     errln((UnicodeString)"FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> "
    971                         + result + " (expected: " + ISO_STR[i][j] + ")");
    972                 }
    973             } else {
    974                 // Offset out of range
    975                 // Note: for now, there is no way to propagate the error status through
    976                 // the SimpleDateFormat::format above.
    977                 if (result.length() > 0) {
    978                     errln((UnicodeString)"FAIL: Non-Empty result for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i]
    979                         + " (expected: empty result)");
    980                 }
    981             }
    982         }
    983     }
    984 
    985     // Parsing
    986     LocalPointer<Calendar> outcal(Calendar::createInstance(status));
    987     if (U_FAILURE(status)) {
    988         dataerrln("Fail new Calendar: %s", u_errorName(status));
    989         return;
    990     }
    991     for (int32_t i = 0; ISO_STR[i][0] != NULL; i++) {
    992         for (int32_t j = 0; PATTERN[j] != 0; j++) {
    993             if (ISO_STR[i][j] == 0) {
    994                 continue;
    995             }
    996             ParsePosition pos(0);
    997             SimpleTimeZone* bogusTZ = new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
    998             outcal->adoptTimeZone(bogusTZ);
    999             sdf->applyPattern(PATTERN[j]);
   1000 
   1001             sdf->parse(UnicodeString(ISO_STR[i][j]), *(outcal.getAlias()), pos);
   1002 
   1003             if (pos.getIndex() != (int32_t)uprv_strlen(ISO_STR[i][j])) {
   1004                 errln((UnicodeString)"FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]);
   1005             }
   1006 
   1007             const TimeZone& outtz = outcal->getTimeZone();
   1008             int32_t outOffset = outtz.getRawOffset();
   1009             int32_t adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j];
   1010             if (outOffset != adjustedOffset) {
   1011                 errln((UnicodeString)"FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j]
   1012                     + " (expected:" + adjustedOffset + "ms)");
   1013             }
   1014         }
   1015     }
   1016 }
   1017 
   1018 
   1019 typedef struct {
   1020     const char*     locale;
   1021     const char*     tzid;
   1022     UDate           date;
   1023     UTimeZoneFormatStyle    style;
   1024     const char*     expected;
   1025     UTimeZoneFormatTimeType timeType;
   1026 } FormatTestData;
   1027 
   1028 void
   1029 TimeZoneFormatTest::TestFormat(void) {
   1030     UDate dateJan = 1358208000000.0;    // 2013-01-15T00:00:00Z
   1031     UDate dateJul = 1373846400000.0;    // 2013-07-15T00:00:00Z
   1032 
   1033     const FormatTestData DATA[] = {
   1034         {
   1035             "en",
   1036             "America/Los_Angeles",
   1037             dateJan,
   1038             UTZFMT_STYLE_GENERIC_LOCATION,
   1039             "Los Angeles Time",
   1040             UTZFMT_TIME_TYPE_UNKNOWN
   1041         },
   1042         {
   1043             "en",
   1044             "America/Los_Angeles",
   1045             dateJan,
   1046             UTZFMT_STYLE_GENERIC_LONG,
   1047             "Pacific Time",
   1048             UTZFMT_TIME_TYPE_UNKNOWN
   1049         },
   1050         {
   1051             "en",
   1052             "America/Los_Angeles",
   1053             dateJan,
   1054             UTZFMT_STYLE_SPECIFIC_LONG,
   1055             "Pacific Standard Time",
   1056             UTZFMT_TIME_TYPE_STANDARD
   1057         },
   1058         {
   1059             "en",
   1060             "America/Los_Angeles",
   1061             dateJul,
   1062             UTZFMT_STYLE_SPECIFIC_LONG,
   1063             "Pacific Daylight Time",
   1064             UTZFMT_TIME_TYPE_DAYLIGHT
   1065         },
   1066         {
   1067             "ja",
   1068             "America/Los_Angeles",
   1069             dateJan,
   1070             UTZFMT_STYLE_ZONE_ID,
   1071             "America/Los_Angeles",
   1072             UTZFMT_TIME_TYPE_UNKNOWN
   1073         },
   1074         {
   1075             "fr",
   1076             "America/Los_Angeles",
   1077             dateJul,
   1078             UTZFMT_STYLE_ZONE_ID_SHORT,
   1079             "uslax",
   1080             UTZFMT_TIME_TYPE_UNKNOWN
   1081         },
   1082         {
   1083             "en",
   1084             "America/Los_Angeles",
   1085             dateJan,
   1086             UTZFMT_STYLE_EXEMPLAR_LOCATION,
   1087             "Los Angeles",
   1088             UTZFMT_TIME_TYPE_UNKNOWN
   1089         },
   1090 
   1091         {
   1092             "ja",
   1093             "Asia/Tokyo",
   1094             dateJan,
   1095             UTZFMT_STYLE_GENERIC_LONG,
   1096             "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
   1097             UTZFMT_TIME_TYPE_UNKNOWN
   1098         },
   1099 
   1100         {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
   1101     };
   1102 
   1103     for (int32_t i = 0; DATA[i].locale; i++) {
   1104         UErrorCode status = U_ZERO_ERROR;
   1105         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
   1106         if (U_FAILURE(status)) {
   1107             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
   1108             continue;
   1109         }
   1110 
   1111         LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
   1112         UnicodeString out;
   1113         UTimeZoneFormatTimeType timeType;
   1114 
   1115         tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
   1116         UnicodeString expected(DATA[i].expected, -1, US_INV);
   1117         expected = expected.unescape();
   1118 
   1119         assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
   1120         if (DATA[i].timeType != timeType) {
   1121             dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
   1122                 + timeType + ", expected=" + DATA[i].timeType);
   1123         }
   1124     }
   1125 }
   1126 
   1127 void
   1128 TimeZoneFormatTest::TestFormatTZDBNames(void) {
   1129     UDate dateJan = 1358208000000.0;    // 2013-01-15T00:00:00Z
   1130     UDate dateJul = 1373846400000.0;    // 2013-07-15T00:00:00Z
   1131 
   1132     const FormatTestData DATA[] = {
   1133         {
   1134             "en",
   1135             "America/Chicago",
   1136             dateJan,
   1137             UTZFMT_STYLE_SPECIFIC_SHORT,
   1138             "CST",
   1139             UTZFMT_TIME_TYPE_STANDARD
   1140         },
   1141         {
   1142             "en",
   1143             "Asia/Shanghai",
   1144             dateJan,
   1145             UTZFMT_STYLE_SPECIFIC_SHORT,
   1146             "CST",
   1147             UTZFMT_TIME_TYPE_STANDARD
   1148         },
   1149         {
   1150             "zh_Hans",
   1151             "Asia/Shanghai",
   1152             dateJan,
   1153             UTZFMT_STYLE_SPECIFIC_SHORT,
   1154             "CST",
   1155             UTZFMT_TIME_TYPE_STANDARD
   1156         },
   1157         {
   1158             "en",
   1159             "America/Los_Angeles",
   1160             dateJul,
   1161             UTZFMT_STYLE_SPECIFIC_LONG,
   1162             "GMT-07:00",    // No long display names
   1163             UTZFMT_TIME_TYPE_DAYLIGHT
   1164         },
   1165         {
   1166             "ja",
   1167             "America/Los_Angeles",
   1168             dateJul,
   1169             UTZFMT_STYLE_SPECIFIC_SHORT,
   1170             "PDT",
   1171             UTZFMT_TIME_TYPE_DAYLIGHT
   1172         },
   1173         {
   1174             "en",
   1175             "Australia/Sydney",
   1176             dateJan,
   1177             UTZFMT_STYLE_SPECIFIC_SHORT,
   1178             "AEDT",
   1179             UTZFMT_TIME_TYPE_DAYLIGHT
   1180         },
   1181         {
   1182             "en",
   1183             "Australia/Sydney",
   1184             dateJul,
   1185             UTZFMT_STYLE_SPECIFIC_SHORT,
   1186             "AEST",
   1187             UTZFMT_TIME_TYPE_STANDARD
   1188         },
   1189 
   1190         {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
   1191     };
   1192 
   1193     for (int32_t i = 0; DATA[i].locale; i++) {
   1194         UErrorCode status = U_ZERO_ERROR;
   1195         Locale loc(DATA[i].locale);
   1196         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(loc, status));
   1197         if (U_FAILURE(status)) {
   1198             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
   1199             continue;
   1200         }
   1201         TimeZoneNames *tzdbNames = TimeZoneNames::createTZDBInstance(loc, status);
   1202         if (U_FAILURE(status)) {
   1203             dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status));
   1204             continue;
   1205         }
   1206         tzfmt->adoptTimeZoneNames(tzdbNames);
   1207 
   1208         LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
   1209         UnicodeString out;
   1210         UTimeZoneFormatTimeType timeType;
   1211 
   1212         tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
   1213         UnicodeString expected(DATA[i].expected, -1, US_INV);
   1214         expected = expected.unescape();
   1215 
   1216         assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
   1217         if (DATA[i].timeType != timeType) {
   1218             dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
   1219                 + timeType + ", expected=" + DATA[i].timeType);
   1220         }
   1221     }
   1222 }
   1223 
   1224 void
   1225 TimeZoneFormatTest::TestFormatCustomZone(void) {
   1226     struct {
   1227         const char* id;
   1228         int32_t offset;
   1229         const char* expected;
   1230     } TESTDATA[] = {
   1231         { "abc", 3600000, "GMT+01:00" },                    // unknown ID
   1232         { "$abc", -3600000, "GMT-01:00" },                 // unknown, with ASCII variant char '$'
   1233         { "\\u00c1\\u00df\\u00c7", 5400000, "GMT+01:30"},    // unknown, with non-ASCII chars
   1234         { 0, 0, 0 }
   1235     };
   1236 
   1237     UDate now = Calendar::getNow();
   1238 
   1239     for (int32_t i = 0; ; i++) {
   1240         const char *id = TESTDATA[i].id;
   1241         if (id == 0) {
   1242             break;
   1243         }
   1244         UnicodeString tzid = UnicodeString(id, -1, US_INV).unescape();
   1245         SimpleTimeZone tz(TESTDATA[i].offset, tzid);
   1246 
   1247         UErrorCode status = U_ZERO_ERROR;
   1248         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale("en"), status));
   1249         if (tzfmt.isNull()) {
   1250             dataerrln("FAIL: TimeZoneFormat::createInstance failed for en");
   1251             return;
   1252         }
   1253         UnicodeString tzstr;
   1254         UnicodeString expected = UnicodeString(TESTDATA[i].expected, -1, US_INV).unescape();
   1255 
   1256         tzfmt->format(UTZFMT_STYLE_SPECIFIC_LONG, tz, now, tzstr, NULL);
   1257         assertEquals(UnicodeString("Format result for ") + tzid, expected, tzstr);
   1258     }
   1259 }
   1260 
   1261 void
   1262 TimeZoneFormatTest::TestFormatTZDBNamesAllZoneCoverage(void) {
   1263     UErrorCode status = U_ZERO_ERROR;
   1264     LocalPointer<StringEnumeration> tzids(TimeZone::createEnumeration());
   1265     if (tzids.getAlias() == nullptr) {
   1266         dataerrln("%s %d tzids is null", __FILE__, __LINE__);
   1267         return;
   1268     }
   1269     const UnicodeString *tzid;
   1270     LocalPointer<TimeZoneNames> tzdbNames(TimeZoneNames::createTZDBInstance(Locale("en"), status));
   1271     UDate now = Calendar::getNow();
   1272     UnicodeString mzId;
   1273     UnicodeString name;
   1274     while ((tzid = tzids->snext(status))) {
   1275         logln("Zone: " + *tzid);
   1276         LocalPointer<TimeZone> tz(TimeZone::createTimeZone(*tzid));
   1277         tzdbNames->getMetaZoneID(*tzid, now, mzId);
   1278         if (mzId.isBogus()) {
   1279             logln((UnicodeString)"Meta zone: <not available>");
   1280         } else {
   1281             logln((UnicodeString)"Meta zone: " + mzId);
   1282         }
   1283 
   1284         // mzID could be bogus here
   1285         tzdbNames->getMetaZoneDisplayName(mzId, UTZNM_SHORT_STANDARD, name);
   1286         // name could be bogus here
   1287         if (name.isBogus()) {
   1288             logln((UnicodeString)"Meta zone short standard name: <not available>");
   1289         }
   1290         else {
   1291             logln((UnicodeString)"Meta zone short standard name: " + name);
   1292         }
   1293 
   1294         tzdbNames->getMetaZoneDisplayName(mzId, UTZNM_SHORT_DAYLIGHT, name);
   1295         // name could be bogus here
   1296         if (name.isBogus()) {
   1297             logln((UnicodeString)"Meta zone short daylight name: <not available>");
   1298         }
   1299         else {
   1300             logln((UnicodeString)"Meta zone short daylight name: " + name);
   1301         }
   1302     }
   1303 }
   1304 
   1305 #endif /* #if !UCONFIG_NO_FORMATTING */
   1306