]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/tzfmttst.cpp
ICU-57132.0.1.tar.gz
[apple/icu.git] / icuSources / test / intltest / tzfmttst.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
2ca993e8 3* Copyright (C) 2007-2015, 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
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"
51004dcb
A
20#include "unicode/tzfmt.h"
21#include "unicode/localpointer.h"
2ca993e8 22
46f4442e 23#include "cstring.h"
2ca993e8
A
24#include "cstr.h"
25#include "mutex.h"
26#include "simplethread.h"
27#include "uassert.h"
51004dcb
A
28#include "zonemeta.h"
29
30static 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};
46f4442e 54
51004dcb
A
55static const UChar ETC_UNKNOWN[] = {0x45, 0x74, 0x63, 0x2F, 0x55, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0};
56
57static const UChar ETC_SLASH[] = { 0x45, 0x74, 0x63, 0x2F, 0 }; // "Etc/"
58static const UChar SYSTEMV_SLASH[] = { 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x56, 0x2F, 0 }; // "SystemV/
59static const UChar RIYADH8[] = { 0x52, 0x69, 0x79, 0x61, 0x64, 0x68, 0x38, 0 }; // "Riyadh8"
60
61static 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
46f4442e
A
70void
71TimeZoneFormatTest::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);
51004dcb
A
79 TESTCASE(2, TestParse);
80 TESTCASE(3, TestISOFormat);
57a6839d 81 TESTCASE(4, TestFormat);
b331163b 82 TESTCASE(5, TestFormatTZDBNames);
46f4442e
A
83 default: name = ""; break;
84 }
85}
86
87void
88TimeZoneFormatTest::TestTimeZoneRoundTrip(void) {
89 UErrorCode status = U_ZERO_ERROR;
90
51004dcb 91 SimpleTimeZone unknownZone(-31415, ETC_UNKNOWN);
46f4442e
A
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)) {
729e4ab9 106 dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
46f4442e
A
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
2ca993e8
A
122 UDate DATES[UPRV_LENGTHOF(testDateData)];
123 const int32_t nDates = UPRV_LENGTHOF(testDateData);
46f4442e
A
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
729e4ab9 135 const Locale testLocales[] = {
46f4442e
A
136 Locale("en"),
137 Locale("en_CA"),
138 Locale("fr"),
139 Locale("zh_Hant")
140 };
141
142 const Locale *LOCALES;
143 int32_t nLocales;
729e4ab9
A
144
145 if (quick) {
146 LOCALES = testLocales;
2ca993e8 147 nLocales = UPRV_LENGTHOF(testLocales);
46f4442e 148 } else {
729e4ab9 149 LOCALES = Locale::getAvailableLocales(nLocales);
46f4442e
A
150 }
151
152 StringEnumeration *tzids = TimeZone::createEnumeration();
46f4442e
A
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++) {
4388f060
A
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
2ca993e8 167 for (int32_t patidx = 0; patidx < UPRV_LENGTHOF(PATTERNS); patidx++) {
46f4442e 168
46f4442e
A
169 SimpleDateFormat *sdf = new SimpleDateFormat((UnicodeString)PATTERNS[patidx], LOCALES[locidx], status);
170 if (U_FAILURE(status)) {
4388f060 171 dataerrln((UnicodeString)"new SimpleDateFormat failed for pattern " +
729e4ab9 172 PATTERNS[patidx] + " for locale " + LOCALES[locidx].getName() + " - " + u_errorName(status));
46f4442e
A
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
51004dcb
A
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) {
729e4ab9
A
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.
46f4442e
A
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)) {
729e4ab9
A
267 if (canonical.indexOf((UChar)0x27 /*'/'*/) == -1) {
268 // Exceptional cases, such as CET, EET, MET and WET
51004dcb 269 logln((UnicodeString)"Canonical round trip failed (as expected); tz=" + *tzid
729e4ab9
A
270 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
271 + ", time=" + DATES[datidx] + ", str=" + tzstr
272 + ", outtz=" + outtzid);
273 } else {
51004dcb 274 errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid
729e4ab9
A
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 {
51004dcb
A
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
4388f060
A
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 }
729e4ab9 303 }
51004dcb 304 isOffsetFormat = (numDigits > 0);
729e4ab9 305 }
4388f060 306 if (isOffsetFormat || tzstr == localGMTString) {
51004dcb 307 // Localized GMT or ISO: total offset (raw + dst) must be preserved.
729e4ab9
A
308 int32_t inOffset = inRaw + inDst;
309 int32_t outOffset = outRaw + outDst;
51004dcb
A
310 int32_t diff = outOffset - inOffset;
311 if (minutesOffset) {
312 diff = (diff / 60000) * 60000;
313 }
314 if (diff != 0) {
729e4ab9 315 errln((UnicodeString)"Offset round trip failed; tz=" + *tzid
46f4442e
A
316 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx]
317 + ", time=" + DATES[datidx] + ", str=" + tzstr
729e4ab9 318 + ", inOffset=" + inOffset + ", outOffset=" + outOffset);
46f4442e 319 }
729e4ab9
A
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);
46f4442e
A
327 }
328 }
329 }
330 delete outcal;
331 }
332 delete tz;
333 }
334 delete sdf;
335 }
336 }
337 delete cal;
338 delete tzids;
339}
340
efa1e659
A
341// Special exclusions in TestTimeZoneRoundTrip.
342// These special cases do not round trip time as designed.
343static 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
2ca993e8
A
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.
729e4ab9 387struct LocaleData {
2ca993e8
A
388 int32_t localeIndex;
389 int32_t patternIndex;
729e4ab9 390 int32_t testCounts;
2ca993e8
A
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;
729e4ab9 396 int32_t numDone;
51004dcb 397
2ca993e8
A
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++) {
729e4ab9
A
401 times[i] = 0;
402 }
2ca993e8 403 };
729e4ab9 404
2ca993e8
A
405 void resetTestIteration() {
406 localeIndex = -1;
407 patternIndex = UPRV_LENGTHOF(PATTERNS);
408 numDone = 0;
409 }
51004dcb 410
2ca993e8
A
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;
729e4ab9 416 }
2ca993e8
A
417 patternIndex = -1;
418 ++localeIndex;
729e4ab9 419 }
2ca993e8
A
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;
729e4ab9 431 }
729e4ab9
A
432};
433
2ca993e8
A
434static LocaleData *gLocaleData = NULL;
435
46f4442e
A
436void
437TimeZoneFormatTest::TestTimeRoundTrip(void) {
729e4ab9 438 UErrorCode status = U_ZERO_ERROR;
2ca993e8 439 LocalPointer <Calendar> cal(Calendar::createInstance(TimeZone::createTimeZone((UnicodeString) "UTC"), status));
46f4442e 440 if (U_FAILURE(status)) {
729e4ab9 441 dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
46f4442e
A
442 return;
443 }
444
729e4ab9
A
445 const char* testAllProp = getProperty("TimeZoneRoundTripAll");
446 UBool bTestAll = (testAllProp && uprv_strcmp(testAllProp, "true") == 0);
46f4442e 447
729e4ab9
A
448 UDate START_TIME, END_TIME;
449 if (bTestAll || !quick) {
46f4442e
A
450 cal->set(1900, UCAL_JANUARY, 1);
451 } else {
2ca993e8 452 cal->set(1999, UCAL_JANUARY, 1);
46f4442e
A
453 }
454 START_TIME = cal->getTime(status);
455
2ca993e8 456 cal->set(2022, UCAL_JANUARY, 1);
46f4442e 457 END_TIME = cal->getTime(status);
729e4ab9 458
46f4442e
A
459 if (U_FAILURE(status)) {
460 errln("getTime failed");
461 return;
462 }
463
2ca993e8
A
464 LocaleData localeData;
465 gLocaleData = &localeData;
46f4442e 466
46f4442e 467 // Set up test locales
729e4ab9 468 const Locale locales1[] = {Locale("en")};
46f4442e 469 const Locale locales2[] = {
729e4ab9
A
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")
46f4442e
A
479 };
480
729e4ab9 481 if (bTestAll) {
2ca993e8 482 gLocaleData->locales = Locale::getAvailableLocales(gLocaleData->nLocales);
46f4442e 483 } else if (quick) {
2ca993e8
A
484 gLocaleData->locales = locales1;
485 gLocaleData->nLocales = UPRV_LENGTHOF(locales1);
46f4442e 486 } else {
2ca993e8
A
487 gLocaleData->locales = locales2;
488 gLocaleData->nLocales = UPRV_LENGTHOF(locales2);
46f4442e
A
489 }
490
2ca993e8
A
491 gLocaleData->START_TIME = START_TIME;
492 gLocaleData->END_TIME = END_TIME;
493 gLocaleData->resetTestIteration();
46f4442e 494
2ca993e8 495 // start IntlTest.threadCount threads, each running the function RunTimeRoundTripTests().
729e4ab9 496
2ca993e8
A
497 ThreadPool<TimeZoneFormatTest> threads(this, threadCount, &TimeZoneFormatTest::RunTimeRoundTripTests);
498 threads.start(); // Start all threads.
499 threads.join(); // Wait for all threads to finish.
729e4ab9 500
46f4442e
A
501 UDate total = 0;
502 logln("### Elapsed time by patterns ###");
2ca993e8
A
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];
46f4442e 506 }
729e4ab9 507 logln((UnicodeString) "Total: " + total + "ms");
2ca993e8
A
508 logln((UnicodeString) "Iteration: " + gLocaleData->testCounts);
509}
46f4442e 510
2ca993e8
A
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//
517void 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
600 BasicTimeZone *tz = (BasicTimeZone*) TimeZone::createTimeZone(*tzid);
601 sdf->setTimeZone(*tz);
602
603 UDate t = gLocaleData->START_TIME;
604 TimeZoneTransition tzt;
605 UBool tztAvail = FALSE;
606 UBool middle = TRUE;
607
608 while (t < gLocaleData->END_TIME) {
609 if (!tztAvail) {
610 testTimes[0] = t;
611 expectedRoundTrip[0] = TRUE;
612 testLen = 1;
613 } else {
614 int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
615 int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
616 int32_t delta = toOffset - fromOffset;
617 if (delta < 0) {
618 UBool isDstDecession = tzt.getFrom()->getDSTSavings() > 0 && tzt.getTo()->getDSTSavings() == 0;
619 testTimes[0] = t + delta - 1;
620 expectedRoundTrip[0] = TRUE;
621 testTimes[1] = t + delta;
622 expectedRoundTrip[1] = isDstDecession ?
623 !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
624 !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
625 testTimes[2] = t - 1;
626 expectedRoundTrip[2] = isDstDecession ?
627 !contains(AMBIGUOUS_DST_DECESSION, PATTERNS[patidx]) :
628 !contains(AMBIGUOUS_NEGATIVE_SHIFT, PATTERNS[patidx]);
629 testTimes[3] = t;
630 expectedRoundTrip[3] = TRUE;
631 testLen = 4;
632 } else {
633 testTimes[0] = t - 1;
634 expectedRoundTrip[0] = TRUE;
635 testTimes[1] = t;
636 expectedRoundTrip[1] = TRUE;
637 testLen = 2;
638 }
639 }
640 for (int32_t testidx = 0; testidx < testLen; testidx++) {
641 if (quick) {
642 // reduce regular test time
643 if (!expectedRoundTrip[testidx]) {
644 continue;
645 }
646 }
647
648 {
649 Mutex lock;
650 gLocaleData->testCounts++;
651 }
652
653 UnicodeString text;
654 FieldPosition fpos(0);
655 sdf->format(testTimes[testidx], text, fpos);
656
657 UDate parsedDate = sdf->parse(text, status);
658 if (U_FAILURE(status)) {
659 errln((UnicodeString) "Parse failure for text=" + text + ", tzid=" + *tzid + ", locale=" + gLocaleData->locales[locidx].getName()
660 + ", pattern=" + PATTERNS[patidx] + ", time=" + testTimes[testidx]);
661 status = U_ZERO_ERROR;
662 continue;
663 }
664
665 int32_t timeDiff = (int32_t)(parsedDate - testTimes[testidx]);
666 UBool bTimeMatch = minutesOffset ?
667 (timeDiff/60000)*60000 == 0 : timeDiff == 0;
668 if (!bTimeMatch) {
669 UnicodeString msg = (UnicodeString) "Time round trip failed for " + "tzid=" + *tzid
670 + ", locale=" + gLocaleData->locales[locidx].getName() + ", pattern=" + PATTERNS[patidx]
671 + ", text=" + text + ", time=" + testTimes[testidx] + ", restime=" + parsedDate + ", diff=" + (parsedDate - testTimes[testidx]);
672 // Timebomb for TZData update
673 if (expectedRoundTrip[testidx]
674 && !isSpecialTimeRoundTripCase(gLocaleData->locales[locidx].getName(), *tzid,
675 PATTERNS[patidx], testTimes[testidx])) {
676 errln((UnicodeString) "FAIL: " + msg);
677 } else if (REALLY_VERBOSE) {
678 logln(msg);
679 }
680 }
681 }
682 tztAvail = tz->getNextTransition(t, FALSE, tzt);
683 if (!tztAvail) {
684 break;
685 }
686 if (middle) {
687 // Test the date in the middle of two transitions.
688 t += (int64_t) ((tzt.getTime() - t) / 2);
689 middle = FALSE;
690 tztAvail = FALSE;
691 } else {
692 t = tzt.getTime();
693 }
694 }
695 delete tz;
696 }
697 UDate elapsedTime = Calendar::getNow() - timer;
698 gLocaleData->addTime(elapsedTime, patidx);
699 delete sdf;
700 }
701 delete tzids;
46f4442e
A
702}
703
51004dcb
A
704
705typedef struct {
706 const char* text;
707 int32_t inPos;
708 const char* locale;
709 UTimeZoneFormatStyle style;
b331163b 710 uint32_t parseOptions;
51004dcb
A
711 const char* expected;
712 int32_t outPos;
713 UTimeZoneFormatTimeType timeType;
714} ParseTestData;
715
716void
717TimeZoneFormatTest::TestParse(void) {
718 const ParseTestData DATA[] = {
b331163b
A
719 // text inPos locale style
720 // parseOptions expected outPos timeType
721 {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL,
722 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN},
723
724 {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG,
725 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN},
726
727 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL,
728 UTZFMT_PARSE_OPTION_ALL_STYLES, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN},
729
730 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION,
731 UTZFMT_PARSE_OPTION_NONE, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN},
732
733 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
734 UTZFMT_PARSE_OPTION_ALL_STYLES, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN},
735
736 {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL,
737 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN},
738
739 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL,
740 UTZFMT_PARSE_OPTION_NONE, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN},
741
742 {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
743 UTZFMT_PARSE_OPTION_NONE, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN},
744
745 {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
746 UTZFMT_PARSE_OPTION_NONE, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN},
747
748 {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL,
749 UTZFMT_PARSE_OPTION_NONE, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN},
750
751 {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT,
752 UTZFMT_PARSE_OPTION_NONE, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN},
753
754 {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT,
755 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN},
756
757 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT,
758 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD},
759
760 {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT,
761 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD},
762
763 {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT,
764 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT},
765
766 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG,
767 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN},
768
769 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG,
770 UTZFMT_PARSE_OPTION_ALL_STYLES, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD},
771
772 {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT,
773 UTZFMT_PARSE_OPTION_NONE, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD},
774
775 {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT,
776 UTZFMT_PARSE_OPTION_NONE, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD},
777
778 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT,
779 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN},
780
781 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT,
782 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD},
783
784 {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT,
785 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD},
786
787 {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT,
788 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD},
789
790 {"AEST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT,
791 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Australia/Sydney", 4, UTZFMT_TIME_TYPE_STANDARD},
792
793 {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT,
794 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD},
795
796 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG,
797 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN},
798
799 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG,
800 UTZFMT_PARSE_OPTION_ALL_STYLES, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN},
801
802 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG,
803 UTZFMT_PARSE_OPTION_ALL_STYLES | UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT},
804
805 {NULL, 0, NULL, UTZFMT_STYLE_GENERIC_LOCATION,
806 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}
51004dcb
A
807 };
808
809 for (int32_t i = 0; DATA[i].text; i++) {
810 UErrorCode status = U_ZERO_ERROR;
811 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
812 if (U_FAILURE(status)) {
813 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
814 continue;
815 }
816 UTimeZoneFormatTimeType ttype = UTZFMT_TIME_TYPE_UNKNOWN;
817 ParsePosition pos(DATA[i].inPos);
b331163b 818 TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype);
51004dcb
A
819
820 UnicodeString errMsg;
821 if (tz) {
822 UnicodeString outID;
823 tz->getID(outID);
824 if (outID != UnicodeString(DATA[i].expected)) {
825 errMsg = (UnicodeString)"Time zone ID: " + outID + " - expected: " + DATA[i].expected;
826 } else if (pos.getIndex() != DATA[i].outPos) {
827 errMsg = (UnicodeString)"Parsed pos: " + pos.getIndex() + " - expected: " + DATA[i].outPos;
828 } else if (ttype != DATA[i].timeType) {
829 errMsg = (UnicodeString)"Time type: " + ttype + " - expected: " + DATA[i].timeType;
830 }
831 delete tz;
832 } else {
833 if (DATA[i].expected) {
834 errln((UnicodeString)"Fail: Parse failure - expected: " + DATA[i].expected);
835 }
836 }
837 if (errMsg.length() > 0) {
838 errln((UnicodeString)"Fail: " + errMsg + " [text=" + DATA[i].text + ", pos=" + DATA[i].inPos + ", style=" + DATA[i].style + "]");
839 }
840 }
841}
842
843void
844TimeZoneFormatTest::TestISOFormat(void) {
845 const int32_t OFFSET[] = {
846 0, // 0
847 999, // 0.999s
848 -59999, // -59.999s
849 60000, // 1m
850 -77777, // -1m 17.777s
851 1800000, // 30m
852 -3600000, // -1h
853 36000000, // 10h
854 -37800000, // -10h 30m
855 -37845000, // -10h 30m 45s
856 108000000, // 30h
857 };
858
859 const char* ISO_STR[][11] = {
860 // 0
861 {
862 "Z", "Z", "Z", "Z", "Z",
863 "+00", "+0000", "+00:00", "+0000", "+00:00",
864 "+0000"
865 },
866 // 999
867 {
868 "Z", "Z", "Z", "Z", "Z",
869 "+00", "+0000", "+00:00", "+0000", "+00:00",
870 "+0000"
871 },
872 // -59999
873 {
874 "Z", "Z", "Z", "-000059", "-00:00:59",
875 "+00", "+0000", "+00:00", "-000059", "-00:00:59",
876 "-000059"
877 },
878 // 60000
879 {
880 "+0001", "+0001", "+00:01", "+0001", "+00:01",
881 "+0001", "+0001", "+00:01", "+0001", "+00:01",
882 "+0001"
883 },
884 // -77777
885 {
886 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
887 "-0001", "-0001", "-00:01", "-000117", "-00:01:17",
888 "-000117"
889 },
890 // 1800000
891 {
892 "+0030", "+0030", "+00:30", "+0030", "+00:30",
893 "+0030", "+0030", "+00:30", "+0030", "+00:30",
894 "+0030"
895 },
896 // -3600000
897 {
898 "-01", "-0100", "-01:00", "-0100", "-01:00",
899 "-01", "-0100", "-01:00", "-0100", "-01:00",
900 "-0100"
901 },
902 // 36000000
903 {
904 "+10", "+1000", "+10:00", "+1000", "+10:00",
905 "+10", "+1000", "+10:00", "+1000", "+10:00",
906 "+1000"
907 },
908 // -37800000
909 {
910 "-1030", "-1030", "-10:30", "-1030", "-10:30",
911 "-1030", "-1030", "-10:30", "-1030", "-10:30",
912 "-1030"
913 },
914 // -37845000
915 {
916 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
917 "-1030", "-1030", "-10:30", "-103045", "-10:30:45",
918 "-103045"
919 },
920 // 108000000
921 {
922 0, 0, 0, 0, 0,
923 0, 0, 0, 0, 0,
924 0
925 }
926 };
927
928 const char* PATTERN[] = {
929 "X", "XX", "XXX", "XXXX", "XXXXX",
930 "x", "xx", "xxx", "xxxx", "xxxxx",
931 "Z", // equivalent to "xxxx"
932 0
933 };
934
935 const int32_t MIN_OFFSET_UNIT[] = {
936 60000, 60000, 60000, 1000, 1000,
937 60000, 60000, 60000, 1000, 1000,
938 1000,
939 };
940
941 // Formatting
942 UErrorCode status = U_ZERO_ERROR;
b331163b 943 LocalPointer<SimpleDateFormat> sdf(new SimpleDateFormat(status), status);
51004dcb
A
944 if (U_FAILURE(status)) {
945 dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status));
946 return;
947 }
948 UDate d = Calendar::getNow();
949
2ca993e8 950 for (uint32_t i = 0; i < UPRV_LENGTHOF(OFFSET); i++) {
51004dcb
A
951 SimpleTimeZone* tz = new SimpleTimeZone(OFFSET[i], UnicodeString("Zone Offset:") + OFFSET[i] + "ms");
952 sdf->adoptTimeZone(tz);
953 for (int32_t j = 0; PATTERN[j] != 0; j++) {
954 sdf->applyPattern(UnicodeString(PATTERN[j]));
955 UnicodeString result;
956 sdf->format(d, result);
957
958 if (ISO_STR[i][j]) {
959 if (result != UnicodeString(ISO_STR[i][j])) {
960 errln((UnicodeString)"FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> "
961 + result + " (expected: " + ISO_STR[i][j] + ")");
962 }
963 } else {
964 // Offset out of range
965 // Note: for now, there is no way to propagate the error status through
966 // the SimpleDateFormat::format above.
967 if (result.length() > 0) {
968 errln((UnicodeString)"FAIL: Non-Empty result for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i]
969 + " (expected: empty result)");
970 }
971 }
972 }
973 }
974
975 // Parsing
976 LocalPointer<Calendar> outcal(Calendar::createInstance(status));
977 if (U_FAILURE(status)) {
978 dataerrln("Fail new Calendar: %s", u_errorName(status));
979 return;
980 }
981 for (int32_t i = 0; ISO_STR[i][0] != NULL; i++) {
982 for (int32_t j = 0; PATTERN[j] != 0; j++) {
983 if (ISO_STR[i][j] == 0) {
984 continue;
985 }
986 ParsePosition pos(0);
987 SimpleTimeZone* bogusTZ = new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms"));
988 outcal->adoptTimeZone(bogusTZ);
989 sdf->applyPattern(PATTERN[j]);
990
991 sdf->parse(UnicodeString(ISO_STR[i][j]), *(outcal.getAlias()), pos);
992
993 if (pos.getIndex() != (int32_t)uprv_strlen(ISO_STR[i][j])) {
994 errln((UnicodeString)"FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]);
995 }
996
997 const TimeZone& outtz = outcal->getTimeZone();
998 int32_t outOffset = outtz.getRawOffset();
999 int32_t adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j];
1000 if (outOffset != adjustedOffset) {
1001 errln((UnicodeString)"FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j]
1002 + " (expected:" + adjustedOffset + "ms)");
1003 }
1004 }
1005 }
1006}
1007
1008
57a6839d
A
1009typedef struct {
1010 const char* locale;
1011 const char* tzid;
1012 UDate date;
1013 UTimeZoneFormatStyle style;
1014 const char* expected;
1015 UTimeZoneFormatTimeType timeType;
1016} FormatTestData;
1017
1018void
1019TimeZoneFormatTest::TestFormat(void) {
1020 UDate dateJan = 1358208000000.0; // 2013-01-15T00:00:00Z
1021 UDate dateJul = 1373846400000.0; // 2013-07-15T00:00:00Z
1022
1023 const FormatTestData DATA[] = {
1024 {
1025 "en",
1026 "America/Los_Angeles",
1027 dateJan,
1028 UTZFMT_STYLE_GENERIC_LOCATION,
1029 "Los Angeles Time",
1030 UTZFMT_TIME_TYPE_UNKNOWN
1031 },
1032 {
1033 "en",
1034 "America/Los_Angeles",
1035 dateJan,
1036 UTZFMT_STYLE_GENERIC_LONG,
1037 "Pacific Time",
1038 UTZFMT_TIME_TYPE_UNKNOWN
1039 },
1040 {
1041 "en",
1042 "America/Los_Angeles",
1043 dateJan,
1044 UTZFMT_STYLE_SPECIFIC_LONG,
1045 "Pacific Standard Time",
1046 UTZFMT_TIME_TYPE_STANDARD
1047 },
1048 {
1049 "en",
1050 "America/Los_Angeles",
1051 dateJul,
1052 UTZFMT_STYLE_SPECIFIC_LONG,
1053 "Pacific Daylight Time",
1054 UTZFMT_TIME_TYPE_DAYLIGHT
1055 },
1056 {
1057 "ja",
1058 "America/Los_Angeles",
1059 dateJan,
1060 UTZFMT_STYLE_ZONE_ID,
1061 "America/Los_Angeles",
1062 UTZFMT_TIME_TYPE_UNKNOWN
1063 },
1064 {
1065 "fr",
1066 "America/Los_Angeles",
1067 dateJul,
1068 UTZFMT_STYLE_ZONE_ID_SHORT,
1069 "uslax",
1070 UTZFMT_TIME_TYPE_UNKNOWN
1071 },
1072 {
1073 "en",
1074 "America/Los_Angeles",
1075 dateJan,
1076 UTZFMT_STYLE_EXEMPLAR_LOCATION,
1077 "Los Angeles",
1078 UTZFMT_TIME_TYPE_UNKNOWN
1079 },
1080
1081 {
1082 "ja",
1083 "Asia/Tokyo",
1084 dateJan,
1085 UTZFMT_STYLE_GENERIC_LONG,
1086 "\\u65E5\\u672C\\u6A19\\u6E96\\u6642",
1087 UTZFMT_TIME_TYPE_UNKNOWN
1088 },
1089
1090 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
1091 };
1092
1093 for (int32_t i = 0; DATA[i].locale; i++) {
1094 UErrorCode status = U_ZERO_ERROR;
1095 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status));
1096 if (U_FAILURE(status)) {
1097 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
1098 continue;
1099 }
1100
1101 LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
1102 UnicodeString out;
1103 UTimeZoneFormatTimeType timeType;
1104
1105 tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
1106 UnicodeString expected(DATA[i].expected, -1, US_INV);
1107 expected = expected.unescape();
1108
1109 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
1110 if (DATA[i].timeType != timeType) {
1111 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
1112 + timeType + ", expected=" + DATA[i].timeType);
1113 }
1114 }
1115}
1116
b331163b
A
1117void
1118TimeZoneFormatTest::TestFormatTZDBNames(void) {
1119 UDate dateJan = 1358208000000.0; // 2013-01-15T00:00:00Z
1120 UDate dateJul = 1373846400000.0; // 2013-07-15T00:00:00Z
1121
1122 const FormatTestData DATA[] = {
1123 {
1124 "en",
1125 "America/Chicago",
1126 dateJan,
1127 UTZFMT_STYLE_SPECIFIC_SHORT,
1128 "CST",
1129 UTZFMT_TIME_TYPE_STANDARD
1130 },
1131 {
1132 "en",
1133 "Asia/Shanghai",
1134 dateJan,
1135 UTZFMT_STYLE_SPECIFIC_SHORT,
1136 "CST",
1137 UTZFMT_TIME_TYPE_STANDARD
1138 },
1139 {
1140 "zh_Hans",
1141 "Asia/Shanghai",
1142 dateJan,
1143 UTZFMT_STYLE_SPECIFIC_SHORT,
1144 "CST",
1145 UTZFMT_TIME_TYPE_STANDARD
1146 },
1147 {
1148 "en",
1149 "America/Los_Angeles",
1150 dateJul,
1151 UTZFMT_STYLE_SPECIFIC_LONG,
1152 "GMT-07:00", // No long display names
1153 UTZFMT_TIME_TYPE_DAYLIGHT
1154 },
1155 {
1156 "ja",
1157 "America/Los_Angeles",
1158 dateJul,
1159 UTZFMT_STYLE_SPECIFIC_SHORT,
1160 "PDT",
1161 UTZFMT_TIME_TYPE_DAYLIGHT
1162 },
1163 {
1164 "en",
1165 "Australia/Sydney",
1166 dateJan,
1167 UTZFMT_STYLE_SPECIFIC_SHORT,
1168 "AEDT",
1169 UTZFMT_TIME_TYPE_DAYLIGHT
1170 },
1171 {
1172 "en",
1173 "Australia/Sydney",
1174 dateJul,
1175 UTZFMT_STYLE_SPECIFIC_SHORT,
1176 "AEST",
1177 UTZFMT_TIME_TYPE_STANDARD
1178 },
1179
1180 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN}
1181 };
1182
1183 for (int32_t i = 0; DATA[i].locale; i++) {
1184 UErrorCode status = U_ZERO_ERROR;
1185 Locale loc(DATA[i].locale);
1186 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(loc, status));
1187 if (U_FAILURE(status)) {
1188 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status));
1189 continue;
1190 }
1191 TimeZoneNames *tzdbNames = TimeZoneNames::createTZDBInstance(loc, status);
1192 if (U_FAILURE(status)) {
1193 dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status));
1194 continue;
1195 }
1196 tzfmt->adoptTimeZoneNames(tzdbNames);
1197
1198 LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid));
1199 UnicodeString out;
1200 UTimeZoneFormatTimeType timeType;
1201
1202 tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType);
1203 UnicodeString expected(DATA[i].expected, -1, US_INV);
1204 expected = expected.unescape();
1205
1206 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out);
1207 if (DATA[i].timeType != timeType) {
1208 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned="
1209 + timeType + ", expected=" + DATA[i].timeType);
1210 }
1211 }
1212}
1213
1214
46f4442e 1215#endif /* #if !UCONFIG_NO_FORMATTING */