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