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