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