Home | History | Annotate | Download | only in intltest
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2015, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 */
      7 #include "unicode/utypes.h"
      8 
      9 #if !UCONFIG_NO_FORMATTING
     10 
     11 #include "tzfmttst.h"
     12 
     13 #include "simplethread.h"
     14 #include "unicode/timezone.h"
     15 #include "unicode/simpletz.h"
     16 #include "unicode/calendar.h"
     17 #include "unicode/strenum.h"
     18 #include "unicode/smpdtfmt.h"
     19 #include "unicode/uchar.h"
     20 #include "unicode/basictz.h"
     21 #include "unicode/tzfmt.h"
     22 #include "unicode/localpointer.h"
     23 #include "cstring.h"
     24 #include "zonemeta.h"
     25 
     26 static const char* PATTERNS[] = {
     27     "z",
     28     "zzzz",
     29     "Z",    // equivalent to "xxxx"
     30     "ZZZZ", // equivalent to "OOOO"
     31     "v",
     32     "vvvv",
     33     "O",
     34     "OOOO",
     35     "X",
     36     "XX",
     37     "XXX",
     38     "XXXX",
     39     "XXXXX",
     40     "x",
     41     "xx",
     42     "xxx",
     43     "xxxx",
     44     "xxxxx",
     45     "V",
     46     "VV",
     47     "VVV",
     48     "VVVV"
     49 };
     50 static const int NUM_PATTERNS = sizeof(PATTERNS)/sizeof(const char*);
     51 
     52 static const UChar ETC_UNKNOWN[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
     53 
     54 static const UChar ETC_SLASH[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
     55 static const UChar SYSTEMV_SLASH[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
     56 static const UChar RIYADH8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
     57 
     58 static UBool contains(const char** list, const char* str) {
     59     for (int32_t i = 0; list[i]; i++) {
     60         if (uprv_strcmp(list[i], str) == 0) {
     61             return TRUE;
     62         }
     63     }
     64     return FALSE;
     65 }
     66 
     67 void
     68 TimeZoneFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
     69 {
     70     if (exec) {
     71         logln("TestSuite TimeZoneFormatTest");
     72     }
     73     switch (index) {
     74         TESTCASE(0, TestTimeZoneRoundTrip);
     75         TESTCASE(1, TestTimeRoundTrip);
     76         TESTCASE(2, TestParse);
     77         TESTCASE(3, TestISOFormat);
     78         TESTCASE(4, TestFormat);
     79         TESTCASE(5, TestFormatTZDBNames);
     80         default: name = ""; break;
     81     }
     82 }
     83 
     84 void
     85 TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
     86     UErrorCode status = U_ZERO_ERROR;
     87 
     88     SimpleTimeZone unknownZone(-31415, ETC_UNKNOWN);
     89     int32_t badDstOffset = -1234;
     90     int32_t badZoneOffset = -2345;
     91 
     92     int32_t testDateData[][3] = {
     93         {2007, 1, 15},
     94         {2007, 6, 15},
     95         {1990, 1, 15},
     96         {1990, 6, 15},
     97         {1960, 1, 15},
     98         {1960, 6, 15},
     99     };
    100 
    101     Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString)"UTC"), status);
    102     if (U_FAILURE(status)) {
    103         dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
    104         return;
    105     }
    106 
    107     // Set up rule equivalency test range
    108     UDate low, high;
    109     cal->set(1900, UCAL_JANUARY, 1);
    110     low = cal->getTime(status);
    111     cal->set(2040, UCAL_JANUARY, 1);
    112     high = cal->getTime(status);
    113     if (U_FAILURE(status)) {
    114         errln("getTime failed");
    115         return;
    116     }
    117 
    118     // Set up test dates
    119     UDate DATES[(sizeof(testDateData)/sizeof(int32_t))/3];
    120     const int32_t nDates = (sizeof(testDateData)/sizeof(int32_t))/3;
    121     cal->clear();
    122     for (int32_t i = 0; i < nDates; i++) {
    123         cal->set(testDateData[i][0], testDateData[i][1], testDateData[i][2]);
    124         DATES[i] = cal->getTime(status);
    125         if (U_FAILURE(status)) {
    126             errln("getTime failed");
    127             return;
    128         }
    129     }
    130 
    131     // Set up test locales
    132     const Locale testLocales[] = {
    133         Locale("en"),
    134         Locale("en_CA"),
    135         Locale("fr"),
    136         Locale("zh_Hant")
    137     };
    138 
    139     const Locale *LOCALES;
    140     int32_t nLocales;
    141 
    142     if (quick) {
    143         LOCALES = testLocales;
    144         nLocales = sizeof(testLocales)/sizeof(Locale);
    145     } else {
    146         LOCALES = Locale::getAvailableLocales(nLocales);
    147     }
    148 
    149     StringEnumeration *tzids = TimeZone::createEnumeration();
    150     int32_t inRaw, inDst;
    151     int32_t outRaw, outDst;
    152 
    153     // Run the roundtrip test
    154     for (int32_t locidx = 0; locidx < nLocales; locidx++) {
    155         UnicodeString localGMTString;
    156         SimpleDateFormat gmtFmt(UnicodeString("ZZZZ"), LOCALES[locidx], status);
    157         if (U_FAILURE(status)) {
    158             dataerrln("Error creating SimpleDateFormat - %s", u_errorName(status));
    159             continue;
    160         }
    161         gmtFmt.setTimeZone(*TimeZone::getGMT());
    162         gmtFmt.format(0.0, localGMTString);
    163 
    164         for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) {
    165 
    166             SimpleDateFormat *sdf = new SimpleDateFormat((UnicodeString)PATTERNS[patidx], LOCALES[locidx], status);
    167             if (U_FAILURE(status)) {
    168                 dataerrln((UnicodeString)"new SimpleDateFormat failed for pattern " +
    169                     PATTERNS[patidx] + " for locale " + LOCALES[locidx].getName() + " - " + u_errorName(status));
    170                 status = U_ZERO_ERROR;
    171                 continue;
    172             }
    173 
    174             tzids->reset(status);
    175             const UnicodeString *tzid;
    176             while ((tzid = tzids->snext(status))) {
    177                 TimeZone *tz = TimeZone::createTimeZone(*tzid);
    178 
    179                 for (int32_t datidx = 0; datidx < nDates; datidx++) {
    180                     UnicodeString tzstr;
    181                     FieldPosition fpos(0);
    182                     // Format
    183                     sdf->setTimeZone(*tz);
    184                     sdf->format(DATES[datidx], tzstr, fpos);
    185 
    186                     // Before parse, set unknown zone to SimpleDateFormat instance
    187                     // just for making sure that it does not depends on the time zone
    188                     // originally set.
    189                     sdf->setTimeZone(unknownZone);
    190 
    191                     // Parse
    192                     ParsePosition pos(0);
    193                     Calendar *outcal = Calendar::createInstance(unknownZone, status);
    194                     if (U_FAILURE(status)) {
    195                         errln("Failed to create an instance of calendar for receiving parse result.");
    196                         status = U_ZERO_ERROR;
    197                         continue;
    198                     }
    199                     outcal->set(UCAL_DST_OFFSET, badDstOffset);
    200                     outcal->set(UCAL_ZONE_OFFSET, badZoneOffset);
    201 
    202                     sdf->parse(tzstr, *outcal, pos);
    203 
    204                     // Check the result
    205                     const TimeZone &outtz = outcal->getTimeZone();
    206                     UnicodeString outtzid;
    207                     outtz.getID(outtzid);
    208 
    209                     tz->getOffset(DATES[datidx], false, inRaw, inDst, status);
    210                     if (U_FAILURE(status)) {
    211                         errln((UnicodeString)"Failed to get offsets from time zone" + *tzid);
    212                         status = U_ZERO_ERROR;
    213                     }
    214                     outtz.getOffset(DATES[datidx], false, outRaw, outDst, status);
    215                     if (U_FAILURE(status)) {
    216                         errln((UnicodeString)"Failed to get offsets from time zone" + outtzid);
    217                         status = U_ZERO_ERROR;
    218                     }
    219 
    220                     if (uprv_strcmp(PATTERNS[patidx], "V") == 0) {
    221                         // Short zone ID - should support roundtrip for canonical CLDR IDs
    222                         UnicodeString canonicalID;
    223                         TimeZone::getCanonicalID(*tzid, canonicalID, status);
    224                         if (U_FAILURE(status)) {
    225                             // Uknown ID - we should not get here
    226                             errln((UnicodeString)"Unknown ID " + *tzid);
    227                             status = U_ZERO_ERROR;
    228                         } else if (outtzid != canonicalID) {
    229                             if (outtzid.compare(ETC_UNKNOWN, -1) == 0) {
    230                                 // Note that some zones like Asia/Riyadh87 does not have
    231                                 // short zone ID and "unk" is used as fallback
    232                                 logln((UnicodeString)"Canonical round trip failed (probably as expected); tz=" + *tzid
    233                                         + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    234                                         + ", time=" + DATES[datidx] + ", str=" + tzstr
    235                                         + ", outtz=" + outtzid);
    236                             } else {
    237                                 errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
    238                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    239                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    240                                     + ", outtz=" + outtzid);
    241                             }
    242                         }
    243                     } else if (uprv_strcmp(PATTERNS[patidx], "VV") == 0) {
    244                         // Zone ID - full roundtrip support
    245                         if (outtzid != *tzid) {
    246                             errln((UnicodeString)"Zone ID round trip failued; tz="  + *tzid
    247                                 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    248                                 + ", time=" + DATES[datidx] + ", str=" + tzstr
    249                                 + ", outtz=" + outtzid);
    250                         }
    251                     } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0 || uprv_strcmp(PATTERNS[patidx], "VVVV") == 0) {
    252                         // Location: time zone rule must be preserved except
    253                         // zones not actually associated with a specific location.
    254                         // Time zones in this category do not have "/" in its ID.
    255                         UnicodeString canonical;
    256                         TimeZone::getCanonicalID(*tzid, canonical, status);
    257                         if (U_FAILURE(status)) {
    258                             // Uknown ID - we should not get here
    259                             errln((UnicodeString)"Unknown ID " + *tzid);
    260                             status = U_ZERO_ERROR;
    261                         } else if (outtzid != canonical) {
    262                             // Canonical ID did not match - check the rules
    263                             if (!((BasicTimeZone*)&outtz)->hasEquivalentTransitions((BasicTimeZone&)*tz, low, high, TRUE, status)) {
    264                                 if (canonical.indexOf((UChar)0x27 /*'/'*/) == -1) {
    265                                     // Exceptional cases, such as CET, EET, MET and WET
    266                                     logln((UnicodeString)"Canonical round trip failed (as expected); tz=" + *tzid
    267                                             + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    268                                             + ", time=" + DATES[datidx] + ", str=" + tzstr
    269                                             + ", outtz=" + outtzid);
    270                                 } else {
    271                                     errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
    272                                         + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    273                                         + ", time=" + DATES[datidx] + ", str=" + tzstr
    274                                         + ", outtz=" + outtzid);
    275                                 }
    276                                 if (U_FAILURE(status)) {
    277                                     errln("hasEquivalentTransitions failed");
    278                                     status = U_ZERO_ERROR;
    279                                 }
    280                             }
    281                         }
    282 
    283                     } else {
    284                         UBool isOffsetFormat = (*PATTERNS[patidx] == 'Z'
    285                                                 || *PATTERNS[patidx] == 'O'
    286                                                 || *PATTERNS[patidx] == 'X'
    287                                                 || *PATTERNS[patidx] == 'x');
    288                         UBool minutesOffset = FALSE;
    289                         if (*PATTERNS[patidx] == 'X' || *PATTERNS[patidx] == 'x') {
    290                             minutesOffset = (uprv_strlen(PATTERNS[patidx]) <= 3);
    291                         }
    292 
    293                         if (!isOffsetFormat) {
    294                             // Check if localized GMT format is used as a fallback of name styles
    295                             int32_t numDigits = 0;
    296                             for (int n = 0; n < tzstr.length(); n++) {
    297                                 if (u_isdigit(tzstr.charAt(n))) {
    298                                     numDigits++;
    299                                 }
    300                             }
    301                             isOffsetFormat = (numDigits > 0);
    302                         }
    303                         if (isOffsetFormat || tzstr == localGMTString) {
    304                             // Localized GMT or ISO: total offset (raw + dst) must be preserved.
    305                             int32_t inOffset = inRaw + inDst;
    306                             int32_t outOffset = outRaw + outDst;
    307                             int32_t diff = outOffset - inOffset;
    308                             if (minutesOffset) {
    309                                 diff = (diff / 60000) * 60000;
    310                             }
    311                             if (diff != 0) {
    312                                 errln((UnicodeString)"Offset round trip failed; tz=" + *tzid
    313                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    314                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    315                                     + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
    316                             }
    317                         } else {
    318                             // Specific or generic: raw offset must be preserved.
    319                             if (inRaw != outRaw) {
    320                                 errln((UnicodeString)"Raw offset round trip failed; tz=" + *tzid
    321                                     + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    322                                     + ", time=" + DATES[datidx] + ", str=" + tzstr
    323                                     + ", inRawOffset=" + inRaw + ", outRawOffset=" + outRaw);
    324                             }
    325                         }
    326                     }
    327                     delete outcal;
    328                 }
    329                 delete tz;
    330             }
    331             delete sdf;
    332         }
    333     }
    334     delete cal;
    335     delete tzids;
    336 }
    337 
    338 // Special exclusions in TestTimeZoneRoundTrip.
    339 // These special cases do not round trip time as designed.
    340 static UBool isSpecialTimeRoundTripCase(const char* loc,
    341                                         const UnicodeString& id,
    342                                         const char* pattern,
    343                                         UDate time) {
    344     struct {
    345         const char* loc;
    346         const char* id;
    347         const char* pattern;
    348         UDate time;
    349     } EXCLUSIONS[] = {
    350         {NULL, "Asia/Chita", "zzzz", 1414252800000.0},
    351         {NULL, "Asia/Chita", "vvvv", 1414252800000.0},
    352         {NULL, "Asia/Srednekolymsk", "zzzz", 1414241999999.0},
    353         {NULL, "Asia/Srednekolymsk", "vvvv", 1414241999999.0},
    354         {NULL, NULL, NULL, U_DATE_MIN}
    355     };
    356 
    357     UBool isExcluded = FALSE;
    358     for (int32_t i = 0; EXCLUSIONS[i].id != NULL; i++) {
    359         if (EXCLUSIONS[i].loc == NULL || uprv_strcmp(loc, EXCLUSIONS[i].loc) == 0) {
    360             if (id.compare(EXCLUSIONS[i].id) == 0) {
    361                 if (EXCLUSIONS[i].pattern == NULL || uprv_strcmp(pattern, EXCLUSIONS[i].pattern) == 0) {
    362                     if (EXCLUSIONS[i].time == U_DATE_MIN || EXCLUSIONS[i].time == time) {
    363                         isExcluded = TRUE;
    364                     }
    365                 }
    366             }
    367         }
    368     }
    369     return isExcluded;
    370 }
    371 
    372 struct LocaleData {
    373     int32_t index;
    374     int32_t testCounts;
    375     UDate *times;
    376     const Locale* locales; // Static
    377     int32_t nLocales; // Static
    378     UBool quick; // Static
    379     UDate START_TIME; // Static
    380     UDate END_TIME; // Static
    381     int32_t numDone;
    382 };
    383 
    384 class TestTimeRoundTripThread: public SimpleThread {
    385 public:
    386     TestTimeRoundTripThread(IntlTest& tlog, LocaleData &ld, int32_t i)
    387         : log(tlog), data(ld), index(i) {}
    388     virtual void run() {
    389         UErrorCode status = U_ZERO_ERROR;
    390         UBool REALLY_VERBOSE = FALSE;
    391 
    392         // These patterns are ambiguous at DST->STD local time overlap
    393         const char* AMBIGUOUS_DST_DECESSION[] = { "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
    394 
    395         // These patterns are ambiguous at STD->STD/DST->DST local time overlap
    396         const char* AMBIGUOUS_NEGATIVE_SHIFT[] = { "z", "zzzz", "v", "vvvv", "V", "VV", "VVV", "VVVV", 0 };
    397 
    398         // These patterns only support integer minutes offset
    399         const char* MINUTES_OFFSET[] = { "X", "XX", "XXX", "x", "xx", "xxx", 0 };
    400 
    401         // Workaround for #6338
    402         //UnicodeString BASEPATTERN("yyyy-MM-dd'T'HH:mm:ss.SSS");
    403         UnicodeString BASEPATTERN("yyyy.MM.dd HH:mm:ss.SSS");
    404 
    405         // timer for performance analysis
    406         UDate timer;
    407         UDate testTimes[4];
    408         UBool expectedRoundTrip[4];
    409         int32_t testLen = 0;
    410 
    411         StringEnumeration *tzids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
    412         if (U_FAILURE(status)) {
    413             if (status == U_MISSING_RESOURCE_ERROR) {
    414                 /* This error is generally caused by data not being present. However, an infinite loop will occur
    415                  * because the thread thinks that the test data is never done so we should treat the data as done.
    416                  */
    417                 log.dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status));
    418                 data.numDone = data.nLocales;
    419             } else {
    420                 log.errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status));
    421             }
    422             return;
    423         }
    424 
    425         int32_t locidx = -1;
    426         UDate times[NUM_PATTERNS];
    427         for (int32_t i = 0; i < NUM_PATTERNS; i++) {
    428             times[i] = 0;
    429         }
    430 
    431         int32_t testCounts = 0;
    432 
    433         while (true) {
    434             umtx_lock(NULL); // Lock to increment the index
    435             for (int32_t i = 0; i < NUM_PATTERNS; i++) {
    436                 data.times[i] += times[i];
    437                 data.testCounts += testCounts;
    438             }
    439             if (data.index < data.nLocales) {
    440                 locidx = data.index;
    441                 data.index++;
    442             } else {
    443                 locidx = -1;
    444             }
    445             umtx_unlock(NULL); // Unlock for other threads to use
    446 
    447             if (locidx == -1) {
    448                 log.logln((UnicodeString) "Thread " + index + " is done.");
    449                 break;
    450             }
    451 
    452             log.logln((UnicodeString) "\nThread " + index + ": Locale: " + UnicodeString(data.locales[locidx].getName()));
    453 
    454             for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) {
    455                 log.logln((UnicodeString) "    Pattern: " + PATTERNS[patidx]);
    456                 times[patidx] = 0;
    457 
    458                 UnicodeString pattern(BASEPATTERN);
    459                 pattern.append(" ").append(PATTERNS[patidx]);
    460 
    461                 SimpleDateFormat *sdf = new SimpleDateFormat(pattern, data.locales[locidx], status);
    462                 if (U_FAILURE(status)) {
    463                     log.errcheckln(status, (UnicodeString) "new SimpleDateFormat failed for pattern " +
    464                         pattern + " for locale " + data.locales[locidx].getName() + " - " + u_errorName(status));
    465                     status = U_ZERO_ERROR;
    466                     continue;
    467                 }
    468 
    469                 UBool minutesOffset = contains(MINUTES_OFFSET, PATTERNS[patidx]);
    470 
    471                 tzids->reset(status);
    472                 const UnicodeString *tzid;
    473 
    474                 timer = Calendar::getNow();
    475 
    476                 while ((tzid = tzids->snext(status))) {
    477                     if (uprv_strcmp(PATTERNS[patidx], "V") == 0) {
    478                         // Some zones do not have short ID assigned, such as Asia/Riyadh87.
    479                         // The time roundtrip will fail for such zones with pattern "V" (short zone ID).
    480                         // This is expected behavior.
    481                         const UChar* shortZoneID = ZoneMeta::getShortID(*tzid);
    482                         if (shortZoneID == NULL) {
    483                             continue;
    484                         }
    485                     } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0) {
    486                         // Some zones are not associated with any region, such as Etc/GMT+8.
    487                         // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location).
    488                         // This is expected behavior.
    489                         if (tzid->indexOf((UChar)0x2F) < 0 || tzid->indexOf(ETC_SLASH, -1, 0) >= 0
    490                             || tzid->indexOf(SYSTEMV_SLASH, -1, 0) >= 0 || tzid->indexOf(RIYADH8, -1, 0) >= 0) {
    491                             continue;
    492                         }
    493                     }
    494 
    495                     if (*tzid == "Pacific/Apia" && uprv_strcmp(PATTERNS[patidx], "vvvv") == 0
    496                             && log.logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
    497                         continue;
    498                     }
    499 
    500                     BasicTimeZone *tz = (BasicTimeZone*) TimeZone::createTimeZone(*tzid);
    501                     sdf->setTimeZone(*tz);
    502 
    503                     UDate t = data.START_TIME;
    504                     TimeZoneTransition tzt;
    505                     UBool tztAvail = FALSE;
    506                     UBool middle = TRUE;
    507 
    508                     while (t < data.END_TIME) {
    509                         if (!tztAvail) {
    510                             testTimes[0] = t;
    511                             expectedRoundTrip[0] = TRUE;
    512                             testLen = 1;
    513                         } else {
    514                             int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
    515                             int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
    516                             int32_t delta = toOffset - fromOffset;
    517                             if (delta < 0) {
    518                                 UBool isDstDecession = tzt.getFrom()->getDSTSavings() > 0 && tzt.getTo()->getDSTSavings() == 0;
    519                                 testTimes[0] = t + delta - 1;
    520                                 expectedRoundTrip[0] = TRUE;
    521                                 testTimes[1] = t + delta;
    522                                 expectedRoundTrip[1] = isDstDecession ?
    523                                     !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
    524                                     !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
    525                                 testTimes[2] = t - 1;
    526                                 expectedRoundTrip[2] = isDstDecession ?
    527                                     !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
    528                                     !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
    529                                 testTimes[3] = t;
    530                                 expectedRoundTrip[3] = TRUE;
    531                                 testLen = 4;
    532                             } else {
    533                                 testTimes[0] = t - 1;
    534                                 expectedRoundTrip[0] = TRUE;
    535                                 testTimes[1] = t;
    536                                 expectedRoundTrip[1] = TRUE;
    537                                 testLen = 2;
    538                             }
    539                         }
    540                         for (int32_t testidx = 0; testidx < testLen; testidx++) {
    541                             if (data.quick) {
    542                                 // reduce regular test time
    543                                 if (!expectedRoundTrip[testidx]) {
    544                                     continue;
    545                                 }
    546                             }
    547 
    548                             testCounts++;
    549 
    550                             UnicodeString text;
    551                             FieldPosition fpos(0);
    552                             sdf->format(testTimes[testidx], text, fpos);
    553 
    554                             UDate parsedDate = sdf->parse(text, status);
    555                             if (U_FAILURE(status)) {
    556                                 log.errln((UnicodeString) "Parse failure for text=" + text + ", tzid=" + *tzid + ", locale=" + data.locales[locidx].getName()
    557                                         + ", pattern=" + PATTERNS[patidx] + ", time=" + testTimes[testidx]);
    558                                 status = U_ZERO_ERROR;
    559                                 continue;
    560                             }
    561 
    562                             int32_t timeDiff = (int32_t)(parsedDate - testTimes[testidx]);
    563                             UBool bTimeMatch = minutesOffset ?
    564                                 (timeDiff/60000)*60000 == 0 : timeDiff == 0;
    565                             if (!bTimeMatch) {
    566                                 UnicodeString msg = (UnicodeString) "Time round trip failed for " + "tzid=" + *tzid + ", locale=" + data.locales[locidx].getName() + ", pattern=" + PATTERNS[patidx]
    567                                         + ", text=" + text + ", time=" + testTimes[testidx] + ", restime=" + parsedDate + ", diff=" + (parsedDate - testTimes[testidx]);
    568                                 // Timebomb for TZData update
    569                                 if (expectedRoundTrip[testidx]
    570                                         && !isSpecialTimeRoundTripCase(data.locales[locidx].getName(), *tzid,
    571                                                 PATTERNS[patidx], testTimes[testidx])) {
    572                                     log.errln((UnicodeString) "FAIL: " + msg);
    573                                 } else if (REALLY_VERBOSE) {
    574                                     log.logln(msg);
    575                                 }
    576                             }
    577                         }
    578                         tztAvail = tz->getNextTransition(t, FALSE, tzt);
    579                         if (!tztAvail) {
    580                             break;
    581                         }
    582                         if (middle) {
    583                             // Test the date in the middle of two transitions.
    584                             t += (int64_t) ((tzt.getTime() - t) / 2);
    585                             middle = FALSE;
    586                             tztAvail = FALSE;
    587                         } else {
    588                             t = tzt.getTime();
    589                         }
    590                     }
    591                     delete tz;
    592                 }
    593                 times[patidx] += (Calendar::getNow() - timer);
    594                 delete sdf;
    595             }
    596             umtx_lock(NULL);
    597             data.numDone++;
    598             umtx_unlock(NULL);
    599         }
    600         delete tzids;
    601     }
    602 private:
    603     IntlTest& log;
    604     LocaleData& data;
    605     int32_t index;
    606 };
    607 
    608 void
    609 TimeZoneFormatTest::TestTimeRoundTrip(void) {
    610     int32_t nThreads = threadCount;
    611     const Locale *LOCALES;
    612     int32_t nLocales;
    613     int32_t testCounts = 0;
    614 
    615     UErrorCode status = U_ZERO_ERROR;
    616     Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString) "UTC"), status);
    617     if (U_FAILURE(status)) {
    618         dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
    619         return;
    620     }
    621 
    622     const char* testAllProp = getProperty("TimeZoneRoundTripAll");
    623     UBool bTestAll = (testAllProp && uprv_strcmp(testAllProp, "true") == 0);
    624 
    625     UDate START_TIME, END_TIME;
    626     if (bTestAll || !quick) {
    627         cal->set(1900, UCAL_JANUARY, 1);
    628     } else {
    629         cal->set(1990, UCAL_JANUARY, 1);
    630     }
    631     START_TIME = cal->getTime(status);
    632 
    633     cal->set(2015, UCAL_JANUARY, 1);
    634     END_TIME = cal->getTime(status);
    635 
    636     if (U_FAILURE(status)) {
    637         errln("getTime failed");
    638         return;
    639     }
    640 
    641     UDate times[NUM_PATTERNS];
    642     for (int32_t i = 0; i < NUM_PATTERNS; i++) {
    643         times[i] = 0;
    644     }
    645 
    646     // Set up test locales
    647     const Locale locales1[] = {Locale("en")};
    648     const Locale locales2[] = {
    649         Locale("ar_EG"), Locale("bg_BG"), Locale("ca_ES"), Locale("da_DK"), Locale("de"),
    650         Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"),
    651         Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"),
    652         Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"),
    653         Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"),
    654         Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"),
    655         Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"),
    656         Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"),
    657         Locale("zh_Hant"), Locale("zh_Hant_TW")
    658     };
    659 
    660     if (bTestAll) {
    661         LOCALES = Locale::getAvailableLocales(nLocales);
    662     } else if (quick) {
    663         LOCALES = locales1;
    664         nLocales = sizeof(locales1)/sizeof(Locale);
    665     } else {
    666         LOCALES = locales2;
    667         nLocales = sizeof(locales2)/sizeof(Locale);
    668     }
    669 
    670     LocaleData data;
    671     data.index = 0;
    672     data.testCounts = testCounts;
    673     data.times = times;
    674     data.locales = LOCALES;
    675     data.nLocales = nLocales;
    676     data.quick = quick;
    677     data.START_TIME = START_TIME;
    678     data.END_TIME = END_TIME;
    679     data.numDone = 0;
    680 
    681     TestTimeRoundTripThread **threads = new TestTimeRoundTripThread*[nThreads];
    682     int32_t i;
    683     for (i = 0; i < nThreads; i++) {
    684         threads[i] = new TestTimeRoundTripThread(*this, data, i);
    685         if (threads[i]->start() != 0) {
    686             errln("Error starting thread %d", i);
    687         }
    688     }
    689 
    690     for (i = 0; i < nThreads; i++) {
    691         threads[i]->join();
    692     }
    693     if (data.numDone != nLocales) {
    694         errln("%s:%d data.numDone = %d, nLocales = %d", __FILE__, __LINE__, data.numDone, nLocales);
    695     }
    696 
    697     for (i = 0; i < nThreads; i++) {
    698         delete threads[i];
    699     }
    700     delete [] threads;
    701 
    702     UDate total = 0;
    703     logln("### Elapsed time by patterns ###");
    704     for (int32_t i = 0; i < NUM_PATTERNS; i++) {
    705         logln(UnicodeString("") + data.times[i] + "ms (" + PATTERNS[i] + ")");
    706         total += data.times[i];
    707     }
    708     logln((UnicodeString) "Total: " + total + "ms");
    709     logln((UnicodeString) "Iteration: " + data.testCounts);
    710 
    711     delete cal;
    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                 errln((UnicodeString)"Fail: 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 < sizeof(OFFSET)/sizeof(OFFSET[0]); 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 
   1225 #endif /* #if !UCONFIG_NO_FORMATTING */
   1226