]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/tzfmttst.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / test / intltest / tzfmttst.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
efa1e659 3* Copyright (C) 2007-2014, International Business Machines Corporation and *
46f4442e
A
4* others. All Rights Reserved. *
5*******************************************************************************
6*/
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "tzfmttst.h"
12
729e4ab9 13#include "simplethread.h"
46f4442e
A
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"
51004dcb
A
21#include "unicode/tzfmt.h"
22#include "unicode/localpointer.h"
46f4442e 23#include "cstring.h"
51004dcb
A
24#include "zonemeta.h"
25
26static 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};
46f4442e
A
50static const int NUM_PATTERNS = sizeof(PATTERNS)/sizeof(const char*);
51
51004dcb
A
52static const UChar ETC_UNKNOWN[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
53
54static const UChar ETC_SLASH[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
55static const UChar SYSTEMV_SLASH[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
56static const UChar RIYADH8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
57
58static 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
46f4442e
A
67void
68TimeZoneFormatTest::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);
51004dcb
A
76 TESTCASE(2, TestParse);
77 TESTCASE(3, TestISOFormat);
57a6839d 78 TESTCASE(4, TestFormat);
b331163b 79 TESTCASE(5, TestFormatTZDBNames);
46f4442e
A
80 default: name = ""; break;
81 }
82}
83
84void
85TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
86 UErrorCode status = U_ZERO_ERROR;
87
51004dcb 88 SimpleTimeZone unknownZone(-31415, ETC_UNKNOWN);
46f4442e
A
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)) {
729e4ab9 103 dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
46f4442e
A
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
729e4ab9 132 const Locale testLocales[] = {
46f4442e
A
133 Locale("en"),
134 Locale("en_CA"),
135 Locale("fr"),
136 Locale("zh_Hant")
137 };
138
139 const Locale *LOCALES;
140 int32_t nLocales;
729e4ab9
A
141
142 if (quick) {
143 LOCALES = testLocales;
144 nLocales = sizeof(testLocales)/sizeof(Locale);
46f4442e 145 } else {
729e4ab9 146 LOCALES = Locale::getAvailableLocales(nLocales);
46f4442e
A
147 }
148
149 StringEnumeration *tzids = TimeZone::createEnumeration();
46f4442e
A
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++) {
4388f060
A
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
46f4442e
A
164 for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) {
165
46f4442e
A
166 SimpleDateFormat *sdf = new SimpleDateFormat((UnicodeString)PATTERNS[patidx], LOCALES[locidx], status);
167 if (U_FAILURE(status)) {
4388f060 168 dataerrln((UnicodeString)"new SimpleDateFormat failed for pattern " +
729e4ab9 169 PATTERNS[patidx] + " for locale " + LOCALES[locidx].getName() + " - " + u_errorName(status));
46f4442e
A
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
51004dcb
A
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) {
729e4ab9
A
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.
46f4442e
A
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)) {
729e4ab9
A
264 if (canonical.indexOf((UChar)0x27 /*'/'*/) == -1) {
265 // Exceptional cases, such as CET, EET, MET and WET
51004dcb 266 logln((UnicodeString)"Canonical round trip failed (as expected); tz=" + *tzid
729e4ab9
A
267 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
268 + ", time=" + DATES[datidx] + ", str=" + tzstr
269 + ", outtz=" + outtzid);
270 } else {
51004dcb 271 errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
729e4ab9
A
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 {
51004dcb
A
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
4388f060
A
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 }
729e4ab9 300 }
51004dcb 301 isOffsetFormat = (numDigits > 0);
729e4ab9 302 }
4388f060 303 if (isOffsetFormat || tzstr == localGMTString) {
51004dcb 304 // Localized GMT or ISO: total offset (raw + dst) must be preserved.
729e4ab9
A
305 int32_t inOffset = inRaw + inDst;
306 int32_t outOffset = outRaw + outDst;
51004dcb
A
307 int32_t diff = outOffset - inOffset;
308 if (minutesOffset) {
309 diff = (diff / 60000) * 60000;
310 }
311 if (diff != 0) {
729e4ab9 312 errln((UnicodeString)"Offset round trip failed; tz=" + *tzid
46f4442e
A
313 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
314 + ", time=" + DATES[datidx] + ", str=" + tzstr
729e4ab9 315 + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
46f4442e 316 }
729e4ab9
A
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);
46f4442e
A
324 }
325 }
326 }
327 delete outcal;
328 }
329 delete tz;
330 }
331 delete sdf;
332 }
333 }
334 delete cal;
335 delete tzids;
336}
337
efa1e659
A
338// Special exclusions in TestTimeZoneRoundTrip.
339// These special cases do not round trip time as designed.
340static 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
729e4ab9
A
372struct 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
384class TestTimeRoundTripThread: public SimpleThread {
385public:
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
51004dcb
A
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 };
729e4ab9
A
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
4388f060 411 StringEnumeration *tzids = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, status);
729e4ab9 412 if (U_FAILURE(status)) {
4388f060
A
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 }
729e4ab9
A
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
51004dcb
A
469 UBool minutesOffset = contains(MINUTES_OFFSET, PATTERNS[patidx]);
470
729e4ab9
A
471 tzids->reset(status);
472 const UnicodeString *tzid;
473
474 timer = Calendar::getNow();
475
476 while ((tzid = tzids->snext(status))) {
51004dcb
A
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
57a6839d
A
495 if (*tzid == "Pacific/Apia" && uprv_strcmp(PATTERNS[patidx], "vvvv") == 0
496 && log.logKnownIssue("11052", "Ambiguous zone name - Samoa Time")) {
efa1e659
A
497 continue;
498 }
499
729e4ab9
A
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;
51004dcb
A
522 expectedRoundTrip[1] = isDstDecession ?
523 !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
524 !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
729e4ab9 525 testTimes[2] = t - 1;
51004dcb
A
526 expectedRoundTrip[2] = isDstDecession ?
527 !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
528 !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
729e4ab9
A
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 }
51004dcb
A
561
562 int32_t timeDiff = (int32_t)(parsedDate - testTimes[testidx]);
563 UBool bTimeMatch = minutesOffset ?
564 (timeDiff/60000)*60000 == 0 : timeDiff == 0;
565 if (!bTimeMatch) {
729e4ab9
A
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]);
4388f060 568 // Timebomb for TZData update
efa1e659
A
569 if (expectedRoundTrip[testidx]
570 && !isSpecialTimeRoundTripCase(data.locales[locidx].getName(), *tzid,
571 PATTERNS[patidx], testTimes[testidx])) {
729e4ab9
A
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 }
602private:
603 IntlTest& log;
604 LocaleData& data;
605 int32_t index;
606};
607
46f4442e
A
608void
609TimeZoneFormatTest::TestTimeRoundTrip(void) {
729e4ab9
A
610 int32_t nThreads = threadCount;
611 const Locale *LOCALES;
612 int32_t nLocales;
613 int32_t testCounts = 0;
46f4442e 614
729e4ab9
A
615 UErrorCode status = U_ZERO_ERROR;
616 Calendar *cal = Calendar::createInstance(TimeZone::createTimeZone((UnicodeString) "UTC"), status);
46f4442e 617 if (U_FAILURE(status)) {
729e4ab9 618 dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
46f4442e
A
619 return;
620 }
621
729e4ab9
A
622 const char* testAllProp = getProperty("TimeZoneRoundTripAll");
623 UBool bTestAll = (testAllProp && uprv_strcmp(testAllProp, "true") == 0);
46f4442e 624
729e4ab9
A
625 UDate START_TIME, END_TIME;
626 if (bTestAll || !quick) {
46f4442e
A
627 cal->set(1900, UCAL_JANUARY, 1);
628 } else {
729e4ab9 629 cal->set(1990, UCAL_JANUARY, 1);
46f4442e
A
630 }
631 START_TIME = cal->getTime(status);
632
633 cal->set(2015, UCAL_JANUARY, 1);
634 END_TIME = cal->getTime(status);
729e4ab9 635
46f4442e
A
636 if (U_FAILURE(status)) {
637 errln("getTime failed");
638 return;
639 }
640
46f4442e
A
641 UDate times[NUM_PATTERNS];
642 for (int32_t i = 0; i < NUM_PATTERNS; i++) {
643 times[i] = 0;
644 }
645
46f4442e 646 // Set up test locales
729e4ab9 647 const Locale locales1[] = {Locale("en")};
46f4442e 648 const Locale locales2[] = {
729e4ab9
A
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")
46f4442e
A
658 };
659
729e4ab9 660 if (bTestAll) {
46f4442e
A
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
729e4ab9
A
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 }
46f4442e
A
692 }
693
729e4ab9
A
694 UBool done = false;
695 while (true) {
696 umtx_lock(NULL);
697 if (data.numDone == nLocales) {
698 done = true;
46f4442e 699 }
729e4ab9
A
700 umtx_unlock(NULL);
701 if (done)
702 break;
703 SimpleThread::sleep(1000);
46f4442e 704 }
729e4ab9
A
705
706 for (i = 0; i < nThreads; i++) {
707 delete threads[i];
708 }
709 delete [] threads;
710
711#endif
46f4442e
A
712 UDate total = 0;
713 logln("### Elapsed time by patterns ###");
714 for (int32_t i = 0; i < NUM_PATTERNS; i++) {
729e4ab9
A
715 logln(UnicodeString("") + data.times[i] + "ms (" + PATTERNS[i] + ")");
716 total += data.times[i];
46f4442e 717 }
729e4ab9
A
718 logln((UnicodeString) "Total: " + total + "ms");
719 logln((UnicodeString) "Iteration: " + data.testCounts);
46f4442e
A
720
721 delete cal;
46f4442e
A
722}
723
51004dcb
A
724
725typedef struct {
726 const char* text;
727 int32_t inPos;
728 const char* locale;
729 UTimeZoneFormatStyle style;
b331163b 730 uint32_t parseOptions;
51004dcb
A
731 const char* expected;
732 int32_t outPos;
733 UTimeZoneFormatTimeType timeType;
734} ParseTestData;
735
736void
737TimeZoneFormatTest::TestParse(void) {
738 const ParseTestData DATA[] = {
b331163b
A
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}
51004dcb
A
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);
b331163b 838 TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype);
51004dcb
A
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
863void
864TimeZoneFormatTest::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;
b331163b 963 LocalPointer<SimpleDateFormat> sdf(new SimpleDateFormat(status), status);
51004dcb
A
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
57a6839d
A
1029typedef 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
1038void
1039TimeZoneFormatTest::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
b331163b
A
1137void
1138TimeZoneFormatTest::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
46f4442e 1235#endif /* #if !UCONFIG_NO_FORMATTING */