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