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