| 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 |