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