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