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, 0x6E, 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((UnicodeString)"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 pattern " + 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 instance 187 // just for making sure that it does not depends on the time zone 188 // originally set. 189 sdf->setTimeZone(unknownZone); 190 191 // Parse 192 ParsePosition pos(0); 193 Calendar *outcal = Calendar::createInstance(unknownZone, status); 194 if (U_FAILURE(status)) { 195 errln("Failed to create an instance of calendar for receiving parse result."); 196 status = U_ZERO_ERROR; 197 continue; 198 } 199 outcal->set(UCAL_DST_OFFSET, badDstOffset); 200 outcal->set(UCAL_ZONE_OFFSET, badZoneOffset); 201 202 sdf->parse(tzstr, *outcal, pos); 203 204 // Check the result 205 const TimeZone &outtz = outcal->getTimeZone(); 206 UnicodeString outtzid; 207 outtz.getID(outtzid); 208 209 tz->getOffset(DATES[datidx], false, inRaw, inDst, status); 210 if (U_FAILURE(status)) { 211 errln((UnicodeString)"Failed to get offsets from time zone" + *tzid); 212 status = U_ZERO_ERROR; 213 } 214 outtz.getOffset(DATES[datidx], false, outRaw, outDst, status); 215 if (U_FAILURE(status)) { 216 errln((UnicodeString)"Failed to get offsets from time zone" + outtzid); 217 status = U_ZERO_ERROR; 218 } 219 220 if (uprv_strcmp(PATTERNS[patidx], "V") == 0) { 221 // Short zone ID - should support roundtrip for canonical CLDR IDs 222 UnicodeString canonicalID; 223 TimeZone::getCanonicalID(*tzid, canonicalID, status); 224 if (U_FAILURE(status)) { 225 // Uknown ID - we should not get here 226 errln((UnicodeString)"Unknown ID " + *tzid); 227 status = U_ZERO_ERROR; 228 } else if (outtzid != canonicalID) { 229 if (outtzid.compare(ETC_UNKNOWN, -1) == 0) { 230 // Note that some zones like Asia/Riyadh87 does not have 231 // short zone ID and "unk" is used as fallback 232 logln((UnicodeString)"Canonical round trip failed (probably as expected); tz=" + *tzid 233 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx] 234 + ", time=" + DATES[datidx] + ", str=" + tzstr 235 + ", outtz=" + outtzid); 236 } else { 237 errln((UnicodeString)"Canonical round trip failed; tz=" + *tzid 238 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx] 239 + ", time=" + DATES[datidx] + ", str=" + tzstr 240 + ", outtz=" + outtzid); 241 } 242 } 243 } else if (uprv_strcmp(PATTERNS[patidx], "VV") == 0) { 244 // Zone ID - full roundtrip support 245 if (outtzid != *tzid) { 246 errln((UnicodeString)"Zone ID round trip failued; tz=" + *tzid 247 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx] 248 + ", time=" + DATES[datidx] + ", str=" + tzstr 249 + ", outtz=" + outtzid); 250 } 251 } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0 || uprv_strcmp(PATTERNS[patidx], "VVVV") == 0) { 252 // Location: time zone rule must be preserved except 253 // zones not actually associated with a specific location. 254 // Time zones in this category do not have "/" in its ID. 255 UnicodeString canonical; 256 TimeZone::getCanonicalID(*tzid, canonical, status); 257 if (U_FAILURE(status)) { 258 // Uknown ID - we should not get here 259 errln((UnicodeString)"Unknown ID " + *tzid); 260 status = U_ZERO_ERROR; 261 } else if (outtzid != canonical) { 262 // Canonical ID did not match - check the rules 263 if (!((BasicTimeZone*)&outtz)->hasEquivalentTransitions((BasicTimeZone&)*tz, low, high, TRUE, status)) { 264 if (canonical.indexOf((UChar)0x27 /*'/'*/) == -1) { 265 // Exceptional cases, such as CET, EET, MET and WET 266 logln((UnicodeString)"Canonical round trip failed (as expected); tz=" + *tzid 267 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx] 268 + ", time=" + DATES[datidx] + ", str=" + tzstr 269 + ", outtz=" + outtzid); 270 } else { 271 errln((UnicodeString)"Canonical round trip failed; 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 fallback of name styles 295 int32_t numDigits = 0; 296 for (int n = 0; n < tzstr.length(); n++) { 297 if (u_isdigit(tzstr.charAt(n))) { 298 numDigits++; 299 } 300 } 301 isOffsetFormat = (numDigits > 0); 302 } 303 if (isOffsetFormat || tzstr == localGMTString) { 304 // Localized GMT or ISO: total offset (raw + dst) must 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=" + tzstr 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 failed; tz=" + *tzid 321 + ", locale=" + LOCALES[locidx].getName() + ", pattern=" + PATTERNS[patidx] 322 + ", time=" + DATES[datidx] + ", str=" + tzstr 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, EXCLUSIONS[i].pattern) == 0) { 362 if (EXCLUSIONS[i].time == U_DATE_MIN || EXCLUSIONS[i].time == time) { 363 isExcluded = TRUE; 364 } 365 } 366 } 367 } 368 } 369 return isExcluded; 370 } 371 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_ZONE_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. However, an infinite loop will occur 415 * because the thread thinks that the test data is never done so we should treat the data as done. 416 */ 417 log.dataerrln("TimeZone::createTimeZoneIDEnumeration failed - %s", u_errorName(status)); 418 data.numDone = data.nLocales; 419 } else { 420 log.errln("TimeZone::createTimeZoneIDEnumeration failed: %s", u_errorName(status)); 421 } 422 return; 423 } 424 425 int32_t locidx = -1; 426 UDate times[NUM_PATTERNS]; 427 for (int32_t i = 0; i < NUM_PATTERNS; i++) { 428 times[i] = 0; 429 } 430 431 int32_t testCounts = 0; 432 433 while (true) { 434 umtx_lock(NULL); // Lock to increment the index 435 for (int32_t i = 0; i < NUM_PATTERNS; i++) { 436 data.times[i] += times[i]; 437 data.testCounts += testCounts; 438 } 439 if (data.index < data.nLocales) { 440 locidx = data.index; 441 data.index++; 442 } else { 443 locidx = -1; 444 } 445 umtx_unlock(NULL); // Unlock for other threads to use 446 447 if (locidx == -1) { 448 log.logln((UnicodeString) "Thread " + index + " is done."); 449 break; 450 } 451 452 log.logln((UnicodeString) "\nThread " + index + ": Locale: " + UnicodeString(data.locales[locidx].getName())); 453 454 for (int32_t patidx = 0; patidx < NUM_PATTERNS; patidx++) { 455 log.logln((UnicodeString) " Pattern: " + PATTERNS[patidx]); 456 times[patidx] = 0; 457 458 UnicodeString pattern(BASEPATTERN); 459 pattern.append(" ").append(PATTERNS[patidx]); 460 461 SimpleDateFormat *sdf = new SimpleDateFormat(pattern, data.locales[locidx], status); 462 if (U_FAILURE(status)) { 463 log.errcheckln(status, (UnicodeString) "new SimpleDateFormat failed for pattern " + 464 pattern + " for locale " + data.locales[locidx].getName() + " - " + u_errorName(status)); 465 status = U_ZERO_ERROR; 466 continue; 467 } 468 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 Asia/Riyadh87. 479 // The time roundtrip will fail for such zones with pattern "V" (short zone ID). 480 // This is expected behavior. 481 const UChar* shortZoneID = ZoneMeta::getShortID(*tzid); 482 if (shortZoneID == NULL) { 483 continue; 484 } 485 } else if (uprv_strcmp(PATTERNS[patidx], "VVV") == 0) { 486 // Some zones are not associated with any region, such as Etc/GMT+8. 487 // The time roundtrip will fail for such zone with pattern "VVV" (exemplar location). 488 // This is expected behavior. 489 if (tzid->indexOf((UChar)0x2F) < 0 || tzid->indexOf(ETC_SLASH, -1, 0) >= 0 490 || tzid->indexOf(SYSTEMV_SLASH, -1, 0) >= 0 || tzid->indexOf(RIYADH8, -1, 0) >= 0) { 491 continue; 492 } 493 } 494 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::createTimeZone(*tzid); 501 sdf->setTimeZone(*tz); 502 503 UDate t = data.START_TIME; 504 TimeZoneTransition tzt; 505 UBool tztAvail = FALSE; 506 UBool middle = TRUE; 507 508 while (t < data.END_TIME) { 509 if (!tztAvail) { 510 testTimes[0] = t; 511 expectedRoundTrip[0] = TRUE; 512 testLen = 1; 513 } else { 514 int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings(); 515 int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings(); 516 int32_t delta = toOffset - fromOffset; 517 if (delta < 0) { 518 UBool isDstDecession = tzt.getFrom()->getDSTSavings() > 0 && tzt.getTo()->getDSTSavings() == 0; 519 testTimes[0] = t + delta - 1; 520 expectedRoundTrip[0] = TRUE; 521 testTimes[1] = t + delta; 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 text=" + text + ", tzid=" + *tzid + ", locale=" + data.locales[locidx].getName() 557 + ", pattern=" + PATTERNS[patidx] + ", time=" + 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=" + testTimes[testidx] + ", restime=" + parsedDate + ", diff=" + (parsedDate - testTimes[testidx]); 568 // Timebomb for TZData update 569 if (expectedRoundTrip[testidx] 570 && !isSpecialTimeRoundTripCase(data.locales[locidx].getName(), *tzid, 571 PATTERNS[patidx], testTimes[testidx])) { 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((UnicodeString) "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"), Locale("de"), 650 Locale("de_DE"), Locale("el_GR"), Locale("en"), Locale("en_AU"), Locale("en_CA"), 651 Locale("en_US"), Locale("es"), Locale("es_ES"), Locale("es_MX"), Locale("fi_FI"), 652 Locale("fr"), Locale("fr_CA"), Locale("fr_FR"), Locale("he_IL"), Locale("hu_HU"), 653 Locale("it"), Locale("it_IT"), Locale("ja"), Locale("ja_JP"), Locale("ko"), 654 Locale("ko_KR"), Locale("nb_NO"), Locale("nl_NL"), Locale("nn_NO"), Locale("pl_PL"), 655 Locale("pt"), Locale("pt_BR"), Locale("pt_PT"), Locale("ru_RU"), Locale("sv_SE"), 656 Locale("th_TH"), Locale("tr_TR"), Locale("zh"), Locale("zh_Hans"), Locale("zh_Hans_CN"), 657 Locale("zh_Hant"), Locale("zh_Hant_TW") 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_FULL, 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_FULL, 738 UTZFMT_PARSE_OPTION_ALL_STYLES, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, 739 740 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION, 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_FULL, 747 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN}, 748 749 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, 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/Sydney", 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_LOCATION, 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(status)); 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].parseOptions, &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() + " - expected: " + 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 Offset:") + 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] + ", offset=" + 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 string: " + 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(status)); 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, &timeType); 1116 UnicodeString expected(DATA[i].expected, -1, US_INV); 1117 expected = expected.unescape(); 1118 1119 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test 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, status)); 1197 if (U_FAILURE(status)) { 1198 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status)); 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, &timeType); 1213 UnicodeString expected(DATA[i].expected, -1, US_INV); 1214 expected = expected.unescape(); 1215 1216 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test 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 */ 1226