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