Home | History | Annotate | Download | only in intltest
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2014, 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 #if (ICU_USE_THREADS==0)
    682     TestTimeRoundTripThread fakeThread(*this, data, 0);
    683     fakeThread.run();
    684 #else
    685     TestTimeRoundTripThread **threads = new TestTimeRoundTripThread*[threadCount];
    686     int32_t i;
    687     for (i = 0; i < nThreads; i++) {
    688         threads[i] = new TestTimeRoundTripThread(*this, data, i);
    689         if (threads[i]->start() != 0) {
    690             errln("Error starting thread %d", i);
    691         }
    692     }
    693 
    694     UBool done = false;
    695     while (true) {
    696         umtx_lock(NULL);
    697         if (data.numDone == nLocales) {
    698             done = true;
    699         }
    700         umtx_unlock(NULL);
    701         if (done)
    702             break;
    703         SimpleThread::sleep(1000);
    704     }
    705 
    706     for (i = 0; i < nThreads; i++) {
    707         delete threads[i];
    708     }
    709     delete [] threads;
    710 
    711 #endif
    712     UDate total = 0;
    713     logln("### Elapsed time by patterns ###");
    714     for (int32_t i = 0; i < NUM_PATTERNS; i++) {
    715         logln(UnicodeString("") + data.times[i] + "ms (" + PATTERNS[i] + ")");
    716         total += data.times[i];
    717     }
    718     logln((UnicodeString) "Total: " + total + "ms");
    719     logln((UnicodeString) "Iteration: " + data.testCounts);
    720 
    721     delete cal;
    722 }
    723 
    724 
    725 typedef struct {
    726     const char*     text;
    727     int32_t         inPos;
    728     const char*     locale;
    729     UTimeZoneFormatStyle    style;
    730     uint32_t        parseOptions;
    731     const char*     expected;
    732     int32_t         outPos;
    733     UTimeZoneFormatTimeType timeType;
    734 } ParseTestData;
    735 
    736 void
    737 TimeZoneFormatTest::TestParse(void) {
    738     const ParseTestData DATA[] = {
    739         //   text               inPos   locale      style
    740         //      parseOptions                        expected            outPos  timeType
    741             {"Z",               0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    742                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    743 
    744             {"Z",               0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    745                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    746 
    747             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    748                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "Etc/GMT",          1,      UTZFMT_TIME_TYPE_UNKNOWN},
    749 
    750             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_GENERIC_LOCATION,
    751                 UTZFMT_PARSE_OPTION_NONE,           "Africa/Lusaka",    11,     UTZFMT_TIME_TYPE_UNKNOWN},
    752 
    753             {"Zambia time",     0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    754                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "Africa/Lusaka",    11,     UTZFMT_TIME_TYPE_UNKNOWN},
    755 
    756             {"+00:00",          0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    757                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          6,      UTZFMT_TIME_TYPE_UNKNOWN},
    758 
    759             {"-01:30:45",       0,      "en_US",    UTZFMT_STYLE_ISO_EXTENDED_FULL,
    760                 UTZFMT_PARSE_OPTION_NONE,           "GMT-01:30:45",     9,      UTZFMT_TIME_TYPE_UNKNOWN},
    761 
    762             {"-7",              0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    763                 UTZFMT_PARSE_OPTION_NONE,           "GMT-07:00",        2,      UTZFMT_TIME_TYPE_UNKNOWN},
    764 
    765             {"-2222",           0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    766                 UTZFMT_PARSE_OPTION_NONE,           "GMT-22:22",        5,      UTZFMT_TIME_TYPE_UNKNOWN},
    767 
    768             {"-3333",           0,      "en_US",    UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
    769                 UTZFMT_PARSE_OPTION_NONE,           "GMT-03:33",        4,      UTZFMT_TIME_TYPE_UNKNOWN},
    770 
    771             {"XXX+01:30YYY",    3,      "en_US",    UTZFMT_STYLE_LOCALIZED_GMT,
    772                 UTZFMT_PARSE_OPTION_NONE,           "GMT+01:30",        9,      UTZFMT_TIME_TYPE_UNKNOWN},
    773 
    774             {"GMT0",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    775                 UTZFMT_PARSE_OPTION_NONE,           "Etc/GMT",          3,      UTZFMT_TIME_TYPE_UNKNOWN},
    776 
    777             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    778                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    779 
    780             {"ESTx",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    781                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    782 
    783             {"EDTx",            0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    784                 UTZFMT_PARSE_OPTION_NONE,           "America/New_York", 3,      UTZFMT_TIME_TYPE_DAYLIGHT},
    785 
    786             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    787                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    788 
    789             {"EST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_LONG,
    790                 UTZFMT_PARSE_OPTION_ALL_STYLES,     "America/New_York", 3,      UTZFMT_TIME_TYPE_STANDARD},
    791 
    792             {"EST",             0,      "en_CA",    UTZFMT_STYLE_SPECIFIC_SHORT,
    793                 UTZFMT_PARSE_OPTION_NONE,           "America/Toronto",  3,      UTZFMT_TIME_TYPE_STANDARD},
    794 
    795             {"CST",             0,      "en_US",    UTZFMT_STYLE_SPECIFIC_SHORT,
    796                 UTZFMT_PARSE_OPTION_NONE,           "America/Chicago",  3,      UTZFMT_TIME_TYPE_STANDARD},
    797 
    798             {"CST",             0,      "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    799                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    800 
    801             {"CST",             0,      "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    802                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "America/Chicago",  3,  UTZFMT_TIME_TYPE_STANDARD},
    803 
    804             {"--CST--",           2,    "en_GB",    UTZFMT_STYLE_SPECIFIC_SHORT,
    805                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "America/Chicago",  5,  UTZFMT_TIME_TYPE_STANDARD},
    806 
    807             {"CST",             0,      "zh_CN",    UTZFMT_STYLE_SPECIFIC_SHORT,
    808                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Asia/Shanghai",    3,  UTZFMT_TIME_TYPE_STANDARD},
    809 
    810             {"AEST",            0,      "en_AU",    UTZFMT_STYLE_SPECIFIC_SHORT,
    811                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Australia/Sydney", 4,  UTZFMT_TIME_TYPE_STANDARD},
    812 
    813             {"AST",             0,      "ar_SA",    UTZFMT_STYLE_SPECIFIC_SHORT,
    814                 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS,  "Asia/Riyadh",      3,  UTZFMT_TIME_TYPE_STANDARD},
    815 
    816             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    817                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    818 
    819             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    820                 UTZFMT_PARSE_OPTION_ALL_STYLES,     NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN},
    821 
    822             {"AQTST",           0,      "en",       UTZFMT_STYLE_SPECIFIC_LONG,
    823                 UTZFMT_PARSE_OPTION_ALL_STYLES | UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Aqtobe",  5,  UTZFMT_TIME_TYPE_DAYLIGHT},
    824 
    825             {NULL,              0,      NULL,       UTZFMT_STYLE_GENERIC_LOCATION,
    826                 UTZFMT_PARSE_OPTION_NONE,           NULL,               0,      UTZFMT_TIME_TYPE_UNKNOWN}
    827     };
    828 
    829     for (int32_t i = 0; DATA[i].text; i++) {
    830         UErrorCode status = U_ZERO_ERROR;
    831         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
    832         if (U_FAILURE(status)) {
    833             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
    834             continue;
    835         }
    836         UTimeZoneFormatTimeType ttype = UTZFMT_TIME_TYPE_UNKNOWN;
    837         ParsePosition pos(DATA[i].inPos);
    838         TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype);
    839 
    840         UnicodeString errMsg;
    841         if (tz) {
    842             UnicodeString outID;
    843             tz->getID(outID);
    844             if (outID != UnicodeString(DATA[i].expected)) {
    845                 errMsg = (UnicodeString)"Time zone ID: " + outID + " - expected: " + DATA[i].expected;
    846             } else if (pos.getIndex() != DATA[i].outPos) {
    847                 errMsg = (UnicodeString)"Parsed pos: " + pos.getIndex() + " - expected: " + DATA[i].outPos;
    848             } else if (ttype != DATA[i].timeType) {
    849                 errMsg = (UnicodeString)"Time type: " + ttype + " - expected: " + DATA[i].timeType;
    850             }
    851             delete tz;
    852         } else {
    853             if (DATA[i].expected) {
    854                 errln((UnicodeString)"Fail: Parse failure - expected: " + DATA[i].expected);
    855             }
    856         }
    857         if (errMsg.length() > 0) {
    858             errln((UnicodeString)"Fail: " + errMsg + " [text=" + DATA[i].text + ", pos=" + DATA[i].inPos + ", style=" + DATA[i].style + "]");
    859         }
    860     }
    861 }
    862 
    863 void
    864 TimeZoneFormatTest::TestISOFormat(void) {
    865     const int32_t OFFSET[] = {
    866         0,          // 0
    867         999,        // 0.999s
    868         -59999,     // -59.999s
    869         60000,      // 1m
    870         -77777,     // -1m 17.777s
    871         1800000,    // 30m
    872         -3600000,   // -1h
    873         36000000,   // 10h
    874         -37800000,  // -10h 30m
    875         -37845000,  // -10h 30m 45s
    876         108000000,  // 30h
    877     };
    878 
    879     const char* ISO_STR[][11] = {
    880         // 0
    881         {
    882             "Z", "Z", "Z", "Z", "Z",
    883             "+00", "+0000", "+00:00", "+0000", "+00:00",
    884             "+0000"
    885         },
    886         // 999
    887         {
    888             "Z", "Z", "Z", "Z", "Z",
    889             "+00", "+0000", "+00:00", "+0000", "+00:00",
    890             "+0000"
    891         },
    892         // -59999
    893         {
    894             "Z", "Z", "Z", "-000059", "-00:00:59",
    895             "+00", "+0000", "+00:00", "-000059", "-00:00:59",
    896             "-000059"
    897         },
    898         // 60000
    899         {
    900             "+0001", "+0001", "+00:01", "+0001", "+00:01",
    901             "+0001", "+0001", "+00:01", "+0001", "+00:01",
    902             "+0001"
    903         },
    904         // -77777
    905         {
    906             "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
    907             "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
    908             "-000117"
    909         },
    910         // 1800000
    911         {
    912             "+0030", "+0030", "+00:30", "+0030", "+00:30",
    913             "+0030", "+0030", "+00:30", "+0030", "+00:30",
    914             "+0030"
    915         },
    916         // -3600000
    917         {
    918             "-01", "-0100", "-01:00", "-0100", "-01:00",
    919             "-01", "-0100", "-01:00", "-0100", "-01:00",
    920             "-0100"
    921         },
    922         // 36000000
    923         {
    924             "+10", "+1000", "+10:00", "+1000", "+10:00",
    925             "+10", "+1000", "+10:00", "+1000", "+10:00",
    926             "+1000"
    927         },
    928         // -37800000
    929         {
    930             "-1030", "-1030", "-10:30", "-1030", "-10:30",
    931             "-1030", "-1030", "-10:30", "-1030", "-10:30",
    932             "-1030"
    933         },
    934         // -37845000
    935         {
    936             "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
    937             "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
    938             "-103045"
    939         },
    940         // 108000000
    941         {
    942             0, 0, 0, 0, 0,
    943             0, 0, 0, 0, 0,
    944             0
    945         }
    946     };
    947 
    948     const char* PATTERN[] = {
    949         "X", "XX", "XXX", "XXXX", "XXXXX",
    950         "x", "xx", "xxx", "xxxx", "xxxxx",
    951         "Z", // equivalent to "xxxx"
    952         0
    953     };
    954 
    955     const int32_t MIN_OFFSET_UNIT[] = {
    956         60000, 60000, 60000, 1000, 1000,
    957         60000, 60000, 60000, 1000, 1000,
    958         1000,
    959     };
    960 
    961     // Formatting
    962     UErrorCode status = U_ZERO_ERROR;
    963     LocalPointer<SimpleDateFormat> sdf(new SimpleDateFormat(status), status);
    964     if (U_FAILURE(status)) {
    965         dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status));
    966         return;
    967     }
    968     UDate d = Calendar::getNow();
    969 
    970     for (uint32_t i = 0; i < sizeof(OFFSET)/sizeof(OFFSET[0]); i++) {
    971         SimpleTimeZone* tz = new SimpleTimeZone(OFFSET[i], UnicodeString("Zone Offset:") + OFFSET[i] + "ms");
    972         sdf->adoptTimeZone(tz);
    973         for (int32_t j = 0; PATTERN[j] != 0; j++) {
    974             sdf->applyPattern(UnicodeString(PATTERN[j]));
    975             UnicodeString result;
    976             sdf->format(d, result);
    977 
    978             if (ISO_STR[i][j]) {
    979                 if (result != UnicodeString(ISO_STR[i][j])) {
    980                     errln((UnicodeString)"FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> "
    981                         + result + " (expected: " + ISO_STR[i][j] + ")");
    982                 }
    983             } else {
    984                 // Offset out of range
    985                 // Note: for now, there is no way to propagate the error status through
    986                 // the SimpleDateFormat::format above.
    987                 if (result.length() > 0) {
    988                     errln((UnicodeString)"FAIL: Non-Empty result for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i]
    989                         + " (expected: empty result)");
    990                 }
    991             }
    992         }
    993     }
    994 
    995     // Parsing
    996     LocalPointer<Calendar> outcal(Calendar::createInstance(status));
    997     if (U_FAILURE(status)) {
    998         dataerrln("Fail new Calendar: %s", u_errorName(status));
    999         return;
   1000     }
   1001     for (int32_t i = 0; ISO_STR[i][0] != NULL; i++) {
   1002         for (int32_t j = 0; PATTERN[j] != 0; j++) {
   1003             if (ISO_STR[i][j] == 0) {
   1004                 continue;
   1005             }
   1006             ParsePosition pos(0);
   1007             SimpleTimeZone* bogusTZ = new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
   1008             outcal->adoptTimeZone(bogusTZ);
   1009             sdf->applyPattern(PATTERN[j]);
   1010 
   1011             sdf->parse(UnicodeString(ISO_STR[i][j]), *(outcal.getAlias()), pos);
   1012 
   1013             if (pos.getIndex() != (int32_t)uprv_strlen(ISO_STR[i][j])) {
   1014                 errln((UnicodeString)"FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]);
   1015             }
   1016 
   1017             const TimeZone& outtz = outcal->getTimeZone();
   1018             int32_t outOffset = outtz.getRawOffset();
   1019             int32_t adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j];
   1020             if (outOffset != adjustedOffset) {
   1021                 errln((UnicodeString)"FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j]
   1022                     + " (expected:" + adjustedOffset + "ms)");
   1023             }
   1024         }
   1025     }
   1026 }
   1027 
   1028 
   1029 typedef struct {
   1030     const char*     locale;
   1031     const char*     tzid;
   1032     UDate           date;
   1033     UTimeZoneFormatStyle    style;
   1034     const char*     expected;
   1035     UTimeZoneFormatTimeType timeType;
   1036 } FormatTestData;
   1037 
   1038 void
   1039 TimeZoneFormatTest::TestFormat(void) {
   1040     UDate dateJan = 1358208000000.0;    // 2013-01-15T00:00:00Z
   1041     UDate dateJul = 1373846400000.0;    // 2013-07-15T00:00:00Z
   1042 
   1043     const FormatTestData DATA[] = {
   1044         {
   1045             "en",
   1046             "America/Los_Angeles",
   1047             dateJan,
   1048             UTZFMT_STYLE_GENERIC_LOCATION,
   1049             "Los Angeles Time",
   1050             UTZFMT_TIME_TYPE_UNKNOWN
   1051         },
   1052         {
   1053             "en",
   1054             "America/Los_Angeles",
   1055             dateJan,
   1056             UTZFMT_STYLE_GENERIC_LONG,
   1057             "Pacific Time",
   1058             UTZFMT_TIME_TYPE_UNKNOWN
   1059         },
   1060         {
   1061             "en",
   1062             "America/Los_Angeles",
   1063             dateJan,
   1064             UTZFMT_STYLE_SPECIFIC_LONG,
   1065             "Pacific Standard Time",
   1066             UTZFMT_TIME_TYPE_STANDARD
   1067         },
   1068         {
   1069             "en",
   1070             "America/Los_Angeles",
   1071             dateJul,
   1072             UTZFMT_STYLE_SPECIFIC_LONG,
   1073             "Pacific Daylight Time",
   1074             UTZFMT_TIME_TYPE_DAYLIGHT
   1075         },
   1076         {
   1077             "ja",
   1078             "America/Los_Angeles",
   1079             dateJan,
   1080             UTZFMT_STYLE_ZONE_ID,
   1081             "America/Los_Angeles",
   1082             UTZFMT_TIME_TYPE_UNKNOWN
   1083         },
   1084         {
   1085             "fr",
   1086             "America/Los_Angeles",
   1087             dateJul,
   1088             UTZFMT_STYLE_ZONE_ID_SHORT,
   1089             "uslax",
   1090             UTZFMT_TIME_TYPE_UNKNOWN
   1091         },
   1092         {
   1093             "en",
   1094             "America/Los_Angeles",
   1095             dateJan,
   1096             UTZFMT_STYLE_EXEMPLAR_LOCATION,
   1097             "Los Angeles",
   1098             UTZFMT_TIME_TYPE_UNKNOWN
   1099         },
   1100 
   1101         {
   1102             "ja",
   1103             "Asia/Tokyo",
   1104             dateJan,
   1105             UTZFMT_STYLE_GENERIC_LONG,
   1106             "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
   1107             UTZFMT_TIME_TYPE_UNKNOWN
   1108         },
   1109 
   1110         {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
   1111     };
   1112 
   1113     for (int32_t i = 0; DATA[i].locale; i++) {
   1114         UErrorCode status = U_ZERO_ERROR;
   1115         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
   1116         if (U_FAILURE(status)) {
   1117             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
   1118             continue;
   1119         }
   1120 
   1121         LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
   1122         UnicodeString out;
   1123         UTimeZoneFormatTimeType timeType;
   1124 
   1125         tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
   1126         UnicodeString expected(DATA[i].expected, -1, US_INV);
   1127         expected = expected.unescape();
   1128 
   1129         assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
   1130         if (DATA[i].timeType != timeType) {
   1131             dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
   1132                 + timeType + ", expected=" + DATA[i].timeType);
   1133         }
   1134     }
   1135 }
   1136 
   1137 void
   1138 TimeZoneFormatTest::TestFormatTZDBNames(void) {
   1139     UDate dateJan = 1358208000000.0;    // 2013-01-15T00:00:00Z
   1140     UDate dateJul = 1373846400000.0;    // 2013-07-15T00:00:00Z
   1141 
   1142     const FormatTestData DATA[] = {
   1143         {
   1144             "en",
   1145             "America/Chicago",
   1146             dateJan,
   1147             UTZFMT_STYLE_SPECIFIC_SHORT,
   1148             "CST",
   1149             UTZFMT_TIME_TYPE_STANDARD
   1150         },
   1151         {
   1152             "en",
   1153             "Asia/Shanghai",
   1154             dateJan,
   1155             UTZFMT_STYLE_SPECIFIC_SHORT,
   1156             "CST",
   1157             UTZFMT_TIME_TYPE_STANDARD
   1158         },
   1159         {
   1160             "zh_Hans",
   1161             "Asia/Shanghai",
   1162             dateJan,
   1163             UTZFMT_STYLE_SPECIFIC_SHORT,
   1164             "CST",
   1165             UTZFMT_TIME_TYPE_STANDARD
   1166         },
   1167         {
   1168             "en",
   1169             "America/Los_Angeles",
   1170             dateJul,
   1171             UTZFMT_STYLE_SPECIFIC_LONG,
   1172             "GMT-07:00",    // No long display names
   1173             UTZFMT_TIME_TYPE_DAYLIGHT
   1174         },
   1175         {
   1176             "ja",
   1177             "America/Los_Angeles",
   1178             dateJul,
   1179             UTZFMT_STYLE_SPECIFIC_SHORT,
   1180             "PDT",
   1181             UTZFMT_TIME_TYPE_DAYLIGHT
   1182         },
   1183         {
   1184             "en",
   1185             "Australia/Sydney",
   1186             dateJan,
   1187             UTZFMT_STYLE_SPECIFIC_SHORT,
   1188             "AEDT",
   1189             UTZFMT_TIME_TYPE_DAYLIGHT
   1190         },
   1191         {
   1192             "en",
   1193             "Australia/Sydney",
   1194             dateJul,
   1195             UTZFMT_STYLE_SPECIFIC_SHORT,
   1196             "AEST",
   1197             UTZFMT_TIME_TYPE_STANDARD
   1198         },
   1199 
   1200         {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
   1201     };
   1202 
   1203     for (int32_t i = 0; DATA[i].locale; i++) {
   1204         UErrorCode status = U_ZERO_ERROR;
   1205         Locale loc(DATA[i].locale);
   1206         LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(loc, status));
   1207         if (U_FAILURE(status)) {
   1208             dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
   1209             continue;
   1210         }
   1211         TimeZoneNames *tzdbNames = TimeZoneNames::createTZDBInstance(loc, status);
   1212         if (U_FAILURE(status)) {
   1213             dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status));
   1214             continue;
   1215         }
   1216         tzfmt->adoptTimeZoneNames(tzdbNames);
   1217 
   1218         LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
   1219         UnicodeString out;
   1220         UTimeZoneFormatTimeType timeType;
   1221 
   1222         tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
   1223         UnicodeString expected(DATA[i].expected, -1, US_INV);
   1224         expected = expected.unescape();
   1225 
   1226         assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
   1227         if (DATA[i].timeType != timeType) {
   1228             dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
   1229                 + timeType + ", expected=" + DATA[i].timeType);
   1230         }
   1231     }
   1232 }
   1233 
   1234 
   1235 #endif /* #if !UCONFIG_NO_FORMATTING */
   1236