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