1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007-2014, 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 #if (ICU_USE_THREADS==0) 682 TestTimeRoundTripThread fakeThread(*this, data, 0); 683 fakeThread.run(); 684 #else 685 TestTimeRoundTripThread **threads = new TestTimeRoundTripThread*[threadCount]; 686 int32_t i; 687 for (i = 0; i < nThreads; i++) { 688 threads[i] = new TestTimeRoundTripThread(*this, data, i); 689 if (threads[i]->start() != 0) { 690 errln("Error starting thread %d", i); 691 } 692 } 693 694 UBool done = false; 695 while (true) { 696 umtx_lock(NULL); 697 if (data.numDone == nLocales) { 698 done = true; 699 } 700 umtx_unlock(NULL); 701 if (done) 702 break; 703 SimpleThread::sleep(1000); 704 } 705 706 for (i = 0; i < nThreads; i++) { 707 delete threads[i]; 708 } 709 delete [] threads; 710 711 #endif 712 UDate total = 0; 713 logln("### Elapsed time by patterns ###"); 714 for (int32_t i = 0; i < NUM_PATTERNS; i++) { 715 logln(UnicodeString("") + data.times[i] + "ms (" + PATTERNS[i] + ")"); 716 total += data.times[i]; 717 } 718 logln((UnicodeString) "Total: " + total + "ms"); 719 logln((UnicodeString) "Iteration: " + data.testCounts); 720 721 delete cal; 722 } 723 724 725 typedef struct { 726 const char* text; 727 int32_t inPos; 728 const char* locale; 729 UTimeZoneFormatStyle style; 730 uint32_t parseOptions; 731 const char* expected; 732 int32_t outPos; 733 UTimeZoneFormatTimeType timeType; 734 } ParseTestData; 735 736 void 737 TimeZoneFormatTest::TestParse(void) { 738 const ParseTestData DATA[] = { 739 // text inPos locale style 740 // parseOptions expected outPos timeType 741 {"Z", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, 742 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, 743 744 {"Z", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, 745 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, 746 747 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, 748 UTZFMT_PARSE_OPTION_ALL_STYLES, "Etc/GMT", 1, UTZFMT_TIME_TYPE_UNKNOWN}, 749 750 {"Zambia time", 0, "en_US", UTZFMT_STYLE_GENERIC_LOCATION, 751 UTZFMT_PARSE_OPTION_NONE, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, 752 753 {"Zambia time", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, 754 UTZFMT_PARSE_OPTION_ALL_STYLES, "Africa/Lusaka", 11, UTZFMT_TIME_TYPE_UNKNOWN}, 755 756 {"+00:00", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, 757 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 6, UTZFMT_TIME_TYPE_UNKNOWN}, 758 759 {"-01:30:45", 0, "en_US", UTZFMT_STYLE_ISO_EXTENDED_FULL, 760 UTZFMT_PARSE_OPTION_NONE, "GMT-01:30:45", 9, UTZFMT_TIME_TYPE_UNKNOWN}, 761 762 {"-7", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, 763 UTZFMT_PARSE_OPTION_NONE, "GMT-07:00", 2, UTZFMT_TIME_TYPE_UNKNOWN}, 764 765 {"-2222", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, 766 UTZFMT_PARSE_OPTION_NONE, "GMT-22:22", 5, UTZFMT_TIME_TYPE_UNKNOWN}, 767 768 {"-3333", 0, "en_US", UTZFMT_STYLE_ISO_BASIC_LOCAL_FULL, 769 UTZFMT_PARSE_OPTION_NONE, "GMT-03:33", 4, UTZFMT_TIME_TYPE_UNKNOWN}, 770 771 {"XXX+01:30YYY", 3, "en_US", UTZFMT_STYLE_LOCALIZED_GMT, 772 UTZFMT_PARSE_OPTION_NONE, "GMT+01:30", 9, UTZFMT_TIME_TYPE_UNKNOWN}, 773 774 {"GMT0", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, 775 UTZFMT_PARSE_OPTION_NONE, "Etc/GMT", 3, UTZFMT_TIME_TYPE_UNKNOWN}, 776 777 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, 778 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, 779 780 {"ESTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, 781 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, 782 783 {"EDTx", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, 784 UTZFMT_PARSE_OPTION_NONE, "America/New_York", 3, UTZFMT_TIME_TYPE_DAYLIGHT}, 785 786 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, 787 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, 788 789 {"EST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_LONG, 790 UTZFMT_PARSE_OPTION_ALL_STYLES, "America/New_York", 3, UTZFMT_TIME_TYPE_STANDARD}, 791 792 {"EST", 0, "en_CA", UTZFMT_STYLE_SPECIFIC_SHORT, 793 UTZFMT_PARSE_OPTION_NONE, "America/Toronto", 3, UTZFMT_TIME_TYPE_STANDARD}, 794 795 {"CST", 0, "en_US", UTZFMT_STYLE_SPECIFIC_SHORT, 796 UTZFMT_PARSE_OPTION_NONE, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD}, 797 798 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, 799 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, 800 801 {"CST", 0, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, 802 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 3, UTZFMT_TIME_TYPE_STANDARD}, 803 804 {"--CST--", 2, "en_GB", UTZFMT_STYLE_SPECIFIC_SHORT, 805 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "America/Chicago", 5, UTZFMT_TIME_TYPE_STANDARD}, 806 807 {"CST", 0, "zh_CN", UTZFMT_STYLE_SPECIFIC_SHORT, 808 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Shanghai", 3, UTZFMT_TIME_TYPE_STANDARD}, 809 810 {"AEST", 0, "en_AU", UTZFMT_STYLE_SPECIFIC_SHORT, 811 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Australia/Sydney", 4, UTZFMT_TIME_TYPE_STANDARD}, 812 813 {"AST", 0, "ar_SA", UTZFMT_STYLE_SPECIFIC_SHORT, 814 UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Riyadh", 3, UTZFMT_TIME_TYPE_STANDARD}, 815 816 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, 817 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, 818 819 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, 820 UTZFMT_PARSE_OPTION_ALL_STYLES, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN}, 821 822 {"AQTST", 0, "en", UTZFMT_STYLE_SPECIFIC_LONG, 823 UTZFMT_PARSE_OPTION_ALL_STYLES | UTZFMT_PARSE_OPTION_TZ_DATABASE_ABBREVIATIONS, "Asia/Aqtobe", 5, UTZFMT_TIME_TYPE_DAYLIGHT}, 824 825 {NULL, 0, NULL, UTZFMT_STYLE_GENERIC_LOCATION, 826 UTZFMT_PARSE_OPTION_NONE, NULL, 0, UTZFMT_TIME_TYPE_UNKNOWN} 827 }; 828 829 for (int32_t i = 0; DATA[i].text; i++) { 830 UErrorCode status = U_ZERO_ERROR; 831 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status)); 832 if (U_FAILURE(status)) { 833 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status)); 834 continue; 835 } 836 UTimeZoneFormatTimeType ttype = UTZFMT_TIME_TYPE_UNKNOWN; 837 ParsePosition pos(DATA[i].inPos); 838 TimeZone* tz = tzfmt->parse(DATA[i].style, DATA[i].text, pos, DATA[i].parseOptions, &ttype); 839 840 UnicodeString errMsg; 841 if (tz) { 842 UnicodeString outID; 843 tz->getID(outID); 844 if (outID != UnicodeString(DATA[i].expected)) { 845 errMsg = (UnicodeString)"Time zone ID: " + outID + " - expected: " + DATA[i].expected; 846 } else if (pos.getIndex() != DATA[i].outPos) { 847 errMsg = (UnicodeString)"Parsed pos: " + pos.getIndex() + " - expected: " + DATA[i].outPos; 848 } else if (ttype != DATA[i].timeType) { 849 errMsg = (UnicodeString)"Time type: " + ttype + " - expected: " + DATA[i].timeType; 850 } 851 delete tz; 852 } else { 853 if (DATA[i].expected) { 854 errln((UnicodeString)"Fail: Parse failure - expected: " + DATA[i].expected); 855 } 856 } 857 if (errMsg.length() > 0) { 858 errln((UnicodeString)"Fail: " + errMsg + " [text=" + DATA[i].text + ", pos=" + DATA[i].inPos + ", style=" + DATA[i].style + "]"); 859 } 860 } 861 } 862 863 void 864 TimeZoneFormatTest::TestISOFormat(void) { 865 const int32_t OFFSET[] = { 866 0, // 0 867 999, // 0.999s 868 -59999, // -59.999s 869 60000, // 1m 870 -77777, // -1m 17.777s 871 1800000, // 30m 872 -3600000, // -1h 873 36000000, // 10h 874 -37800000, // -10h 30m 875 -37845000, // -10h 30m 45s 876 108000000, // 30h 877 }; 878 879 const char* ISO_STR[][11] = { 880 // 0 881 { 882 "Z", "Z", "Z", "Z", "Z", 883 "+00", "+0000", "+00:00", "+0000", "+00:00", 884 "+0000" 885 }, 886 // 999 887 { 888 "Z", "Z", "Z", "Z", "Z", 889 "+00", "+0000", "+00:00", "+0000", "+00:00", 890 "+0000" 891 }, 892 // -59999 893 { 894 "Z", "Z", "Z", "-000059", "-00:00:59", 895 "+00", "+0000", "+00:00", "-000059", "-00:00:59", 896 "-000059" 897 }, 898 // 60000 899 { 900 "+0001", "+0001", "+00:01", "+0001", "+00:01", 901 "+0001", "+0001", "+00:01", "+0001", "+00:01", 902 "+0001" 903 }, 904 // -77777 905 { 906 "-0001", "-0001", "-00:01", "-000117", "-00:01:17", 907 "-0001", "-0001", "-00:01", "-000117", "-00:01:17", 908 "-000117" 909 }, 910 // 1800000 911 { 912 "+0030", "+0030", "+00:30", "+0030", "+00:30", 913 "+0030", "+0030", "+00:30", "+0030", "+00:30", 914 "+0030" 915 }, 916 // -3600000 917 { 918 "-01", "-0100", "-01:00", "-0100", "-01:00", 919 "-01", "-0100", "-01:00", "-0100", "-01:00", 920 "-0100" 921 }, 922 // 36000000 923 { 924 "+10", "+1000", "+10:00", "+1000", "+10:00", 925 "+10", "+1000", "+10:00", "+1000", "+10:00", 926 "+1000" 927 }, 928 // -37800000 929 { 930 "-1030", "-1030", "-10:30", "-1030", "-10:30", 931 "-1030", "-1030", "-10:30", "-1030", "-10:30", 932 "-1030" 933 }, 934 // -37845000 935 { 936 "-1030", "-1030", "-10:30", "-103045", "-10:30:45", 937 "-1030", "-1030", "-10:30", "-103045", "-10:30:45", 938 "-103045" 939 }, 940 // 108000000 941 { 942 0, 0, 0, 0, 0, 943 0, 0, 0, 0, 0, 944 0 945 } 946 }; 947 948 const char* PATTERN[] = { 949 "X", "XX", "XXX", "XXXX", "XXXXX", 950 "x", "xx", "xxx", "xxxx", "xxxxx", 951 "Z", // equivalent to "xxxx" 952 0 953 }; 954 955 const int32_t MIN_OFFSET_UNIT[] = { 956 60000, 60000, 60000, 1000, 1000, 957 60000, 60000, 60000, 1000, 1000, 958 1000, 959 }; 960 961 // Formatting 962 UErrorCode status = U_ZERO_ERROR; 963 LocalPointer<SimpleDateFormat> sdf(new SimpleDateFormat(status), status); 964 if (U_FAILURE(status)) { 965 dataerrln("Fail new SimpleDateFormat: %s", u_errorName(status)); 966 return; 967 } 968 UDate d = Calendar::getNow(); 969 970 for (uint32_t i = 0; i < sizeof(OFFSET)/sizeof(OFFSET[0]); i++) { 971 SimpleTimeZone* tz = new SimpleTimeZone(OFFSET[i], UnicodeString("Zone Offset:") + OFFSET[i] + "ms"); 972 sdf->adoptTimeZone(tz); 973 for (int32_t j = 0; PATTERN[j] != 0; j++) { 974 sdf->applyPattern(UnicodeString(PATTERN[j])); 975 UnicodeString result; 976 sdf->format(d, result); 977 978 if (ISO_STR[i][j]) { 979 if (result != UnicodeString(ISO_STR[i][j])) { 980 errln((UnicodeString)"FAIL: pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] + " -> " 981 + result + " (expected: " + ISO_STR[i][j] + ")"); 982 } 983 } else { 984 // Offset out of range 985 // Note: for now, there is no way to propagate the error status through 986 // the SimpleDateFormat::format above. 987 if (result.length() > 0) { 988 errln((UnicodeString)"FAIL: Non-Empty result for pattern=" + PATTERN[j] + ", offset=" + OFFSET[i] 989 + " (expected: empty result)"); 990 } 991 } 992 } 993 } 994 995 // Parsing 996 LocalPointer<Calendar> outcal(Calendar::createInstance(status)); 997 if (U_FAILURE(status)) { 998 dataerrln("Fail new Calendar: %s", u_errorName(status)); 999 return; 1000 } 1001 for (int32_t i = 0; ISO_STR[i][0] != NULL; i++) { 1002 for (int32_t j = 0; PATTERN[j] != 0; j++) { 1003 if (ISO_STR[i][j] == 0) { 1004 continue; 1005 } 1006 ParsePosition pos(0); 1007 SimpleTimeZone* bogusTZ = new SimpleTimeZone(-1, UnicodeString("Zone Offset: -1ms")); 1008 outcal->adoptTimeZone(bogusTZ); 1009 sdf->applyPattern(PATTERN[j]); 1010 1011 sdf->parse(UnicodeString(ISO_STR[i][j]), *(outcal.getAlias()), pos); 1012 1013 if (pos.getIndex() != (int32_t)uprv_strlen(ISO_STR[i][j])) { 1014 errln((UnicodeString)"FAIL: Failed to parse the entire input string: " + ISO_STR[i][j]); 1015 } 1016 1017 const TimeZone& outtz = outcal->getTimeZone(); 1018 int32_t outOffset = outtz.getRawOffset(); 1019 int32_t adjustedOffset = OFFSET[i] / MIN_OFFSET_UNIT[j] * MIN_OFFSET_UNIT[j]; 1020 if (outOffset != adjustedOffset) { 1021 errln((UnicodeString)"FAIL: Incorrect offset:" + outOffset + "ms for input string: " + ISO_STR[i][j] 1022 + " (expected:" + adjustedOffset + "ms)"); 1023 } 1024 } 1025 } 1026 } 1027 1028 1029 typedef struct { 1030 const char* locale; 1031 const char* tzid; 1032 UDate date; 1033 UTimeZoneFormatStyle style; 1034 const char* expected; 1035 UTimeZoneFormatTimeType timeType; 1036 } FormatTestData; 1037 1038 void 1039 TimeZoneFormatTest::TestFormat(void) { 1040 UDate dateJan = 1358208000000.0; // 2013-01-15T00:00:00Z 1041 UDate dateJul = 1373846400000.0; // 2013-07-15T00:00:00Z 1042 1043 const FormatTestData DATA[] = { 1044 { 1045 "en", 1046 "America/Los_Angeles", 1047 dateJan, 1048 UTZFMT_STYLE_GENERIC_LOCATION, 1049 "Los Angeles Time", 1050 UTZFMT_TIME_TYPE_UNKNOWN 1051 }, 1052 { 1053 "en", 1054 "America/Los_Angeles", 1055 dateJan, 1056 UTZFMT_STYLE_GENERIC_LONG, 1057 "Pacific Time", 1058 UTZFMT_TIME_TYPE_UNKNOWN 1059 }, 1060 { 1061 "en", 1062 "America/Los_Angeles", 1063 dateJan, 1064 UTZFMT_STYLE_SPECIFIC_LONG, 1065 "Pacific Standard Time", 1066 UTZFMT_TIME_TYPE_STANDARD 1067 }, 1068 { 1069 "en", 1070 "America/Los_Angeles", 1071 dateJul, 1072 UTZFMT_STYLE_SPECIFIC_LONG, 1073 "Pacific Daylight Time", 1074 UTZFMT_TIME_TYPE_DAYLIGHT 1075 }, 1076 { 1077 "ja", 1078 "America/Los_Angeles", 1079 dateJan, 1080 UTZFMT_STYLE_ZONE_ID, 1081 "America/Los_Angeles", 1082 UTZFMT_TIME_TYPE_UNKNOWN 1083 }, 1084 { 1085 "fr", 1086 "America/Los_Angeles", 1087 dateJul, 1088 UTZFMT_STYLE_ZONE_ID_SHORT, 1089 "uslax", 1090 UTZFMT_TIME_TYPE_UNKNOWN 1091 }, 1092 { 1093 "en", 1094 "America/Los_Angeles", 1095 dateJan, 1096 UTZFMT_STYLE_EXEMPLAR_LOCATION, 1097 "Los Angeles", 1098 UTZFMT_TIME_TYPE_UNKNOWN 1099 }, 1100 1101 { 1102 "ja", 1103 "Asia/Tokyo", 1104 dateJan, 1105 UTZFMT_STYLE_GENERIC_LONG, 1106 "\\u65E5\\u672C\\u6A19\\u6E96\\u6642", 1107 UTZFMT_TIME_TYPE_UNKNOWN 1108 }, 1109 1110 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN} 1111 }; 1112 1113 for (int32_t i = 0; DATA[i].locale; i++) { 1114 UErrorCode status = U_ZERO_ERROR; 1115 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(Locale(DATA[i].locale), status)); 1116 if (U_FAILURE(status)) { 1117 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status)); 1118 continue; 1119 } 1120 1121 LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid)); 1122 UnicodeString out; 1123 UTimeZoneFormatTimeType timeType; 1124 1125 tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType); 1126 UnicodeString expected(DATA[i].expected, -1, US_INV); 1127 expected = expected.unescape(); 1128 1129 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out); 1130 if (DATA[i].timeType != timeType) { 1131 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned=" 1132 + timeType + ", expected=" + DATA[i].timeType); 1133 } 1134 } 1135 } 1136 1137 void 1138 TimeZoneFormatTest::TestFormatTZDBNames(void) { 1139 UDate dateJan = 1358208000000.0; // 2013-01-15T00:00:00Z 1140 UDate dateJul = 1373846400000.0; // 2013-07-15T00:00:00Z 1141 1142 const FormatTestData DATA[] = { 1143 { 1144 "en", 1145 "America/Chicago", 1146 dateJan, 1147 UTZFMT_STYLE_SPECIFIC_SHORT, 1148 "CST", 1149 UTZFMT_TIME_TYPE_STANDARD 1150 }, 1151 { 1152 "en", 1153 "Asia/Shanghai", 1154 dateJan, 1155 UTZFMT_STYLE_SPECIFIC_SHORT, 1156 "CST", 1157 UTZFMT_TIME_TYPE_STANDARD 1158 }, 1159 { 1160 "zh_Hans", 1161 "Asia/Shanghai", 1162 dateJan, 1163 UTZFMT_STYLE_SPECIFIC_SHORT, 1164 "CST", 1165 UTZFMT_TIME_TYPE_STANDARD 1166 }, 1167 { 1168 "en", 1169 "America/Los_Angeles", 1170 dateJul, 1171 UTZFMT_STYLE_SPECIFIC_LONG, 1172 "GMT-07:00", // No long display names 1173 UTZFMT_TIME_TYPE_DAYLIGHT 1174 }, 1175 { 1176 "ja", 1177 "America/Los_Angeles", 1178 dateJul, 1179 UTZFMT_STYLE_SPECIFIC_SHORT, 1180 "PDT", 1181 UTZFMT_TIME_TYPE_DAYLIGHT 1182 }, 1183 { 1184 "en", 1185 "Australia/Sydney", 1186 dateJan, 1187 UTZFMT_STYLE_SPECIFIC_SHORT, 1188 "AEDT", 1189 UTZFMT_TIME_TYPE_DAYLIGHT 1190 }, 1191 { 1192 "en", 1193 "Australia/Sydney", 1194 dateJul, 1195 UTZFMT_STYLE_SPECIFIC_SHORT, 1196 "AEST", 1197 UTZFMT_TIME_TYPE_STANDARD 1198 }, 1199 1200 {0, 0, 0.0, UTZFMT_STYLE_GENERIC_LOCATION, 0, UTZFMT_TIME_TYPE_UNKNOWN} 1201 }; 1202 1203 for (int32_t i = 0; DATA[i].locale; i++) { 1204 UErrorCode status = U_ZERO_ERROR; 1205 Locale loc(DATA[i].locale); 1206 LocalPointer<TimeZoneFormat> tzfmt(TimeZoneFormat::createInstance(loc, status)); 1207 if (U_FAILURE(status)) { 1208 dataerrln("Fail TimeZoneFormat::createInstance: %s", u_errorName(status)); 1209 continue; 1210 } 1211 TimeZoneNames *tzdbNames = TimeZoneNames::createTZDBInstance(loc, status); 1212 if (U_FAILURE(status)) { 1213 dataerrln("Fail TimeZoneNames::createTZDBInstance: %s", u_errorName(status)); 1214 continue; 1215 } 1216 tzfmt->adoptTimeZoneNames(tzdbNames); 1217 1218 LocalPointer<TimeZone> tz(TimeZone::createTimeZone(DATA[i].tzid)); 1219 UnicodeString out; 1220 UTimeZoneFormatTimeType timeType; 1221 1222 tzfmt->format(DATA[i].style, *(tz.getAlias()), DATA[i].date, out, &timeType); 1223 UnicodeString expected(DATA[i].expected, -1, US_INV); 1224 expected = expected.unescape(); 1225 1226 assertEquals(UnicodeString("Format result for ") + DATA[i].tzid + " (Test Case " + i + ")", expected, out); 1227 if (DATA[i].timeType != timeType) { 1228 dataerrln(UnicodeString("Formatted time zone type (Test Case ") + i + "), returned=" 1229 + timeType + ", expected=" + DATA[i].timeType); 1230 } 1231 } 1232 } 1233 1234 1235 #endif /* #if !UCONFIG_NO_FORMATTING */ 1236