1 /*********************************************************************** 2 * COPYRIGHT: 3 * Copyright (c) 1997-2013, International Business Machines Corporation 4 * and others. All Rights Reserved. 5 ***********************************************************************/ 6 7 #include "unicode/utypes.h" 8 9 #if !UCONFIG_NO_FORMATTING 10 11 #include "unicode/timezone.h" 12 #include "unicode/simpletz.h" 13 #include "unicode/calendar.h" 14 #include "unicode/gregocal.h" 15 #include "unicode/resbund.h" 16 #include "unicode/strenum.h" 17 #include "unicode/uversion.h" 18 #include "tztest.h" 19 #include "cmemory.h" 20 #include "putilimp.h" 21 #include "cstring.h" 22 #include "olsontz.h" 23 24 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) 25 26 #define CASE(id,test) case id: \ 27 name = #test; \ 28 if (exec) { \ 29 logln(#test "---"); logln(""); \ 30 test(); \ 31 } \ 32 break 33 34 // ***************************************************************************** 35 // class TimeZoneTest 36 // ***************************************************************************** 37 38 // Some test case data is current date/tzdata version sensitive and producing errors 39 // when year/rule are changed. Although we want to keep our eyes on test failures 40 // caused by tzdata changes while development, keep maintaining test data in maintenance 41 // stream is a little bit hassle. ICU 49 or later versions are using minor version field 42 // to indicate a development build (0) or official release build (others). For development 43 // builds, a test failure triggers an error, while release builds only report them in 44 // verbose mode with logln. 45 static UBool isDevelopmentBuild = (U_ICU_VERSION_MINOR_NUM == 0); 46 47 void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) 48 { 49 if (exec) { 50 logln("TestSuite TestTimeZone"); 51 } 52 TESTCASE_AUTO_BEGIN; 53 TESTCASE_AUTO(TestPRTOffset); 54 TESTCASE_AUTO(TestVariousAPI518); 55 TESTCASE_AUTO(TestGetAvailableIDs913); 56 TESTCASE_AUTO(TestGenericAPI); 57 TESTCASE_AUTO(TestRuleAPI); 58 TESTCASE_AUTO(TestShortZoneIDs); 59 TESTCASE_AUTO(TestCustomParse); 60 TESTCASE_AUTO(TestDisplayName); 61 TESTCASE_AUTO(TestDSTSavings); 62 TESTCASE_AUTO(TestAlternateRules); 63 TESTCASE_AUTO(TestCountries); 64 TESTCASE_AUTO(TestHistorical); 65 TESTCASE_AUTO(TestEquivalentIDs); 66 TESTCASE_AUTO(TestAliasedNames); 67 TESTCASE_AUTO(TestFractionalDST); 68 TESTCASE_AUTO(TestFebruary); 69 TESTCASE_AUTO(TestCanonicalID); 70 TESTCASE_AUTO(TestDisplayNamesMeta); 71 TESTCASE_AUTO(TestGetRegion); 72 TESTCASE_AUTO(TestGetAvailableIDsNew); 73 TESTCASE_AUTO(TestGetUnknown); 74 TESTCASE_AUTO(TestGetWindowsID); 75 TESTCASE_AUTO(TestGetIDForWindowsID); 76 TESTCASE_AUTO_END; 77 } 78 79 const int32_t TimeZoneTest::millisPerHour = 3600000; 80 81 // --------------------------------------------------------------------------------- 82 83 /** 84 * Generic API testing for API coverage. 85 */ 86 void 87 TimeZoneTest::TestGenericAPI() 88 { 89 UnicodeString id("NewGMT"); 90 int32_t offset = 12345; 91 92 SimpleTimeZone *zone = new SimpleTimeZone(offset, id); 93 if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE"); 94 95 TimeZone* zoneclone = zone->clone(); 96 if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed"); 97 zoneclone->setID("abc"); 98 if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed"); 99 delete zoneclone; 100 101 zoneclone = zone->clone(); 102 if (!(*zoneclone == *zone)) errln("FAIL: clone or operator== failed"); 103 zoneclone->setRawOffset(45678); 104 if (!(*zoneclone != *zone)) errln("FAIL: clone or operator!= failed"); 105 106 SimpleTimeZone copy(*zone); 107 if (!(copy == *zone)) errln("FAIL: copy constructor or operator== failed"); 108 copy = *(SimpleTimeZone*)zoneclone; 109 if (!(copy == *zoneclone)) errln("FAIL: assignment operator or operator== failed"); 110 111 TimeZone* saveDefault = TimeZone::createDefault(); 112 logln((UnicodeString)"TimeZone::createDefault() => " + saveDefault->getID(id)); 113 TimeZone* pstZone = TimeZone::createTimeZone("America/Los_Angeles"); 114 115 logln("call uprv_timezone() which uses the host"); 116 logln("to get the difference in seconds between coordinated universal"); 117 logln("time and local time. E.g., -28,800 for PST (GMT-8hrs)"); 118 119 int32_t tzoffset = uprv_timezone(); 120 logln(UnicodeString("Value returned from uprv_timezone = ") + tzoffset); 121 // Invert sign because UNIX semantics are backwards 122 if (tzoffset < 0) 123 tzoffset = -tzoffset; 124 if ((*saveDefault == *pstZone) && (tzoffset != 28800)) { 125 errln("FAIL: t_timezone may be incorrect. It is not 28800"); 126 } 127 128 if ((tzoffset % 900) != 0) { 129 /* 130 * Ticket#6364 and #7648 131 * A few time zones are using GMT offests not a multiple of 15 minutes. 132 * Therefore, we should not interpret such case as an error. 133 * We downgrade this from errln to infoln. When we see this message, 134 * we should examine if it is ignorable or not. 135 */ 136 infoln("WARNING: t_timezone may be incorrect. It is not a multiple of 15min.", tzoffset); 137 } 138 139 TimeZone::adoptDefault(zone); 140 TimeZone* defaultzone = TimeZone::createDefault(); 141 if (defaultzone == zone || 142 !(*defaultzone == *zone)) 143 errln("FAIL: createDefault failed"); 144 TimeZone::adoptDefault(saveDefault); 145 delete defaultzone; 146 delete zoneclone; 147 delete pstZone; 148 149 UErrorCode status = U_ZERO_ERROR; 150 const char* tzver = TimeZone::getTZDataVersion(status); 151 if (U_FAILURE(status)) { 152 errcheckln(status, "FAIL: getTZDataVersion failed - %s", u_errorName(status)); 153 } else if (uprv_strlen(tzver) != 5 /* 4 digits + 1 letter */) { 154 errln((UnicodeString)"FAIL: getTZDataVersion returned " + tzver); 155 } else { 156 logln((UnicodeString)"tzdata version: " + tzver); 157 } 158 } 159 160 // --------------------------------------------------------------------------------- 161 162 /** 163 * Test the setStartRule/setEndRule API calls. 164 */ 165 void 166 TimeZoneTest::TestRuleAPI() 167 { 168 UErrorCode status = U_ZERO_ERROR; 169 170 UDate offset = 60*60*1000*1.75; // Pick a weird offset 171 SimpleTimeZone *zone = new SimpleTimeZone((int32_t)offset, "TestZone"); 172 if (zone->useDaylightTime()) errln("FAIL: useDaylightTime should return FALSE"); 173 174 // Establish our expected transition times. Do this with a non-DST 175 // calendar with the (above) declared local offset. 176 GregorianCalendar *gc = new GregorianCalendar(*zone, status); 177 if (failure(status, "new GregorianCalendar", TRUE)) return; 178 gc->clear(); 179 gc->set(1990, UCAL_MARCH, 1); 180 UDate marchOneStd = gc->getTime(status); // Local Std time midnight 181 gc->clear(); 182 gc->set(1990, UCAL_JULY, 1); 183 UDate julyOneStd = gc->getTime(status); // Local Std time midnight 184 if (failure(status, "GregorianCalendar::getTime")) return; 185 186 // Starting and ending hours, WALL TIME 187 int32_t startHour = (int32_t)(2.25 * 3600000); 188 int32_t endHour = (int32_t)(3.5 * 3600000); 189 190 zone->setStartRule(UCAL_MARCH, 1, 0, startHour, status); 191 zone->setEndRule (UCAL_JULY, 1, 0, endHour, status); 192 193 delete gc; 194 gc = new GregorianCalendar(*zone, status); 195 if (failure(status, "new GregorianCalendar")) return; 196 197 UDate marchOne = marchOneStd + startHour; 198 UDate julyOne = julyOneStd + endHour - 3600000; // Adjust from wall to Std time 199 200 UDate expMarchOne = 636251400000.0; 201 if (marchOne != expMarchOne) 202 { 203 errln((UnicodeString)"FAIL: Expected start computed as " + marchOne + 204 " = " + dateToString(marchOne)); 205 logln((UnicodeString)" Should be " + expMarchOne + 206 " = " + dateToString(expMarchOne)); 207 } 208 209 UDate expJulyOne = 646793100000.0; 210 if (julyOne != expJulyOne) 211 { 212 errln((UnicodeString)"FAIL: Expected start computed as " + julyOne + 213 " = " + dateToString(julyOne)); 214 logln((UnicodeString)" Should be " + expJulyOne + 215 " = " + dateToString(expJulyOne)); 216 } 217 218 testUsingBinarySearch(*zone, date(90, UCAL_JANUARY, 1), date(90, UCAL_JUNE, 15), marchOne); 219 testUsingBinarySearch(*zone, date(90, UCAL_JUNE, 1), date(90, UCAL_DECEMBER, 31), julyOne); 220 221 if (zone->inDaylightTime(marchOne - 1000, status) || 222 !zone->inDaylightTime(marchOne, status)) 223 errln("FAIL: Start rule broken"); 224 if (!zone->inDaylightTime(julyOne - 1000, status) || 225 zone->inDaylightTime(julyOne, status)) 226 errln("FAIL: End rule broken"); 227 228 zone->setStartYear(1991); 229 if (zone->inDaylightTime(marchOne, status) || 230 zone->inDaylightTime(julyOne - 1000, status)) 231 errln("FAIL: Start year broken"); 232 233 failure(status, "TestRuleAPI"); 234 delete gc; 235 delete zone; 236 } 237 238 void 239 TimeZoneTest::findTransition(const TimeZone& tz, 240 UDate min, UDate max) { 241 UErrorCode ec = U_ZERO_ERROR; 242 UnicodeString id,s; 243 UBool startsInDST = tz.inDaylightTime(min, ec); 244 if (failure(ec, "TimeZone::inDaylightTime")) return; 245 if (tz.inDaylightTime(max, ec) == startsInDST) { 246 logln("Error: " + tz.getID(id) + ".inDaylightTime(" + dateToString(min) + ") = " + (startsInDST?"TRUE":"FALSE") + 247 ", inDaylightTime(" + dateToString(max) + ") = " + (startsInDST?"TRUE":"FALSE")); 248 return; 249 } 250 if (failure(ec, "TimeZone::inDaylightTime")) return; 251 while ((max - min) > INTERVAL) { 252 UDate mid = (min + max) / 2; 253 if (tz.inDaylightTime(mid, ec) == startsInDST) { 254 min = mid; 255 } else { 256 max = mid; 257 } 258 if (failure(ec, "TimeZone::inDaylightTime")) return; 259 } 260 min = 1000.0 * uprv_floor(min/1000.0); 261 max = 1000.0 * uprv_floor(max/1000.0); 262 logln(tz.getID(id) + " Before: " + min/1000 + " = " + 263 dateToString(min,s,tz)); 264 logln(tz.getID(id) + " After: " + max/1000 + " = " + 265 dateToString(max,s,tz)); 266 } 267 268 void 269 TimeZoneTest::testUsingBinarySearch(const TimeZone& tz, 270 UDate min, UDate max, 271 UDate expectedBoundary) 272 { 273 UErrorCode status = U_ZERO_ERROR; 274 UBool startsInDST = tz.inDaylightTime(min, status); 275 if (failure(status, "TimeZone::inDaylightTime")) return; 276 if (tz.inDaylightTime(max, status) == startsInDST) { 277 logln("Error: inDaylightTime(" + dateToString(max) + ") != " + ((!startsInDST)?"TRUE":"FALSE")); 278 return; 279 } 280 if (failure(status, "TimeZone::inDaylightTime")) return; 281 while ((max - min) > INTERVAL) { 282 UDate mid = (min + max) / 2; 283 if (tz.inDaylightTime(mid, status) == startsInDST) { 284 min = mid; 285 } else { 286 max = mid; 287 } 288 if (failure(status, "TimeZone::inDaylightTime")) return; 289 } 290 logln(UnicodeString("Binary Search Before: ") + uprv_floor(0.5 + min) + " = " + dateToString(min)); 291 logln(UnicodeString("Binary Search After: ") + uprv_floor(0.5 + max) + " = " + dateToString(max)); 292 UDate mindelta = expectedBoundary - min; 293 UDate maxdelta = max - expectedBoundary; 294 if (mindelta >= 0 && 295 mindelta <= INTERVAL && 296 maxdelta >= 0 && 297 maxdelta <= INTERVAL) 298 logln(UnicodeString("PASS: Expected bdry: ") + expectedBoundary + " = " + dateToString(expectedBoundary)); 299 else 300 errln(UnicodeString("FAIL: Expected bdry: ") + expectedBoundary + " = " + dateToString(expectedBoundary)); 301 } 302 303 const UDate TimeZoneTest::INTERVAL = 100; 304 305 // --------------------------------------------------------------------------------- 306 307 // ------------------------------------- 308 309 /** 310 * Test the offset of the PRT timezone. 311 */ 312 void 313 TimeZoneTest::TestPRTOffset() 314 { 315 TimeZone* tz = TimeZone::createTimeZone("PRT"); 316 if (tz == 0) { 317 errln("FAIL: TimeZone(PRT) is null"); 318 } 319 else { 320 int32_t expectedHour = -4; 321 double expectedOffset = (((double)expectedHour) * millisPerHour); 322 double foundOffset = tz->getRawOffset(); 323 int32_t foundHour = (int32_t)foundOffset / millisPerHour; 324 if (expectedOffset != foundOffset) { 325 dataerrln("FAIL: Offset for PRT should be %d, found %d", expectedHour, foundHour); 326 } else { 327 logln("PASS: Offset for PRT should be %d, found %d", expectedHour, foundHour); 328 } 329 } 330 delete tz; 331 } 332 333 // ------------------------------------- 334 335 /** 336 * Regress a specific bug with a sequence of API calls. 337 */ 338 void 339 TimeZoneTest::TestVariousAPI518() 340 { 341 UErrorCode status = U_ZERO_ERROR; 342 TimeZone* time_zone = TimeZone::createTimeZone("PST"); 343 UDate d = date(97, UCAL_APRIL, 30); 344 UnicodeString str; 345 logln("The timezone is " + time_zone->getID(str)); 346 if (!time_zone->inDaylightTime(d, status)) dataerrln("FAIL: inDaylightTime returned FALSE"); 347 if (failure(status, "TimeZone::inDaylightTime", TRUE)) return; 348 if (!time_zone->useDaylightTime()) dataerrln("FAIL: useDaylightTime returned FALSE"); 349 if (time_zone->getRawOffset() != - 8 * millisPerHour) dataerrln("FAIL: getRawOffset returned wrong value"); 350 GregorianCalendar *gc = new GregorianCalendar(status); 351 if (U_FAILURE(status)) { errln("FAIL: Couldn't create GregorianCalendar"); return; } 352 gc->setTime(d, status); 353 if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::setTime failed"); return; } 354 if (time_zone->getOffset(gc->AD, gc->get(UCAL_YEAR, status), gc->get(UCAL_MONTH, status), 355 gc->get(UCAL_DATE, status), (uint8_t)gc->get(UCAL_DAY_OF_WEEK, status), 0, status) != - 7 * millisPerHour) 356 dataerrln("FAIL: getOffset returned wrong value"); 357 if (U_FAILURE(status)) { errln("FAIL: GregorianCalendar::set failed"); return; } 358 delete gc; 359 delete time_zone; 360 } 361 362 // ------------------------------------- 363 364 /** 365 * Test the call which retrieves the available IDs. 366 */ 367 void 368 TimeZoneTest::TestGetAvailableIDs913() 369 { 370 UErrorCode ec = U_ZERO_ERROR; 371 int32_t i; 372 373 #ifdef U_USE_TIMEZONE_OBSOLETE_2_8 374 // Test legacy API -- remove these tests when the corresponding API goes away (duh) 375 int32_t numIDs = -1; 376 const UnicodeString** ids = TimeZone::createAvailableIDs(numIDs); 377 if (ids == 0 || numIDs < 1) { 378 errln("FAIL: createAvailableIDs()"); 379 } else { 380 UnicodeString buf("TimeZone::createAvailableIDs() = { "); 381 for(i=0; i<numIDs; ++i) { 382 if (i) buf.append(", "); 383 buf.append(*ids[i]); 384 } 385 buf.append(" } "); 386 logln(buf + numIDs); 387 // we own the array; the caller owns the contained strings (yuck) 388 uprv_free(ids); 389 } 390 391 numIDs = -1; 392 ids = TimeZone::createAvailableIDs(-8*U_MILLIS_PER_HOUR, numIDs); 393 if (ids == 0 || numIDs < 1) { 394 errln("FAIL: createAvailableIDs(-8:00)"); 395 } else { 396 UnicodeString buf("TimeZone::createAvailableIDs(-8:00) = { "); 397 for(i=0; i<numIDs; ++i) { 398 if (i) buf.append(", "); 399 buf.append(*ids[i]); 400 } 401 buf.append(" } "); 402 logln(buf + numIDs); 403 // we own the array; the caller owns the contained strings (yuck) 404 uprv_free(ids); 405 } 406 numIDs = -1; 407 ids = TimeZone::createAvailableIDs("US", numIDs); 408 if (ids == 0 || numIDs < 1) { 409 errln("FAIL: createAvailableIDs(US) ids=%d, numIDs=%d", ids, numIDs); 410 } else { 411 UnicodeString buf("TimeZone::createAvailableIDs(US) = { "); 412 for(i=0; i<numIDs; ++i) { 413 if (i) buf.append(", "); 414 buf.append(*ids[i]); 415 } 416 buf.append(" } "); 417 logln(buf + numIDs); 418 // we own the array; the caller owns the contained strings (yuck) 419 uprv_free(ids); 420 } 421 #endif 422 423 UnicodeString str; 424 UnicodeString *buf = new UnicodeString("TimeZone::createEnumeration() = { "); 425 int32_t s_length; 426 StringEnumeration* s = TimeZone::createEnumeration(); 427 if (s == NULL) { 428 dataerrln("Unable to create TimeZone enumeration"); 429 return; 430 } 431 s_length = s->count(ec); 432 for (i = 0; i < s_length;++i) { 433 if (i > 0) *buf += ", "; 434 if ((i & 1) == 0) { 435 *buf += *s->snext(ec); 436 } else { 437 *buf += UnicodeString(s->next(NULL, ec), ""); 438 } 439 440 if((i % 5) == 4) { 441 // replace s with a clone of itself 442 StringEnumeration *s2 = s->clone(); 443 if(s2 == NULL || s_length != s2->count(ec)) { 444 errln("TimezoneEnumeration.clone() failed"); 445 } else { 446 delete s; 447 s = s2; 448 } 449 } 450 } 451 *buf += " };"; 452 logln(*buf); 453 454 /* Confirm that the following zones can be retrieved: The first 455 * zone, the last zone, and one in-between. This tests the binary 456 * search through the system zone data. 457 */ 458 s->reset(ec); 459 int32_t middle = s_length/2; 460 for (i=0; i<s_length; ++i) { 461 const UnicodeString* id = s->snext(ec); 462 if (i==0 || i==middle || i==(s_length-1)) { 463 TimeZone *z = TimeZone::createTimeZone(*id); 464 if (z == 0) { 465 errln(UnicodeString("FAIL: createTimeZone(") + 466 *id + ") -> 0"); 467 } else if (z->getID(str) != *id) { 468 errln(UnicodeString("FAIL: createTimeZone(") + 469 *id + ") -> zone " + str); 470 } else { 471 logln(UnicodeString("OK: createTimeZone(") + 472 *id + ") succeeded"); 473 } 474 delete z; 475 } 476 } 477 delete s; 478 479 buf->truncate(0); 480 *buf += "TimeZone::createEnumeration(GMT+01:00) = { "; 481 482 s = TimeZone::createEnumeration(1 * U_MILLIS_PER_HOUR); 483 s_length = s->count(ec); 484 for (i = 0; i < s_length;++i) { 485 if (i > 0) *buf += ", "; 486 *buf += *s->snext(ec); 487 } 488 delete s; 489 *buf += " };"; 490 logln(*buf); 491 492 493 buf->truncate(0); 494 *buf += "TimeZone::createEnumeration(US) = { "; 495 496 s = TimeZone::createEnumeration("US"); 497 s_length = s->count(ec); 498 for (i = 0; i < s_length;++i) { 499 if (i > 0) *buf += ", "; 500 *buf += *s->snext(ec); 501 } 502 *buf += " };"; 503 logln(*buf); 504 505 TimeZone *tz = TimeZone::createTimeZone("PST"); 506 if (tz != 0) logln("getTimeZone(PST) = " + tz->getID(str)); 507 else errln("FAIL: getTimeZone(PST) = null"); 508 delete tz; 509 tz = TimeZone::createTimeZone("America/Los_Angeles"); 510 if (tz != 0) logln("getTimeZone(America/Los_Angeles) = " + tz->getID(str)); 511 else errln("FAIL: getTimeZone(PST) = null"); 512 delete tz; 513 514 // @bug 4096694 515 tz = TimeZone::createTimeZone("NON_EXISTENT"); 516 UnicodeString temp; 517 if (tz == 0) 518 errln("FAIL: getTimeZone(NON_EXISTENT) = null"); 519 else if (tz->getID(temp) != UCAL_UNKNOWN_ZONE_ID) 520 errln("FAIL: getTimeZone(NON_EXISTENT) = " + temp); 521 delete tz; 522 523 delete buf; 524 delete s; 525 } 526 527 void 528 TimeZoneTest::TestGetAvailableIDsNew() 529 { 530 UErrorCode ec = U_ZERO_ERROR; 531 StringEnumeration *any, *canonical, *canonicalLoc; 532 StringEnumeration *any_US, *canonical_US, *canonicalLoc_US; 533 StringEnumeration *any_W5, *any_CA_W5; 534 StringEnumeration *any_US_E14; 535 int32_t rawOffset; 536 const UnicodeString *id1, *id2; 537 UnicodeString canonicalID; 538 UBool isSystemID; 539 char region[4]; 540 int32_t zoneCount; 541 542 any = canonical = canonicalLoc = any_US = canonical_US = canonicalLoc_US = any_W5 = any_CA_W5 = any_US_E14 = NULL; 543 544 any = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, NULL, ec); 545 if (U_FAILURE(ec)) { 546 dataerrln("Failed to create enumration for ANY"); 547 goto cleanup; 548 } 549 550 canonical = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, ec); 551 if (U_FAILURE(ec)) { 552 errln("Failed to create enumration for CANONICAL"); 553 goto cleanup; 554 } 555 556 canonicalLoc = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, NULL, ec); 557 if (U_FAILURE(ec)) { 558 errln("Failed to create enumration for CANONICALLOC"); 559 goto cleanup; 560 } 561 562 any_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", NULL, ec); 563 if (U_FAILURE(ec)) { 564 errln("Failed to create enumration for ANY_US"); 565 goto cleanup; 566 } 567 568 canonical_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, "US", NULL, ec); 569 if (U_FAILURE(ec)) { 570 errln("Failed to create enumration for CANONICAL_US"); 571 goto cleanup; 572 } 573 574 canonicalLoc_US = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, "US", NULL, ec); 575 if (U_FAILURE(ec)) { 576 errln("Failed to create enumration for CANONICALLOC_US"); 577 goto cleanup; 578 } 579 580 rawOffset = (-5)*60*60*1000; 581 any_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, NULL, &rawOffset, ec); 582 if (U_FAILURE(ec)) { 583 errln("Failed to create enumration for ANY_W5"); 584 goto cleanup; 585 } 586 587 any_CA_W5 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "CA", &rawOffset, ec); 588 if (U_FAILURE(ec)) { 589 errln("Failed to create enumration for ANY_CA_W5"); 590 goto cleanup; 591 } 592 593 rawOffset = 14*60*60*1000; 594 any_US_E14 = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_ANY, "US", &rawOffset, ec); 595 if (U_FAILURE(ec)) { 596 errln("Failed to create enumration for ANY_US_E14"); 597 goto cleanup; 598 } 599 600 checkContainsAll(any, "ANY", canonical, "CANONICAL"); 601 checkContainsAll(canonical, "CANONICAL", canonicalLoc, "CANONICALLOC"); 602 603 checkContainsAll(any, "ANY", any_US, "ANY_US"); 604 checkContainsAll(canonical, "CANONICAL", canonical_US, "CANONICAL_US"); 605 checkContainsAll(canonicalLoc, "CANONICALLOC", canonicalLoc_US, "CANONICALLOC_US"); 606 607 checkContainsAll(any_US, "ANY_US", canonical_US, "CANONICAL_US"); 608 checkContainsAll(canonical_US, "CANONICAL_US", canonicalLoc_US, "CANONICALLOC_US"); 609 610 checkContainsAll(any, "ANY", any_W5, "ANY_W5"); 611 checkContainsAll(any_W5, "ANY_W5", any_CA_W5, "ANY_CA_W5"); 612 613 // And ID in any set, but not in canonical set must not be a canonical ID 614 any->reset(ec); 615 while ((id1 = any->snext(ec)) != NULL) { 616 UBool found = FALSE; 617 canonical->reset(ec); 618 while ((id2 = canonical->snext(ec)) != NULL) { 619 if (*id1 == *id2) { 620 found = TRUE; 621 break; 622 } 623 } 624 if (U_FAILURE(ec)) { 625 break; 626 } 627 if (!found) { 628 TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec); 629 if (U_FAILURE(ec)) { 630 break; 631 } 632 if (*id1 == canonicalID) { 633 errln((UnicodeString)"FAIL: canonicalID [" + *id1 + "] is not in CANONICAL"); 634 } 635 if (!isSystemID) { 636 errln((UnicodeString)"FAIL: ANY contains non-system ID: " + *id1); 637 } 638 } 639 } 640 if (U_FAILURE(ec)) { 641 errln("Error checking IDs in ANY, but not in CANONICAL"); 642 ec = U_ZERO_ERROR; 643 } 644 645 // canonical set must contains only canonical IDs 646 canonical->reset(ec); 647 while ((id1 = canonical->snext(ec)) != NULL) { 648 TimeZone::getCanonicalID(*id1, canonicalID, isSystemID, ec); 649 if (U_FAILURE(ec)) { 650 break; 651 } 652 if (*id1 != canonicalID) { 653 errln((UnicodeString)"FAIL: CANONICAL contains non-canonical ID: " + *id1); 654 } 655 if (!isSystemID) { 656 errln((UnicodeString)"FAILE: CANONICAL contains non-system ID: " + *id1); 657 } 658 } 659 if (U_FAILURE(ec)) { 660 errln("Error checking IDs in CANONICAL"); 661 ec = U_ZERO_ERROR; 662 } 663 664 // canonicalLoc set must contain only canonical location IDs 665 canonicalLoc->reset(ec); 666 while ((id1 = canonicalLoc->snext(ec)) != NULL) { 667 TimeZone::getRegion(*id1, region, sizeof(region), ec); 668 if (U_FAILURE(ec)) { 669 break; 670 } 671 if (uprv_strcmp(region, "001") == 0) { 672 errln((UnicodeString)"FAIL: CANONICALLOC contains non location zone: " + *id1); 673 } 674 } 675 if (U_FAILURE(ec)) { 676 errln("Error checking IDs in CANONICALLOC"); 677 ec = U_ZERO_ERROR; 678 } 679 680 // any_US must contain only US zones 681 any_US->reset(ec); 682 while ((id1 = any_US->snext(ec)) != NULL) { 683 TimeZone::getRegion(*id1, region, sizeof(region), ec); 684 if (U_FAILURE(ec)) { 685 break; 686 } 687 if (uprv_strcmp(region, "US") != 0) { 688 errln((UnicodeString)"FAIL: ANY_US contains non-US zone ID: " + *id1); 689 } 690 } 691 if (U_FAILURE(ec)) { 692 errln("Error checking IDs in ANY_US"); 693 ec = U_ZERO_ERROR; 694 } 695 696 // any_W5 must contain only GMT-05:00 zones 697 any_W5->reset(ec); 698 while ((id1 = any_W5->snext(ec)) != NULL) { 699 TimeZone *tz = TimeZone::createTimeZone(*id1); 700 if (tz->getRawOffset() != (-5)*60*60*1000) { 701 errln((UnicodeString)"FAIL: ANY_W5 contains a zone whose offset is not -05:00: " + *id1); 702 } 703 delete tz; 704 } 705 if (U_FAILURE(ec)) { 706 errln("Error checking IDs in ANY_W5"); 707 ec = U_ZERO_ERROR; 708 } 709 710 // No US zone swith GMT+14:00 711 zoneCount = any_US_E14->count(ec); 712 if (U_FAILURE(ec)) { 713 errln("Error checking IDs in ANY_US_E14"); 714 ec = U_ZERO_ERROR; 715 } else if (zoneCount != 0) { 716 errln("FAIL: ANY_US_E14 must be empty"); 717 } 718 719 cleanup: 720 delete any; 721 delete canonical; 722 delete canonicalLoc; 723 delete any_US; 724 delete canonical_US; 725 delete canonicalLoc_US; 726 delete any_W5; 727 delete any_CA_W5; 728 delete any_US_E14; 729 } 730 731 void 732 TimeZoneTest::checkContainsAll(StringEnumeration *s1, const char *name1, 733 StringEnumeration *s2, const char *name2) 734 { 735 UErrorCode ec = U_ZERO_ERROR; 736 const UnicodeString *id1, *id2; 737 738 s2->reset(ec); 739 740 while ((id2 = s2->snext(ec)) != NULL) { 741 UBool found = FALSE; 742 s1->reset(ec); 743 while ((id1 = s1->snext(ec)) != NULL) { 744 if (*id1 == *id2) { 745 found = TRUE; 746 break; 747 } 748 } 749 if (!found) { 750 errln((UnicodeString)"FAIL: " + name1 + "does not contain " 751 + *id2 + " in " + name2); 752 } 753 } 754 755 if (U_FAILURE(ec)) { 756 errln((UnicodeString)"Error checkContainsAll for " + name1 + " - " + name2); 757 } 758 } 759 760 /** 761 * NOTE: As of ICU 2.8, this test confirms that the "tz.alias" 762 * file, used to build ICU alias zones, is working. It also 763 * looks at some genuine Olson compatibility IDs. [aliu] 764 * 765 * This test is problematic. It should really just confirm that 766 * the list of compatibility zone IDs exist and are somewhat 767 * meaningful (that is, they aren't all aliases of GMT). It goes a 768 * bit further -- it hard-codes expectations about zone behavior, 769 * when in fact zones are redefined quite frequently. ICU's build 770 * process means that it is easy to update ICU to contain the 771 * latest Olson zone data, but if a zone tested here changes, then 772 * this test will fail. I have updated the test for 1999j data, 773 * but further updates will probably be required. Note that some 774 * of the concerts listed below no longer apply -- in particular, 775 * we do NOT overwrite real UNIX zones with 3-letter IDs. There 776 * are two points of overlap as of 1999j: MET and EET. These are 777 * both real UNIX zones, so we just use the official 778 * definition. This test has been updated to reflect this. 779 * 12/3/99 aliu 780 * 781 * Added tests for additional zones and aliases from the icuzones file. 782 * Markus Scherer 2006-nov-06 783 * 784 * [srl - from java - 7/5/1998] 785 * @bug 4130885 786 * Certain short zone IDs, used since 1.1.x, are incorrect. 787 * 788 * The worst of these is: 789 * 790 * "CAT" (Central African Time) should be GMT+2:00, but instead returns a 791 * zone at GMT-1:00. The zone at GMT-1:00 should be called EGT, CVT, EGST, 792 * or AZOST, depending on which zone is meant, but in no case is it CAT. 793 * 794 * Other wrong zone IDs: 795 * 796 * ECT (European Central Time) GMT+1:00: ECT is Ecuador Time, 797 * GMT-5:00. European Central time is abbreviated CEST. 798 * 799 * SST (Solomon Island Time) GMT+11:00. SST is actually Samoa Standard Time, 800 * GMT-11:00. Solomon Island time is SBT. 801 * 802 * NST (New Zealand Time) GMT+12:00. NST is the abbreviation for 803 * Newfoundland Standard Time, GMT-3:30. New Zealanders use NZST. 804 * 805 * AST (Alaska Standard Time) GMT-9:00. [This has already been noted in 806 * another bug.] It should be "AKST". AST is Atlantic Standard Time, 807 * GMT-4:00. 808 * 809 * PNT (Phoenix Time) GMT-7:00. PNT usually means Pitcairn Time, 810 * GMT-8:30. There is no standard abbreviation for Phoenix time, as distinct 811 * from MST with daylight savings. 812 * 813 * In addition to these problems, a number of zones are FAKE. That is, they 814 * don't match what people use in the real world. 815 * 816 * FAKE zones: 817 * 818 * EET (should be EEST) 819 * ART (should be EEST) 820 * MET (should be IRST) 821 * NET (should be AMST) 822 * PLT (should be PKT) 823 * BST (should be BDT) 824 * VST (should be ICT) 825 * CTT (should be CST) + 826 * ACT (should be CST) + 827 * AET (should be EST) + 828 * MIT (should be WST) + 829 * IET (should be EST) + 830 * PRT (should be AST) + 831 * CNT (should be NST) 832 * AGT (should be ARST) 833 * BET (should be EST) + 834 * 835 * + A zone with the correct name already exists and means something 836 * else. E.g., EST usually indicates the US Eastern zone, so it cannot be 837 * used for Brazil (BET). 838 */ 839 void TimeZoneTest::TestShortZoneIDs() 840 { 841 int32_t i; 842 // Create a small struct to hold the array 843 struct 844 { 845 const char *id; 846 int32_t offset; 847 UBool daylight; 848 } 849 kReferenceList [] = 850 { 851 {"HST", -600, FALSE}, // Olson northamerica -10:00 852 {"AST", -540, TRUE}, // ICU Link - America/Anchorage 853 {"PST", -480, TRUE}, // ICU Link - America/Los_Angeles 854 {"PNT", -420, FALSE}, // ICU Link - America/Phoenix 855 {"MST", -420, FALSE}, // updated Aug 2003 aliu 856 {"CST", -360, TRUE}, // Olson northamerica -7:00 857 {"IET", -300, TRUE}, // ICU Link - America/Indiana/Indianapolis 858 {"EST", -300, FALSE}, // Olson northamerica -5:00 859 {"PRT", -240, FALSE}, // ICU Link - America/Puerto_Rico 860 {"CNT", -210, TRUE}, // ICU Link - America/St_Johns 861 {"AGT", -180, FALSE}, // ICU Link - America/Argentina/Buenos_Aires 862 {"BET", -180, TRUE}, // ICU Link - America/Sao_Paulo 863 {"GMT", 0, FALSE}, // Olson etcetera Link - Etc/GMT 864 {"UTC", 0, FALSE}, // Olson etcetera 0 865 {"ECT", 60, TRUE}, // ICU Link - Europe/Paris 866 {"MET", 60, TRUE}, // Olson europe 1:00 C-Eur 867 {"CAT", 120, FALSE}, // ICU Link - Africa/Harare 868 {"ART", 120, FALSE}, // ICU Link - Africa/Cairo 869 {"EET", 120, TRUE}, // Olson europe 2:00 EU 870 {"EAT", 180, FALSE}, // ICU Link - Africa/Addis_Ababa 871 {"NET", 240, FALSE}, // ICU Link - Asia/Yerevan 872 {"PLT", 300, FALSE}, // ICU Link - Asia/Karachi 873 {"IST", 330, FALSE}, // ICU Link - Asia/Kolkata 874 {"BST", 360, FALSE}, // ICU Link - Asia/Dhaka 875 {"VST", 420, FALSE}, // ICU Link - Asia/Ho_Chi_Minh 876 {"CTT", 480, FALSE}, // ICU Link - Asia/Shanghai 877 {"JST", 540, FALSE}, // ICU Link - Asia/Tokyo 878 {"ACT", 570, FALSE}, // ICU Link - Australia/Darwin 879 {"AET", 600, TRUE}, // ICU Link - Australia/Sydney 880 {"SST", 660, FALSE}, // ICU Link - Pacific/Guadalcanal 881 {"NST", 720, TRUE}, // ICU Link - Pacific/Auckland 882 {"MIT", 780, TRUE}, // ICU Link - Pacific/Apia 883 884 {"Etc/Unknown", 0, FALSE}, // CLDR 885 886 {"SystemV/AST4ADT", -240, TRUE}, 887 {"SystemV/EST5EDT", -300, TRUE}, 888 {"SystemV/CST6CDT", -360, TRUE}, 889 {"SystemV/MST7MDT", -420, TRUE}, 890 {"SystemV/PST8PDT", -480, TRUE}, 891 {"SystemV/YST9YDT", -540, TRUE}, 892 {"SystemV/AST4", -240, FALSE}, 893 {"SystemV/EST5", -300, FALSE}, 894 {"SystemV/CST6", -360, FALSE}, 895 {"SystemV/MST7", -420, FALSE}, 896 {"SystemV/PST8", -480, FALSE}, 897 {"SystemV/YST9", -540, FALSE}, 898 {"SystemV/HST10", -600, FALSE}, 899 900 {"",0,FALSE} 901 }; 902 903 for(i=0;kReferenceList[i].id[0];i++) { 904 UnicodeString itsID(kReferenceList[i].id); 905 UBool ok = TRUE; 906 // Check existence. 907 TimeZone *tz = TimeZone::createTimeZone(itsID); 908 if (!tz || (kReferenceList[i].offset != 0 && *tz == *TimeZone::getGMT())) { 909 errln("FAIL: Time Zone " + itsID + " does not exist!"); 910 continue; 911 } 912 913 // Check daylight usage. 914 UBool usesDaylight = tz->useDaylightTime(); 915 if (usesDaylight != kReferenceList[i].daylight) { 916 if (!isDevelopmentBuild) { 917 logln("Warning: Time Zone " + itsID + " use daylight is " + 918 (usesDaylight?"TRUE":"FALSE") + 919 " but it should be " + 920 ((kReferenceList[i].daylight)?"TRUE":"FALSE")); 921 } else { 922 dataerrln("FAIL: Time Zone " + itsID + " use daylight is " + 923 (usesDaylight?"TRUE":"FALSE") + 924 " but it should be " + 925 ((kReferenceList[i].daylight)?"TRUE":"FALSE")); 926 } 927 ok = FALSE; 928 } 929 930 // Check offset 931 int32_t offsetInMinutes = tz->getRawOffset()/60000; 932 if (offsetInMinutes != kReferenceList[i].offset) { 933 if (!isDevelopmentBuild) { 934 logln("FAIL: Time Zone " + itsID + " raw offset is " + 935 offsetInMinutes + 936 " but it should be " + kReferenceList[i].offset); 937 } else { 938 dataerrln("FAIL: Time Zone " + itsID + " raw offset is " + 939 offsetInMinutes + 940 " but it should be " + kReferenceList[i].offset); 941 } 942 ok = FALSE; 943 } 944 945 if (ok) { 946 logln("OK: " + itsID + 947 " useDaylightTime() & getRawOffset() as expected"); 948 } 949 delete tz; 950 } 951 952 953 // OK now test compat 954 logln("Testing for compatibility zones"); 955 956 const char* compatibilityMap[] = { 957 // This list is copied from tz.alias. If tz.alias 958 // changes, this list must be updated. Current as of Mar 2007 959 "ACT", "Australia/Darwin", 960 "AET", "Australia/Sydney", 961 "AGT", "America/Buenos_Aires", 962 "ART", "Africa/Cairo", 963 "AST", "America/Anchorage", 964 "BET", "America/Sao_Paulo", 965 "BST", "Asia/Dhaka", // # spelling changed in 2000h; was Asia/Dacca 966 "CAT", "Africa/Harare", 967 "CNT", "America/St_Johns", 968 "CST", "America/Chicago", 969 "CTT", "Asia/Shanghai", 970 "EAT", "Africa/Addis_Ababa", 971 "ECT", "Europe/Paris", 972 // EET Europe/Istanbul # EET is a standard UNIX zone 973 // "EST", "America/New_York", # Defined as -05:00 974 // "HST", "Pacific/Honolulu", # Defined as -10:00 975 "IET", "America/Indianapolis", 976 "IST", "Asia/Calcutta", 977 "JST", "Asia/Tokyo", 978 // MET Asia/Tehran # MET is a standard UNIX zone 979 "MIT", "Pacific/Apia", 980 // "MST", "America/Denver", # Defined as -07:00 981 "NET", "Asia/Yerevan", 982 "NST", "Pacific/Auckland", 983 "PLT", "Asia/Karachi", 984 "PNT", "America/Phoenix", 985 "PRT", "America/Puerto_Rico", 986 "PST", "America/Los_Angeles", 987 "SST", "Pacific/Guadalcanal", 988 "UTC", "Etc/GMT", 989 "VST", "Asia/Saigon", 990 "","","" 991 }; 992 993 for (i=0;*compatibilityMap[i];i+=2) { 994 UnicodeString itsID; 995 996 const char *zone1 = compatibilityMap[i]; 997 const char *zone2 = compatibilityMap[i+1]; 998 999 TimeZone *tz1 = TimeZone::createTimeZone(zone1); 1000 TimeZone *tz2 = TimeZone::createTimeZone(zone2); 1001 1002 if (!tz1) { 1003 errln(UnicodeString("FAIL: Could not find short ID zone ") + zone1); 1004 } 1005 if (!tz2) { 1006 errln(UnicodeString("FAIL: Could not find long ID zone ") + zone2); 1007 } 1008 1009 if (tz1 && tz2) { 1010 // make NAME same so comparison will only look at the rest 1011 tz2->setID(tz1->getID(itsID)); 1012 1013 if (*tz1 != *tz2) { 1014 errln("FAIL: " + UnicodeString(zone1) + 1015 " != " + UnicodeString(zone2)); 1016 } else { 1017 logln("OK: " + UnicodeString(zone1) + 1018 " == " + UnicodeString(zone2)); 1019 } 1020 } 1021 1022 delete tz1; 1023 delete tz2; 1024 } 1025 } 1026 1027 1028 /** 1029 * Utility function for TestCustomParse 1030 */ 1031 UnicodeString& TimeZoneTest::formatOffset(int32_t offset, UnicodeString &rv) { 1032 rv.remove(); 1033 UChar sign = 0x002B; 1034 if (offset < 0) { 1035 sign = 0x002D; 1036 offset = -offset; 1037 } 1038 1039 int32_t s = offset % 60; 1040 offset /= 60; 1041 int32_t m = offset % 60; 1042 int32_t h = offset / 60; 1043 1044 rv += (UChar)(sign); 1045 if (h >= 10) { 1046 rv += (UChar)(0x0030 + (h/10)); 1047 } else { 1048 rv += (UChar)0x0030; 1049 } 1050 rv += (UChar)(0x0030 + (h%10)); 1051 1052 rv += (UChar)0x003A; /* ':' */ 1053 if (m >= 10) { 1054 rv += (UChar)(0x0030 + (m/10)); 1055 } else { 1056 rv += (UChar)0x0030; 1057 } 1058 rv += (UChar)(0x0030 + (m%10)); 1059 1060 if (s) { 1061 rv += (UChar)0x003A; /* ':' */ 1062 if (s >= 10) { 1063 rv += (UChar)(0x0030 + (s/10)); 1064 } else { 1065 rv += (UChar)0x0030; 1066 } 1067 rv += (UChar)(0x0030 + (s%10)); 1068 } 1069 return rv; 1070 } 1071 1072 /** 1073 * Utility function for TestCustomParse, generating time zone ID 1074 * string for the give offset. 1075 */ 1076 UnicodeString& TimeZoneTest::formatTZID(int32_t offset, UnicodeString &rv) { 1077 rv.remove(); 1078 UChar sign = 0x002B; 1079 if (offset < 0) { 1080 sign = 0x002D; 1081 offset = -offset; 1082 } 1083 1084 int32_t s = offset % 60; 1085 offset /= 60; 1086 int32_t m = offset % 60; 1087 int32_t h = offset / 60; 1088 1089 rv += "GMT"; 1090 rv += (UChar)(sign); 1091 if (h >= 10) { 1092 rv += (UChar)(0x0030 + (h/10)); 1093 } else { 1094 rv += (UChar)0x0030; 1095 } 1096 rv += (UChar)(0x0030 + (h%10)); 1097 rv += (UChar)0x003A; 1098 if (m >= 10) { 1099 rv += (UChar)(0x0030 + (m/10)); 1100 } else { 1101 rv += (UChar)0x0030; 1102 } 1103 rv += (UChar)(0x0030 + (m%10)); 1104 1105 if (s) { 1106 rv += (UChar)0x003A; 1107 if (s >= 10) { 1108 rv += (UChar)(0x0030 + (s/10)); 1109 } else { 1110 rv += (UChar)0x0030; 1111 } 1112 rv += (UChar)(0x0030 + (s%10)); 1113 } 1114 return rv; 1115 } 1116 1117 /** 1118 * As part of the VM fix (see CCC approved RFE 4028006, bug 1119 * 4044013), TimeZone.getTimeZone() has been modified to recognize 1120 * generic IDs of the form GMT[+-]hh:mm, GMT[+-]hhmm, and 1121 * GMT[+-]hh. Test this behavior here. 1122 * 1123 * @bug 4044013 1124 */ 1125 void TimeZoneTest::TestCustomParse() 1126 { 1127 int32_t i; 1128 const int32_t kUnparseable = 604800; // the number of seconds in a week. More than any offset should be. 1129 1130 struct 1131 { 1132 const char *customId; 1133 int32_t expectedOffset; 1134 } 1135 kData[] = 1136 { 1137 // ID Expected offset in seconds 1138 {"GMT", kUnparseable}, //Isn't custom. [returns normal GMT] 1139 {"GMT-YOUR.AD.HERE", kUnparseable}, 1140 {"GMT0", kUnparseable}, 1141 {"GMT+0", (0)}, 1142 {"GMT+1", (1*60*60)}, 1143 {"GMT-0030", (-30*60)}, 1144 {"GMT+15:99", kUnparseable}, 1145 {"GMT+", kUnparseable}, 1146 {"GMT-", kUnparseable}, 1147 {"GMT+0:", kUnparseable}, 1148 {"GMT-:", kUnparseable}, 1149 {"GMT-YOUR.AD.HERE", kUnparseable}, 1150 {"GMT+0010", (10*60)}, // Interpret this as 00:10 1151 {"GMT-10", (-10*60*60)}, 1152 {"GMT+30", kUnparseable}, 1153 {"GMT-3:30", (-(3*60+30)*60)}, 1154 {"GMT-230", (-(2*60+30)*60)}, 1155 {"GMT+05:13:05", ((5*60+13)*60+5)}, 1156 {"GMT-71023", (-((7*60+10)*60+23))}, 1157 {"GMT+01:23:45:67", kUnparseable}, 1158 {"GMT+01:234", kUnparseable}, 1159 {"GMT-2:31:123", kUnparseable}, 1160 {"GMT+3:75", kUnparseable}, 1161 {"GMT-01010101", kUnparseable}, 1162 {0, 0} 1163 }; 1164 1165 for (i=0; kData[i].customId != 0; i++) { 1166 UnicodeString id(kData[i].customId); 1167 int32_t exp = kData[i].expectedOffset; 1168 TimeZone *zone = TimeZone::createTimeZone(id); 1169 UnicodeString itsID, temp; 1170 1171 if (dynamic_cast<OlsonTimeZone *>(zone) != NULL) { 1172 logln(id + " -> Olson time zone"); 1173 } else { 1174 zone->getID(itsID); 1175 int32_t ioffset = zone->getRawOffset()/1000; 1176 UnicodeString offset, expectedID; 1177 formatOffset(ioffset, offset); 1178 formatTZID(ioffset, expectedID); 1179 logln(id + " -> " + itsID + " " + offset); 1180 if (exp == kUnparseable && itsID != UCAL_UNKNOWN_ZONE_ID) { 1181 errln("Expected parse failure for " + id + 1182 ", got offset of " + offset + 1183 ", id " + itsID); 1184 } 1185 // JDK 1.3 creates custom zones with the ID "Custom" 1186 // JDK 1.4 creates custom zones with IDs of the form "GMT+02:00" 1187 // ICU creates custom zones with IDs of the form "GMT+02:00" 1188 else if (exp != kUnparseable && (ioffset != exp || itsID != expectedID)) { 1189 dataerrln("Expected offset of " + formatOffset(exp, temp) + 1190 ", id " + expectedID + 1191 ", for " + id + 1192 ", got offset of " + offset + 1193 ", id " + itsID); 1194 } 1195 } 1196 delete zone; 1197 } 1198 } 1199 1200 void 1201 TimeZoneTest::TestAliasedNames() 1202 { 1203 struct { 1204 const char *from; 1205 const char *to; 1206 } kData[] = { 1207 /* Generated by org.unicode.cldr.tool.CountItems */ 1208 1209 /* zoneID, canonical zoneID */ 1210 {"Africa/Timbuktu", "Africa/Bamako"}, 1211 {"America/Argentina/Buenos_Aires", "America/Buenos_Aires"}, 1212 {"America/Argentina/Catamarca", "America/Catamarca"}, 1213 {"America/Argentina/ComodRivadavia", "America/Catamarca"}, 1214 {"America/Argentina/Cordoba", "America/Cordoba"}, 1215 {"America/Argentina/Jujuy", "America/Jujuy"}, 1216 {"America/Argentina/Mendoza", "America/Mendoza"}, 1217 {"America/Atka", "America/Adak"}, 1218 {"America/Ensenada", "America/Tijuana"}, 1219 {"America/Fort_Wayne", "America/Indiana/Indianapolis"}, 1220 {"America/Indianapolis", "America/Indiana/Indianapolis"}, 1221 {"America/Knox_IN", "America/Indiana/Knox"}, 1222 {"America/Louisville", "America/Kentucky/Louisville"}, 1223 {"America/Porto_Acre", "America/Rio_Branco"}, 1224 {"America/Rosario", "America/Cordoba"}, 1225 {"America/Virgin", "America/St_Thomas"}, 1226 {"Asia/Ashkhabad", "Asia/Ashgabat"}, 1227 {"Asia/Chungking", "Asia/Chongqing"}, 1228 {"Asia/Dacca", "Asia/Dhaka"}, 1229 {"Asia/Istanbul", "Europe/Istanbul"}, 1230 {"Asia/Macao", "Asia/Macau"}, 1231 {"Asia/Tel_Aviv", "Asia/Jerusalem"}, 1232 {"Asia/Thimbu", "Asia/Thimphu"}, 1233 {"Asia/Ujung_Pandang", "Asia/Makassar"}, 1234 {"Asia/Ulan_Bator", "Asia/Ulaanbaatar"}, 1235 {"Australia/ACT", "Australia/Sydney"}, 1236 {"Australia/Canberra", "Australia/Sydney"}, 1237 {"Australia/LHI", "Australia/Lord_Howe"}, 1238 {"Australia/NSW", "Australia/Sydney"}, 1239 {"Australia/North", "Australia/Darwin"}, 1240 {"Australia/Queensland", "Australia/Brisbane"}, 1241 {"Australia/South", "Australia/Adelaide"}, 1242 {"Australia/Tasmania", "Australia/Hobart"}, 1243 {"Australia/Victoria", "Australia/Melbourne"}, 1244 {"Australia/West", "Australia/Perth"}, 1245 {"Australia/Yancowinna", "Australia/Broken_Hill"}, 1246 {"Brazil/Acre", "America/Rio_Branco"}, 1247 {"Brazil/DeNoronha", "America/Noronha"}, 1248 {"Brazil/East", "America/Sao_Paulo"}, 1249 {"Brazil/West", "America/Manaus"}, 1250 {"Canada/Atlantic", "America/Halifax"}, 1251 {"Canada/Central", "America/Winnipeg"}, 1252 {"Canada/East-Saskatchewan", "America/Regina"}, 1253 {"Canada/Eastern", "America/Toronto"}, 1254 {"Canada/Mountain", "America/Edmonton"}, 1255 {"Canada/Newfoundland", "America/St_Johns"}, 1256 {"Canada/Pacific", "America/Vancouver"}, 1257 {"Canada/Saskatchewan", "America/Regina"}, 1258 {"Canada/Yukon", "America/Whitehorse"}, 1259 {"Chile/Continental", "America/Santiago"}, 1260 {"Chile/EasterIsland", "Pacific/Easter"}, 1261 {"Cuba", "America/Havana"}, 1262 {"Egypt", "Africa/Cairo"}, 1263 {"Eire", "Europe/Dublin"}, 1264 {"Etc/GMT+0", "Etc/GMT"}, 1265 {"Etc/GMT-0", "Etc/GMT"}, 1266 {"Etc/GMT0", "Etc/GMT"}, 1267 {"Etc/Greenwich", "Etc/GMT"}, 1268 {"Etc/UCT", "Etc/GMT"}, 1269 {"Etc/UTC", "Etc/GMT"}, 1270 {"Etc/Universal", "Etc/GMT"}, 1271 {"Etc/Zulu", "Etc/GMT"}, 1272 {"Europe/Belfast", "Europe/London"}, 1273 {"Europe/Nicosia", "Asia/Nicosia"}, 1274 {"Europe/Tiraspol", "Europe/Chisinau"}, 1275 {"GB", "Europe/London"}, 1276 {"GB-Eire", "Europe/London"}, 1277 {"GMT", "Etc/GMT"}, 1278 {"GMT+0", "Etc/GMT"}, 1279 {"GMT-0", "Etc/GMT"}, 1280 {"GMT0", "Etc/GMT"}, 1281 {"Greenwich", "Etc/GMT"}, 1282 {"Hongkong", "Asia/Hong_Kong"}, 1283 {"Iceland", "Atlantic/Reykjavik"}, 1284 {"Iran", "Asia/Tehran"}, 1285 {"Israel", "Asia/Jerusalem"}, 1286 {"Jamaica", "America/Jamaica"}, 1287 {"Japan", "Asia/Tokyo"}, 1288 {"Kwajalein", "Pacific/Kwajalein"}, 1289 {"Libya", "Africa/Tripoli"}, 1290 {"Mexico/BajaNorte", "America/Tijuana"}, 1291 {"Mexico/BajaSur", "America/Mazatlan"}, 1292 {"Mexico/General", "America/Mexico_City"}, 1293 {"NZ", "Pacific/Auckland"}, 1294 {"NZ-CHAT", "Pacific/Chatham"}, 1295 {"Navajo", "America/Shiprock"}, 1296 {"PRC", "Asia/Shanghai"}, 1297 {"Pacific/Samoa", "Pacific/Pago_Pago"}, 1298 {"Pacific/Yap", "Pacific/Truk"}, 1299 {"Poland", "Europe/Warsaw"}, 1300 {"Portugal", "Europe/Lisbon"}, 1301 {"ROC", "Asia/Taipei"}, 1302 {"ROK", "Asia/Seoul"}, 1303 {"Singapore", "Asia/Singapore"}, 1304 {"Turkey", "Europe/Istanbul"}, 1305 {"UCT", "Etc/GMT"}, 1306 {"US/Alaska", "America/Anchorage"}, 1307 {"US/Aleutian", "America/Adak"}, 1308 {"US/Arizona", "America/Phoenix"}, 1309 {"US/Central", "America/Chicago"}, 1310 {"US/East-Indiana", "America/Indiana/Indianapolis"}, 1311 {"US/Eastern", "America/New_York"}, 1312 {"US/Hawaii", "Pacific/Honolulu"}, 1313 {"US/Indiana-Starke", "America/Indiana/Knox"}, 1314 {"US/Michigan", "America/Detroit"}, 1315 {"US/Mountain", "America/Denver"}, 1316 {"US/Pacific", "America/Los_Angeles"}, 1317 {"US/Pacific-New", "America/Los_Angeles"}, 1318 {"US/Samoa", "Pacific/Pago_Pago"}, 1319 {"UTC", "Etc/GMT"}, 1320 {"Universal", "Etc/GMT"}, 1321 {"W-SU", "Europe/Moscow"}, 1322 {"Zulu", "Etc/GMT"}, 1323 /* Total: 113 */ 1324 1325 }; 1326 1327 TimeZone::EDisplayType styles[] = { TimeZone::SHORT, TimeZone::LONG }; 1328 UBool useDst[] = { FALSE, TRUE }; 1329 int32_t noLoc = uloc_countAvailable(); 1330 1331 int32_t i, j, k, loc; 1332 UnicodeString fromName, toName; 1333 TimeZone *from = NULL, *to = NULL; 1334 for(i = 0; i < (int32_t)(sizeof(kData)/sizeof(kData[0])); i++) { 1335 from = TimeZone::createTimeZone(kData[i].from); 1336 to = TimeZone::createTimeZone(kData[i].to); 1337 if(!from->hasSameRules(*to)) { 1338 errln("different at %i\n", i); 1339 } 1340 if(!quick) { 1341 for(loc = 0; loc < noLoc; loc++) { 1342 const char* locale = uloc_getAvailable(loc); 1343 for(j = 0; j < (int32_t)(sizeof(styles)/sizeof(styles[0])); j++) { 1344 for(k = 0; k < (int32_t)(sizeof(useDst)/sizeof(useDst[0])); k++) { 1345 fromName.remove(); 1346 toName.remove(); 1347 from->getDisplayName(useDst[k], styles[j],locale, fromName); 1348 to->getDisplayName(useDst[k], styles[j], locale, toName); 1349 if(fromName.compare(toName) != 0) { 1350 errln("Fail: Expected "+toName+" but got " + prettify(fromName) 1351 + " for locale: " + locale + " index: "+ loc 1352 + " to id "+ kData[i].to 1353 + " from id " + kData[i].from); 1354 } 1355 } 1356 } 1357 } 1358 } else { 1359 fromName.remove(); 1360 toName.remove(); 1361 from->getDisplayName(fromName); 1362 to->getDisplayName(toName); 1363 if(fromName.compare(toName) != 0) { 1364 errln("Fail: Expected "+toName+" but got " + fromName); 1365 } 1366 } 1367 delete from; 1368 delete to; 1369 } 1370 } 1371 1372 /** 1373 * Test the basic functionality of the getDisplayName() API. 1374 * 1375 * @bug 4112869 1376 * @bug 4028006 1377 * 1378 * See also API change request A41. 1379 * 1380 * 4/21/98 - make smarter, so the test works if the ext resources 1381 * are present or not. 1382 */ 1383 void 1384 TimeZoneTest::TestDisplayName() 1385 { 1386 UErrorCode status = U_ZERO_ERROR; 1387 int32_t i; 1388 TimeZone *zone = TimeZone::createTimeZone("PST"); 1389 UnicodeString name; 1390 zone->getDisplayName(Locale::getEnglish(), name); 1391 logln("PST->" + name); 1392 if (name.compare("Pacific Standard Time") != 0) 1393 dataerrln("Fail: Expected \"Pacific Standard Time\" but got " + name); 1394 1395 //***************************************************************** 1396 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES 1397 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES 1398 // THE FOLLOWING LINES MUST BE UPDATED IF THE LOCALE DATA CHANGES 1399 //***************************************************************** 1400 struct 1401 { 1402 UBool useDst; 1403 TimeZone::EDisplayType style; 1404 const char *expect; 1405 } kData[] = { 1406 {FALSE, TimeZone::SHORT, "PST"}, 1407 {TRUE, TimeZone::SHORT, "PDT"}, 1408 {FALSE, TimeZone::LONG, "Pacific Standard Time"}, 1409 {TRUE, TimeZone::LONG, "Pacific Daylight Time"}, 1410 1411 {FALSE, TimeZone::SHORT_GENERIC, "PT"}, 1412 {TRUE, TimeZone::SHORT_GENERIC, "PT"}, 1413 {FALSE, TimeZone::LONG_GENERIC, "Pacific Time"}, 1414 {TRUE, TimeZone::LONG_GENERIC, "Pacific Time"}, 1415 1416 {FALSE, TimeZone::SHORT_GMT, "-0800"}, 1417 {TRUE, TimeZone::SHORT_GMT, "-0700"}, 1418 {FALSE, TimeZone::LONG_GMT, "GMT-08:00"}, 1419 {TRUE, TimeZone::LONG_GMT, "GMT-07:00"}, 1420 1421 {FALSE, TimeZone::SHORT_COMMONLY_USED, "PST"}, 1422 {TRUE, TimeZone::SHORT_COMMONLY_USED, "PDT"}, 1423 {FALSE, TimeZone::GENERIC_LOCATION, "Los Angeles Time"}, 1424 {TRUE, TimeZone::GENERIC_LOCATION, "Los Angeles Time"}, 1425 1426 {FALSE, TimeZone::LONG, ""} 1427 }; 1428 1429 for (i=0; kData[i].expect[0] != '\0'; i++) 1430 { 1431 name.remove(); 1432 name = zone->getDisplayName(kData[i].useDst, 1433 kData[i].style, 1434 Locale::getEnglish(), name); 1435 if (name.compare(kData[i].expect) != 0) 1436 dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name); 1437 logln("PST [with options]->" + name); 1438 } 1439 for (i=0; kData[i].expect[0] != '\0'; i++) 1440 { 1441 name.remove(); 1442 name = zone->getDisplayName(kData[i].useDst, 1443 kData[i].style, name); 1444 if (name.compare(kData[i].expect) != 0) 1445 dataerrln("Fail: Expected " + UnicodeString(kData[i].expect) + "; got " + name); 1446 logln("PST [with options]->" + name); 1447 } 1448 1449 1450 // Make sure that we don't display the DST name by constructing a fake 1451 // PST zone that has DST all year long. 1452 SimpleTimeZone *zone2 = new SimpleTimeZone(0, "PST"); 1453 1454 zone2->setStartRule(UCAL_JANUARY, 1, 0, 0, status); 1455 zone2->setEndRule(UCAL_DECEMBER, 31, 0, 0, status); 1456 1457 UnicodeString inDaylight; 1458 if (zone2->inDaylightTime(UDate(0), status)) { 1459 inDaylight = UnicodeString("TRUE"); 1460 } else { 1461 inDaylight = UnicodeString("FALSE"); 1462 } 1463 logln(UnicodeString("Modified PST inDaylightTime->") + inDaylight ); 1464 if(U_FAILURE(status)) 1465 { 1466 dataerrln("Some sort of error..." + UnicodeString(u_errorName(status))); // REVISIT 1467 } 1468 name.remove(); 1469 name = zone2->getDisplayName(Locale::getEnglish(),name); 1470 logln("Modified PST->" + name); 1471 if (name.compare("Pacific Standard Time") != 0) 1472 dataerrln("Fail: Expected \"Pacific Standard Time\""); 1473 1474 // Make sure we get the default display format for Locales 1475 // with no display name data. 1476 Locale mt_MT("mt_MT"); 1477 name.remove(); 1478 name = zone->getDisplayName(mt_MT,name); 1479 //***************************************************************** 1480 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES 1481 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES 1482 // THE FOLLOWING LINE MUST BE UPDATED IF THE LOCALE DATA CHANGES 1483 //***************************************************************** 1484 logln("PST(mt_MT)->" + name); 1485 1486 // *** REVISIT SRL how in the world do I check this? looks java specific. 1487 // Now be smart -- check to see if zh resource is even present. 1488 // If not, we expect the en fallback behavior. 1489 ResourceBundle enRB(NULL, 1490 Locale::getEnglish(), status); 1491 if(U_FAILURE(status)) 1492 dataerrln("Couldn't get ResourceBundle for en - %s", u_errorName(status)); 1493 1494 ResourceBundle mtRB(NULL, 1495 mt_MT, status); 1496 //if(U_FAILURE(status)) 1497 // errln("Couldn't get ResourceBundle for mt_MT"); 1498 1499 UBool noZH = U_FAILURE(status); 1500 1501 if (noZH) { 1502 logln("Warning: Not testing the mt_MT behavior because resource is absent"); 1503 if (name != "Pacific Standard Time") 1504 dataerrln("Fail: Expected Pacific Standard Time"); 1505 } 1506 1507 1508 if (name.compare("GMT-08:00") && 1509 name.compare("GMT-8:00") && 1510 name.compare("GMT-0800") && 1511 name.compare("GMT-800")) { 1512 dataerrln(UnicodeString("Fail: Expected GMT-08:00 or something similar for PST in mt_MT but got ") + name ); 1513 dataerrln("************************************************************"); 1514 dataerrln("THE ABOVE FAILURE MAY JUST MEAN THE LOCALE DATA HAS CHANGED"); 1515 dataerrln("************************************************************"); 1516 } 1517 1518 // Now try a non-existent zone 1519 delete zone2; 1520 zone2 = new SimpleTimeZone(90*60*1000, "xyzzy"); 1521 name.remove(); 1522 name = zone2->getDisplayName(Locale::getEnglish(),name); 1523 logln("GMT+90min->" + name); 1524 if (name.compare("GMT+01:30") && 1525 name.compare("GMT+1:30") && 1526 name.compare("GMT+0130") && 1527 name.compare("GMT+130")) 1528 dataerrln("Fail: Expected GMT+01:30 or something similar"); 1529 name.truncate(0); 1530 zone2->getDisplayName(name); 1531 logln("GMT+90min->" + name); 1532 if (name.compare("GMT+01:30") && 1533 name.compare("GMT+1:30") && 1534 name.compare("GMT+0130") && 1535 name.compare("GMT+130")) 1536 dataerrln("Fail: Expected GMT+01:30 or something similar"); 1537 // clean up 1538 delete zone; 1539 delete zone2; 1540 } 1541 1542 /** 1543 * @bug 4107276 1544 */ 1545 void 1546 TimeZoneTest::TestDSTSavings() 1547 { 1548 UErrorCode status = U_ZERO_ERROR; 1549 // It might be better to find a way to integrate this test into the main TimeZone 1550 // tests above, but I don't have time to figure out how to do this (or if it's 1551 // even really a good idea). Let's consider that a future. --rtg 1/27/98 1552 SimpleTimeZone *tz = new SimpleTimeZone(-5 * U_MILLIS_PER_HOUR, "dstSavingsTest", 1553 UCAL_MARCH, 1, 0, 0, UCAL_SEPTEMBER, 1, 0, 0, 1554 (int32_t)(0.5 * U_MILLIS_PER_HOUR), status); 1555 if(U_FAILURE(status)) 1556 errln("couldn't create TimeZone"); 1557 1558 if (tz->getRawOffset() != -5 * U_MILLIS_PER_HOUR) 1559 errln(UnicodeString("Got back a raw offset of ") + (tz->getRawOffset() / U_MILLIS_PER_HOUR) + 1560 " hours instead of -5 hours."); 1561 if (!tz->useDaylightTime()) 1562 errln("Test time zone should use DST but claims it doesn't."); 1563 if (tz->getDSTSavings() != 0.5 * U_MILLIS_PER_HOUR) 1564 errln(UnicodeString("Set DST offset to 0.5 hour, but got back ") + (tz->getDSTSavings() / 1565 U_MILLIS_PER_HOUR) + " hours instead."); 1566 1567 int32_t offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1, 1568 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status); 1569 if (offset != -5 * U_MILLIS_PER_HOUR) 1570 errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ") 1571 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1572 1573 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY, 1574 10 * U_MILLIS_PER_HOUR,status); 1575 if (offset != -4.5 * U_MILLIS_PER_HOUR) 1576 errln(UnicodeString("The offset for 10 AM, 6/1/98 should have been -4.5 hours, but we got ") 1577 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1578 1579 tz->setDSTSavings(U_MILLIS_PER_HOUR, status); 1580 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JANUARY, 1, 1581 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status); 1582 if (offset != -5 * U_MILLIS_PER_HOUR) 1583 errln(UnicodeString("The offset for 10 AM, 1/1/98 should have been -5 hours, but we got ") 1584 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1585 1586 offset = tz->getOffset(GregorianCalendar::AD, 1998, UCAL_JUNE, 1, UCAL_MONDAY, 1587 10 * U_MILLIS_PER_HOUR,status); 1588 if (offset != -4 * U_MILLIS_PER_HOUR) 1589 errln(UnicodeString("The offset for 10 AM, 6/1/98 (with a 1-hour DST offset) should have been -4 hours, but we got ") 1590 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1591 1592 delete tz; 1593 } 1594 1595 /** 1596 * @bug 4107570 1597 */ 1598 void 1599 TimeZoneTest::TestAlternateRules() 1600 { 1601 // Like TestDSTSavings, this test should probably be integrated somehow with the main 1602 // test at the top of this class, but I didn't have time to figure out how to do that. 1603 // --rtg 1/28/98 1604 1605 SimpleTimeZone tz(-5 * U_MILLIS_PER_HOUR, "alternateRuleTest"); 1606 1607 // test the day-of-month API 1608 UErrorCode status = U_ZERO_ERROR; 1609 tz.setStartRule(UCAL_MARCH, 10, 12 * U_MILLIS_PER_HOUR, status); 1610 if(U_FAILURE(status)) 1611 errln("tz.setStartRule failed"); 1612 tz.setEndRule(UCAL_OCTOBER, 20, 12 * U_MILLIS_PER_HOUR, status); 1613 if(U_FAILURE(status)) 1614 errln("tz.setStartRule failed"); 1615 1616 int32_t offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 5, 1617 UCAL_THURSDAY, 10 * U_MILLIS_PER_HOUR,status); 1618 if (offset != -5 * U_MILLIS_PER_HOUR) 1619 errln(UnicodeString("The offset for 10AM, 3/5/98 should have been -5 hours, but we got ") 1620 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1621 1622 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 15, 1623 UCAL_SUNDAY, 10 * millisPerHour,status); 1624 if (offset != -4 * U_MILLIS_PER_HOUR) 1625 errln(UnicodeString("The offset for 10AM, 3/15/98 should have been -4 hours, but we got ") 1626 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1627 1628 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15, 1629 UCAL_THURSDAY, 10 * millisPerHour,status); 1630 if (offset != -4 * U_MILLIS_PER_HOUR) 1631 errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ") + (offset / U_MILLIS_PER_HOUR) + " hours."); 1632 1633 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 25, 1634 UCAL_SUNDAY, 10 * millisPerHour,status); 1635 if (offset != -5 * U_MILLIS_PER_HOUR) 1636 errln(UnicodeString("The offset for 10AM, 10/25/98 should have been -5 hours, but we got ") 1637 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1638 1639 // test the day-of-week-after-day-in-month API 1640 tz.setStartRule(UCAL_MARCH, 10, UCAL_FRIDAY, 12 * millisPerHour, TRUE, status); 1641 if(U_FAILURE(status)) 1642 errln("tz.setStartRule failed"); 1643 tz.setEndRule(UCAL_OCTOBER, 20, UCAL_FRIDAY, 12 * millisPerHour, FALSE, status); 1644 if(U_FAILURE(status)) 1645 errln("tz.setStartRule failed"); 1646 1647 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 11, 1648 UCAL_WEDNESDAY, 10 * millisPerHour,status); 1649 if (offset != -5 * U_MILLIS_PER_HOUR) 1650 errln(UnicodeString("The offset for 10AM, 3/11/98 should have been -5 hours, but we got ") 1651 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1652 1653 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_MARCH, 14, 1654 UCAL_SATURDAY, 10 * millisPerHour,status); 1655 if (offset != -4 * U_MILLIS_PER_HOUR) 1656 errln(UnicodeString("The offset for 10AM, 3/14/98 should have been -4 hours, but we got ") 1657 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1658 1659 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 15, 1660 UCAL_THURSDAY, 10 * millisPerHour,status); 1661 if (offset != -4 * U_MILLIS_PER_HOUR) 1662 errln(UnicodeString("The offset for 10AM, 10/15/98 should have been -4 hours, but we got ") 1663 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1664 1665 offset = tz.getOffset(GregorianCalendar::AD, 1998, UCAL_OCTOBER, 17, 1666 UCAL_SATURDAY, 10 * millisPerHour,status); 1667 if (offset != -5 * U_MILLIS_PER_HOUR) 1668 errln(UnicodeString("The offset for 10AM, 10/17/98 should have been -5 hours, but we got ") 1669 + (offset / U_MILLIS_PER_HOUR) + " hours."); 1670 } 1671 1672 void TimeZoneTest::TestFractionalDST() { 1673 const char* tzName = "Australia/Lord_Howe"; // 30 min offset 1674 TimeZone* tz_icu = TimeZone::createTimeZone(tzName); 1675 int dst_icu = tz_icu->getDSTSavings(); 1676 UnicodeString id; 1677 int32_t expected = 1800000; 1678 if (expected != dst_icu) { 1679 dataerrln(UnicodeString("java reports dst savings of ") + expected + 1680 " but icu reports " + dst_icu + 1681 " for tz " + tz_icu->getID(id)); 1682 } else { 1683 logln(UnicodeString("both java and icu report dst savings of ") + expected + " for tz " + tz_icu->getID(id)); 1684 } 1685 delete tz_icu; 1686 } 1687 1688 /** 1689 * Test country code support. Jitterbug 776. 1690 */ 1691 void TimeZoneTest::TestCountries() { 1692 // Make sure America/Los_Angeles is in the "US" group, and 1693 // Asia/Tokyo isn't. Vice versa for the "JP" group. 1694 UErrorCode ec = U_ZERO_ERROR; 1695 int32_t n; 1696 StringEnumeration* s = TimeZone::createEnumeration("US"); 1697 if (s == NULL) { 1698 dataerrln("Unable to create TimeZone enumeration for US"); 1699 return; 1700 } 1701 n = s->count(ec); 1702 UBool la = FALSE, tokyo = FALSE; 1703 UnicodeString laZone("America/Los_Angeles", ""); 1704 UnicodeString tokyoZone("Asia/Tokyo", ""); 1705 int32_t i; 1706 1707 if (s == NULL || n <= 0) { 1708 dataerrln("FAIL: TimeZone::createEnumeration() returned nothing"); 1709 return; 1710 } 1711 for (i=0; i<n; ++i) { 1712 const UnicodeString* id = s->snext(ec); 1713 if (*id == (laZone)) { 1714 la = TRUE; 1715 } 1716 if (*id == (tokyoZone)) { 1717 tokyo = TRUE; 1718 } 1719 } 1720 if (!la || tokyo) { 1721 errln("FAIL: " + laZone + " in US = " + la); 1722 errln("FAIL: " + tokyoZone + " in US = " + tokyo); 1723 } 1724 delete s; 1725 1726 s = TimeZone::createEnumeration("JP"); 1727 n = s->count(ec); 1728 la = FALSE; tokyo = FALSE; 1729 1730 for (i=0; i<n; ++i) { 1731 const UnicodeString* id = s->snext(ec); 1732 if (*id == (laZone)) { 1733 la = TRUE; 1734 } 1735 if (*id == (tokyoZone)) { 1736 tokyo = TRUE; 1737 } 1738 } 1739 if (la || !tokyo) { 1740 errln("FAIL: " + laZone + " in JP = " + la); 1741 errln("FAIL: " + tokyoZone + " in JP = " + tokyo); 1742 } 1743 StringEnumeration* s1 = TimeZone::createEnumeration("US"); 1744 StringEnumeration* s2 = TimeZone::createEnumeration("US"); 1745 for(i=0;i<n;++i){ 1746 const UnicodeString* id1 = s1->snext(ec); 1747 if(id1==NULL || U_FAILURE(ec)){ 1748 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i); 1749 } 1750 TimeZone* tz1 = TimeZone::createTimeZone(*id1); 1751 for(int j=0; j<n;++j){ 1752 const UnicodeString* id2 = s2->snext(ec); 1753 if(id2==NULL || U_FAILURE(ec)){ 1754 errln("Failed to fetch next from TimeZone enumeration. Length returned : %i Current Index: %i", n,i); 1755 } 1756 TimeZone* tz2 = TimeZone::createTimeZone(*id2); 1757 if(tz1->hasSameRules(*tz2)){ 1758 logln("ID1 : " + *id1+" == ID2 : " +*id2); 1759 } 1760 delete tz2; 1761 } 1762 delete tz1; 1763 } 1764 delete s1; 1765 delete s2; 1766 delete s; 1767 } 1768 1769 void TimeZoneTest::TestHistorical() { 1770 const int32_t H = U_MILLIS_PER_HOUR; 1771 struct { 1772 const char* id; 1773 int32_t time; // epoch seconds 1774 int32_t offset; // total offset (millis) 1775 } DATA[] = { 1776 // Add transition points (before/after) as desired to test historical 1777 // behavior. 1778 {"America/Los_Angeles", 638963999, -8*H}, // Sun Apr 01 01:59:59 GMT-08:00 1990 1779 {"America/Los_Angeles", 638964000, -7*H}, // Sun Apr 01 03:00:00 GMT-07:00 1990 1780 {"America/Los_Angeles", 657104399, -7*H}, // Sun Oct 28 01:59:59 GMT-07:00 1990 1781 {"America/Los_Angeles", 657104400, -8*H}, // Sun Oct 28 01:00:00 GMT-08:00 1990 1782 {"America/Goose_Bay", -116445601, -4*H}, // Sun Apr 24 01:59:59 GMT-04:00 1966 1783 {"America/Goose_Bay", -116445600, -3*H}, // Sun Apr 24 03:00:00 GMT-03:00 1966 1784 {"America/Goose_Bay", -100119601, -3*H}, // Sun Oct 30 01:59:59 GMT-03:00 1966 1785 {"America/Goose_Bay", -100119600, -4*H}, // Sun Oct 30 01:00:00 GMT-04:00 1966 1786 {"America/Goose_Bay", -84391201, -4*H}, // Sun Apr 30 01:59:59 GMT-04:00 1967 1787 {"America/Goose_Bay", -84391200, -3*H}, // Sun Apr 30 03:00:00 GMT-03:00 1967 1788 {"America/Goose_Bay", -68670001, -3*H}, // Sun Oct 29 01:59:59 GMT-03:00 1967 1789 {"America/Goose_Bay", -68670000, -4*H}, // Sun Oct 29 01:00:00 GMT-04:00 1967 1790 {0, 0, 0} 1791 }; 1792 1793 for (int32_t i=0; DATA[i].id!=0; ++i) { 1794 const char* id = DATA[i].id; 1795 TimeZone *tz = TimeZone::createTimeZone(id); 1796 UnicodeString s; 1797 if (tz == 0) { 1798 errln("FAIL: Cannot create %s", id); 1799 } else if (tz->getID(s) != UnicodeString(id)) { 1800 dataerrln((UnicodeString)"FAIL: createTimeZone(" + id + ") => " + s); 1801 } else { 1802 UErrorCode ec = U_ZERO_ERROR; 1803 int32_t raw, dst; 1804 UDate when = (double) DATA[i].time * U_MILLIS_PER_SECOND; 1805 tz->getOffset(when, FALSE, raw, dst, ec); 1806 if (U_FAILURE(ec)) { 1807 errln("FAIL: getOffset"); 1808 } else if ((raw+dst) != DATA[i].offset) { 1809 errln((UnicodeString)"FAIL: " + DATA[i].id + ".getOffset(" + 1810 //when + " = " + 1811 dateToString(when) + ") => " + 1812 raw + ", " + dst); 1813 } else { 1814 logln((UnicodeString)"Ok: " + DATA[i].id + ".getOffset(" + 1815 //when + " = " + 1816 dateToString(when) + ") => " + 1817 raw + ", " + dst); 1818 } 1819 } 1820 delete tz; 1821 } 1822 } 1823 1824 void TimeZoneTest::TestEquivalentIDs() { 1825 int32_t n = TimeZone::countEquivalentIDs("PST"); 1826 if (n < 2) { 1827 dataerrln((UnicodeString)"FAIL: countEquivalentIDs(PST) = " + n); 1828 } else { 1829 UBool sawLA = FALSE; 1830 for (int32_t i=0; i<n; ++i) { 1831 UnicodeString id = TimeZone::getEquivalentID("PST", i); 1832 logln((UnicodeString)"" + i + " : " + id); 1833 if (id == UnicodeString("America/Los_Angeles")) { 1834 sawLA = TRUE; 1835 } 1836 } 1837 if (!sawLA) { 1838 errln("FAIL: America/Los_Angeles should be in the list"); 1839 } 1840 } 1841 } 1842 1843 // Test that a transition at the end of February is handled correctly. 1844 void TimeZoneTest::TestFebruary() { 1845 UErrorCode status = U_ZERO_ERROR; 1846 1847 // Time zone with daylight savings time from the first Sunday in November 1848 // to the last Sunday in February. 1849 // Similar to the new rule for Brazil (Sao Paulo) in tzdata2006n. 1850 // 1851 // Note: In tzdata2007h, the rule had changed, so no actual zones uses 1852 // lastSun in Feb anymore. 1853 SimpleTimeZone tz1(-3 * U_MILLIS_PER_HOUR, // raw offset: 3h before (west of) GMT 1854 UNICODE_STRING("nov-feb", 7), 1855 UCAL_NOVEMBER, 1, UCAL_SUNDAY, // start: November, first, Sunday 1856 0, // midnight wall time 1857 UCAL_FEBRUARY, -1, UCAL_SUNDAY, // end: February, last, Sunday 1858 0, // midnight wall time 1859 status); 1860 if (U_FAILURE(status)) { 1861 errln("Unable to create the SimpleTimeZone(nov-feb): %s", u_errorName(status)); 1862 return; 1863 } 1864 1865 // Now hardcode the same rules as for Brazil in tzdata 2006n, so that 1866 // we cover the intended code even when in the future zoneinfo hardcodes 1867 // these transition dates. 1868 SimpleTimeZone tz2(-3 * U_MILLIS_PER_HOUR, // raw offset: 3h before (west of) GMT 1869 UNICODE_STRING("nov-feb2", 8), 1870 UCAL_NOVEMBER, 1, -UCAL_SUNDAY, // start: November, 1 or after, Sunday 1871 0, // midnight wall time 1872 UCAL_FEBRUARY, -29, -UCAL_SUNDAY,// end: February, 29 or before, Sunday 1873 0, // midnight wall time 1874 status); 1875 if (U_FAILURE(status)) { 1876 errln("Unable to create the SimpleTimeZone(nov-feb2): %s", u_errorName(status)); 1877 return; 1878 } 1879 1880 // Gregorian calendar with the UTC time zone for getting sample test date/times. 1881 GregorianCalendar gc(*TimeZone::getGMT(), status); 1882 if (U_FAILURE(status)) { 1883 dataerrln("Unable to create the UTC calendar: %s", u_errorName(status)); 1884 return; 1885 } 1886 1887 struct { 1888 // UTC time. 1889 int32_t year, month, day, hour, minute, second; 1890 // Expected time zone offset in hours after GMT (negative=before GMT). 1891 int32_t offsetHours; 1892 } data[] = { 1893 { 2006, UCAL_NOVEMBER, 5, 02, 59, 59, -3 }, 1894 { 2006, UCAL_NOVEMBER, 5, 03, 00, 00, -2 }, 1895 { 2007, UCAL_FEBRUARY, 25, 01, 59, 59, -2 }, 1896 { 2007, UCAL_FEBRUARY, 25, 02, 00, 00, -3 }, 1897 1898 { 2007, UCAL_NOVEMBER, 4, 02, 59, 59, -3 }, 1899 { 2007, UCAL_NOVEMBER, 4, 03, 00, 00, -2 }, 1900 { 2008, UCAL_FEBRUARY, 24, 01, 59, 59, -2 }, 1901 { 2008, UCAL_FEBRUARY, 24, 02, 00, 00, -3 }, 1902 1903 { 2008, UCAL_NOVEMBER, 2, 02, 59, 59, -3 }, 1904 { 2008, UCAL_NOVEMBER, 2, 03, 00, 00, -2 }, 1905 { 2009, UCAL_FEBRUARY, 22, 01, 59, 59, -2 }, 1906 { 2009, UCAL_FEBRUARY, 22, 02, 00, 00, -3 }, 1907 1908 { 2009, UCAL_NOVEMBER, 1, 02, 59, 59, -3 }, 1909 { 2009, UCAL_NOVEMBER, 1, 03, 00, 00, -2 }, 1910 { 2010, UCAL_FEBRUARY, 28, 01, 59, 59, -2 }, 1911 { 2010, UCAL_FEBRUARY, 28, 02, 00, 00, -3 } 1912 }; 1913 1914 TimeZone *timezones[] = { &tz1, &tz2 }; 1915 1916 TimeZone *tz; 1917 UDate dt; 1918 int32_t t, i, raw, dst; 1919 for (t = 0; t < LENGTHOF(timezones); ++t) { 1920 tz = timezones[t]; 1921 for (i = 0; i < LENGTHOF(data); ++i) { 1922 gc.set(data[i].year, data[i].month, data[i].day, 1923 data[i].hour, data[i].minute, data[i].second); 1924 dt = gc.getTime(status); 1925 if (U_FAILURE(status)) { 1926 errln("test case %d.%d: bad date/time %04d-%02d-%02d %02d:%02d:%02d", 1927 t, i, 1928 data[i].year, data[i].month + 1, data[i].day, 1929 data[i].hour, data[i].minute, data[i].second); 1930 status = U_ZERO_ERROR; 1931 continue; 1932 } 1933 tz->getOffset(dt, FALSE, raw, dst, status); 1934 if (U_FAILURE(status)) { 1935 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) fails: %s", 1936 t, i, 1937 data[i].year, data[i].month + 1, data[i].day, 1938 data[i].hour, data[i].minute, data[i].second, 1939 u_errorName(status)); 1940 status = U_ZERO_ERROR; 1941 } else if ((raw + dst) != data[i].offsetHours * U_MILLIS_PER_HOUR) { 1942 errln("test case %d.%d: tz.getOffset(%04d-%02d-%02d %02d:%02d:%02d) returns %d+%d != %d", 1943 t, i, 1944 data[i].year, data[i].month + 1, data[i].day, 1945 data[i].hour, data[i].minute, data[i].second, 1946 raw, dst, data[i].offsetHours * U_MILLIS_PER_HOUR); 1947 } 1948 } 1949 } 1950 } 1951 void TimeZoneTest::TestCanonicalID() { 1952 1953 // Some canonical IDs in CLDR are defined as "Link" 1954 // in Olson tzdata. 1955 static const struct { 1956 const char *alias; 1957 const char *zone; 1958 } excluded1[] = { 1959 {"Africa/Khartoum", "Africa/Juba"}, 1960 {"America/Shiprock", "America/Denver"}, // America/Shiprock is defined as a Link to America/Denver in tzdata 1961 {"America/Dominica", "America/Anguilla"}, 1962 {"America/Grenada", "America/Anguilla"}, 1963 {"America/Guadeloupe", "America/Anguilla"}, 1964 {"America/Marigot", "America/Anguilla"}, 1965 {"America/Montserrat", "America/Anguilla"}, 1966 {"America/Port_of_Spain", "America/Anguilla"}, 1967 {"America/St_Barthelemy", "America/Anguilla"}, 1968 {"America/St_Kitts", "America/Anguilla"}, 1969 {"America/St_Lucia", "America/Anguilla"}, 1970 {"America/St_Thomas", "America/Anguilla"}, 1971 {"America/St_Vincent", "America/Anguilla"}, 1972 {"America/Tortola", "America/Anguilla"}, 1973 {"America/Virgin", "America/Anguilla"}, 1974 {"America/Curacao", "America/Aruba"}, 1975 {"America/Kralendijk", "America/Aruba"}, 1976 {"America/Lower_Princes", "America/Aruba"}, 1977 {"Antarctica/South_Pole", "Antarctica/McMurdo"}, 1978 {"Atlantic/Jan_Mayen", "Europe/Oslo"}, 1979 {"Arctic/Longyearbyen", "Europe/Oslo"}, 1980 {"Europe/Busingen", "Europe/Zurich"}, 1981 {"Europe/Guernsey", "Europe/London"}, 1982 {"Europe/Isle_of_Man", "Europe/London"}, 1983 {"Europe/Jersey", "Europe/London"}, 1984 {"Europe/Ljubljana", "Europe/Belgrade"}, 1985 {"Europe/Podgorica", "Europe/Belgrade"}, 1986 {"Europe/Sarajevo", "Europe/Belgrade"}, 1987 {"Europe/Skopje", "Europe/Belgrade"}, 1988 {"Europe/Zagreb", "Europe/Belgrade"}, 1989 {"Europe/Bratislava", "Europe/Prague"}, 1990 {"Europe/Mariehamn", "Europe/Helsinki"}, 1991 {"Europe/San_Marino", "Europe/Rome"}, 1992 {"Europe/Vatican", "Europe/Rome"}, 1993 {"Europe/Vaduz", "Europe/Zurich"}, 1994 {"Pacific/Auckland", "Antarctica/McMurdo"}, 1995 {"Pacific/Johnston", "Pacific/Honolulu"}, 1996 {0, 0} 1997 }; 1998 1999 // Following IDs are aliases of Etc/GMT in CLDR, 2000 // but Olson tzdata has 3 independent definitions 2001 // for Etc/GMT, Etc/UTC, Etc/UCT. 2002 // Until we merge them into one equivalent group 2003 // in zoneinfo.res, we exclude them in the test 2004 // below. 2005 static const char* excluded2[] = { 2006 "Etc/UCT", "UCT", 2007 "Etc/UTC", "UTC", 2008 "Etc/Universal", "Universal", 2009 "Etc/Zulu", "Zulu", 0 2010 }; 2011 2012 // Walk through equivalency groups 2013 UErrorCode ec = U_ZERO_ERROR; 2014 int32_t s_length, i, j, k; 2015 StringEnumeration* s = TimeZone::createEnumeration(); 2016 if (s == NULL) { 2017 dataerrln("Unable to create TimeZone enumeration"); 2018 return; 2019 } 2020 UnicodeString canonicalID, tmpCanonical; 2021 s_length = s->count(ec); 2022 for (i = 0; i < s_length;++i) { 2023 const UnicodeString *tzid = s->snext(ec); 2024 int32_t nEquiv = TimeZone::countEquivalentIDs(*tzid); 2025 if (nEquiv == 0) { 2026 continue; 2027 } 2028 UBool bFoundCanonical = FALSE; 2029 // Make sure getCanonicalID returns the exact same result 2030 // for all entries within a same equivalency group with some 2031 // exceptions listed in exluded1. 2032 // Also, one of them must be canonical id. 2033 for (j = 0; j < nEquiv; j++) { 2034 UnicodeString tmp = TimeZone::getEquivalentID(*tzid, j); 2035 TimeZone::getCanonicalID(tmp, tmpCanonical, ec); 2036 if (U_FAILURE(ec)) { 2037 errln((UnicodeString)"FAIL: getCanonicalID(" + tmp + ") failed."); 2038 ec = U_ZERO_ERROR; 2039 continue; 2040 } 2041 // Some exceptional cases 2042 for (k = 0; excluded1[k].alias != 0; k++) { 2043 if (tmpCanonical == excluded1[k].alias) { 2044 tmpCanonical = excluded1[k].zone; 2045 break; 2046 } 2047 } 2048 if (j == 0) { 2049 canonicalID = tmpCanonical; 2050 } else if (canonicalID != tmpCanonical) { 2051 errln("FAIL: getCanonicalID(" + tmp + ") returned " + tmpCanonical + " expected:" + canonicalID); 2052 } 2053 2054 if (canonicalID == tmp) { 2055 bFoundCanonical = TRUE; 2056 } 2057 } 2058 // At least one ID in an equvalency group must match the 2059 // canonicalID 2060 if (bFoundCanonical == FALSE) { 2061 // test exclusion because of differences between Olson tzdata and CLDR 2062 UBool isExcluded = FALSE; 2063 for (k = 0; excluded2[k] != 0; k++) { 2064 if (*tzid == UnicodeString(excluded2[k])) { 2065 isExcluded = TRUE; 2066 break; 2067 } 2068 } 2069 if (isExcluded) { 2070 continue; 2071 } 2072 errln((UnicodeString)"FAIL: No timezone ids match the canonical ID " + canonicalID); 2073 } 2074 } 2075 delete s; 2076 2077 // Testing some special cases 2078 static const struct { 2079 const char *id; 2080 const char *expected; 2081 UBool isSystem; 2082 } data[] = { 2083 {"GMT-03", "GMT-03:00", FALSE}, 2084 {"GMT+4", "GMT+04:00", FALSE}, 2085 {"GMT-055", "GMT-00:55", FALSE}, 2086 {"GMT+430", "GMT+04:30", FALSE}, 2087 {"GMT-12:15", "GMT-12:15", FALSE}, 2088 {"GMT-091015", "GMT-09:10:15", FALSE}, 2089 {"GMT+1:90", 0, FALSE}, 2090 {"America/Argentina/Buenos_Aires", "America/Buenos_Aires", TRUE}, 2091 {"Etc/Unknown", "Etc/Unknown", FALSE}, 2092 {"bogus", 0, FALSE}, 2093 {"", 0, FALSE}, 2094 {"America/Marigot", "America/Marigot", TRUE}, // Olson link, but CLDR canonical (#8953) 2095 {"Europe/Bratislava", "Europe/Bratislava", TRUE}, // Same as above 2096 {0, 0, FALSE} 2097 }; 2098 2099 UBool isSystemID; 2100 for (i = 0; data[i].id != 0; i++) { 2101 TimeZone::getCanonicalID(UnicodeString(data[i].id), canonicalID, isSystemID, ec); 2102 if (U_FAILURE(ec)) { 2103 if (ec != U_ILLEGAL_ARGUMENT_ERROR || data[i].expected != 0) { 2104 errln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id 2105 + "\") returned status U_ILLEGAL_ARGUMENT_ERROR"); 2106 } 2107 ec = U_ZERO_ERROR; 2108 continue; 2109 } 2110 if (canonicalID != data[i].expected) { 2111 dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id 2112 + "\") returned " + canonicalID + " - expected: " + data[i].expected); 2113 } 2114 if (isSystemID != data[i].isSystem) { 2115 dataerrln((UnicodeString)"FAIL: getCanonicalID(\"" + data[i].id 2116 + "\") set " + isSystemID + " to isSystemID"); 2117 } 2118 } 2119 } 2120 2121 // 2122 // Test Display Names, choosing zones and lcoales where there are multiple 2123 // meta-zones defined. 2124 // 2125 static struct { 2126 const char *zoneName; 2127 const char *localeName; 2128 UBool summerTime; 2129 TimeZone::EDisplayType style; 2130 const char *expectedDisplayName; } 2131 zoneDisplayTestData [] = { 2132 // zone id locale summer format expected display name 2133 {"Europe/London", "en", FALSE, TimeZone::SHORT, "GMT"}, 2134 {"Europe/London", "en", FALSE, TimeZone::LONG, "Greenwich Mean Time"}, 2135 {"Europe/London", "en", TRUE, TimeZone::SHORT, "GMT+1" /*"BST"*/}, 2136 {"Europe/London", "en", TRUE, TimeZone::LONG, "British Summer Time"}, 2137 2138 {"America/Anchorage", "en", FALSE, TimeZone::SHORT, "AKST"}, 2139 {"America/Anchorage", "en", FALSE, TimeZone::LONG, "Alaska Standard Time"}, 2140 {"America/Anchorage", "en", TRUE, TimeZone::SHORT, "AKDT"}, 2141 {"America/Anchorage", "en", TRUE, TimeZone::LONG, "Alaska Daylight Time"}, 2142 2143 // Southern Hemisphere, all data from meta:Australia_Western 2144 {"Australia/Perth", "en", FALSE, TimeZone::SHORT, "GMT+8"/*"AWST"*/}, 2145 {"Australia/Perth", "en", FALSE, TimeZone::LONG, "Australian Western Standard Time"}, 2146 // Note: Perth does not observe DST currently. When display name is missing, 2147 // the localized GMT format with the current offset is used even daylight name was 2148 // requested. See #9350. 2149 {"Australia/Perth", "en", TRUE, TimeZone::SHORT, "GMT+8"/*"AWDT"*/}, 2150 {"Australia/Perth", "en", TRUE, TimeZone::LONG, "Australian Western Daylight Time"}, 2151 2152 {"America/Sao_Paulo", "en", FALSE, TimeZone::SHORT, "GMT-3"/*"BRT"*/}, 2153 {"America/Sao_Paulo", "en", FALSE, TimeZone::LONG, "Brasilia Standard Time"}, 2154 {"America/Sao_Paulo", "en", TRUE, TimeZone::SHORT, "GMT-2"/*"BRST"*/}, 2155 {"America/Sao_Paulo", "en", TRUE, TimeZone::LONG, "Brasilia Summer Time"}, 2156 2157 // No Summer Time, but had it before 1983. 2158 {"Pacific/Honolulu", "en", FALSE, TimeZone::SHORT, "HST"}, 2159 {"Pacific/Honolulu", "en", FALSE, TimeZone::LONG, "Hawaii-Aleutian Standard Time"}, 2160 {"Pacific/Honolulu", "en", TRUE, TimeZone::SHORT, "HDT"}, 2161 {"Pacific/Honolulu", "en", TRUE, TimeZone::LONG, "Hawaii-Aleutian Daylight Time"}, 2162 2163 // Northern, has Summer, not commonly used. 2164 {"Europe/Helsinki", "en", FALSE, TimeZone::SHORT, "GMT+2"/*"EET"*/}, 2165 {"Europe/Helsinki", "en", FALSE, TimeZone::LONG, "Eastern European Standard Time"}, 2166 {"Europe/Helsinki", "en", TRUE, TimeZone::SHORT, "GMT+3"/*"EEST"*/}, 2167 {"Europe/Helsinki", "en", TRUE, TimeZone::LONG, "Eastern European Summer Time"}, 2168 2169 // Repeating the test data for DST. The test data below trigger the problem reported 2170 // by Ticket#6644 2171 {"Europe/London", "en", TRUE, TimeZone::SHORT, "GMT+1" /*"BST"*/}, 2172 {"Europe/London", "en", TRUE, TimeZone::LONG, "British Summer Time"}, 2173 2174 {NULL, NULL, FALSE, TimeZone::SHORT, NULL} // NULL values terminate list 2175 }; 2176 2177 void TimeZoneTest::TestDisplayNamesMeta() { 2178 UErrorCode status = U_ZERO_ERROR; 2179 GregorianCalendar cal(*TimeZone::getGMT(), status); 2180 if (failure(status, "GregorianCalendar", TRUE)) return; 2181 2182 UBool sawAnError = FALSE; 2183 for (int testNum = 0; zoneDisplayTestData[testNum].zoneName != NULL; testNum++) { 2184 Locale locale = Locale::createFromName(zoneDisplayTestData[testNum].localeName); 2185 TimeZone *zone = TimeZone::createTimeZone(zoneDisplayTestData[testNum].zoneName); 2186 UnicodeString displayName; 2187 zone->getDisplayName(zoneDisplayTestData[testNum].summerTime, 2188 zoneDisplayTestData[testNum].style, 2189 locale, 2190 displayName); 2191 if (displayName != zoneDisplayTestData[testNum].expectedDisplayName) { 2192 char name[100]; 2193 UErrorCode status = U_ZERO_ERROR; 2194 displayName.extract(name, 100, NULL, status); 2195 if (isDevelopmentBuild) { 2196 sawAnError = TRUE; 2197 dataerrln("Incorrect time zone display name. zone = \"%s\",\n" 2198 " locale = \"%s\", style = %s, Summertime = %d\n" 2199 " Expected \"%s\", " 2200 " Got \"%s\"\n Error: %s", zoneDisplayTestData[testNum].zoneName, 2201 zoneDisplayTestData[testNum].localeName, 2202 zoneDisplayTestData[testNum].style==TimeZone::SHORT ? 2203 "SHORT" : "LONG", 2204 zoneDisplayTestData[testNum].summerTime, 2205 zoneDisplayTestData[testNum].expectedDisplayName, 2206 name, 2207 u_errorName(status)); 2208 } else { 2209 logln("Incorrect time zone display name. zone = \"%s\",\n" 2210 " locale = \"%s\", style = %s, Summertime = %d\n" 2211 " Expected \"%s\", " 2212 " Got \"%s\"\n", zoneDisplayTestData[testNum].zoneName, 2213 zoneDisplayTestData[testNum].localeName, 2214 zoneDisplayTestData[testNum].style==TimeZone::SHORT ? 2215 "SHORT" : "LONG", 2216 zoneDisplayTestData[testNum].summerTime, 2217 zoneDisplayTestData[testNum].expectedDisplayName, 2218 name); 2219 } 2220 } 2221 delete zone; 2222 } 2223 if (sawAnError) { 2224 dataerrln("***Note: Errors could be the result of changes to zoneStrings locale data"); 2225 } 2226 } 2227 2228 void TimeZoneTest::TestGetRegion() 2229 { 2230 static const struct { 2231 const char *id; 2232 const char *region; 2233 } data[] = { 2234 {"America/Los_Angeles", "US"}, 2235 {"America/Indianapolis", "US"}, // CLDR canonical, Olson backward 2236 {"America/Indiana/Indianapolis", "US"}, // CLDR alias 2237 {"Mexico/General", "MX"}, // Link America/Mexico_City, Olson backward 2238 {"Etc/UTC", "001"}, 2239 {"EST5EDT", "001"}, 2240 {"PST", "US"}, // Link America/Los_Angeles 2241 {"Europe/Helsinki", "FI"}, 2242 {"Europe/Mariehamn", "AX"}, // Link Europe/Helsinki, but in zone.tab 2243 {"Asia/Riyadh", "SA"}, 2244 {"Asia/Riyadh87", "001"}, // this should be "SA" actually, but not in zone.tab 2245 {"Etc/Unknown", 0}, // CLDR canonical, but not a sysmte zone ID 2246 {"bogus", 0}, // bogus 2247 {"GMT+08:00", 0}, // a custom ID, not a system zone ID 2248 {0, 0} 2249 }; 2250 2251 int32_t i; 2252 char region[4]; 2253 UErrorCode sts; 2254 for (i = 0; data[i].id; i++) { 2255 sts = U_ZERO_ERROR; 2256 TimeZone::getRegion(data[i].id, region, sizeof(region), sts); 2257 if (U_SUCCESS(sts)) { 2258 if (data[i].region == 0) { 2259 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns " 2260 + region + " [expected: U_ILLEGAL_ARGUMENT_ERROR]"); 2261 } else if (uprv_strcmp(region, data[i].region) != 0) { 2262 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id + "\") returns " 2263 + region + " [expected: " + data[i].region + "]"); 2264 } 2265 } else if (sts == U_ILLEGAL_ARGUMENT_ERROR) { 2266 if (data[i].region != 0) { 2267 dataerrln((UnicodeString)"Fail: getRegion(\"" + data[i].id 2268 + "\") returns error status U_ILLEGAL_ARGUMENT_ERROR [expected: " 2269 + data[i].region + "]"); 2270 } 2271 } else { 2272 errln((UnicodeString)"Fail: getRegion(\"" + data[i].id 2273 + "\") returns an unexpected error status"); 2274 } 2275 } 2276 2277 // Extra test cases for short buffer 2278 int32_t len; 2279 char region2[2]; 2280 sts = U_ZERO_ERROR; 2281 2282 len = TimeZone::getRegion("America/New_York", region2, sizeof(region2), sts); 2283 if (sts == U_ILLEGAL_ARGUMENT_ERROR) { 2284 dataerrln("Error calling TimeZone::getRegion"); 2285 } else { 2286 if (sts != U_STRING_NOT_TERMINATED_WARNING) { 2287 errln("Expected U_STRING_NOT_TERMINATED_WARNING"); 2288 } 2289 if (len != 2) { // length of "US" 2290 errln("Incorrect result length"); 2291 } 2292 if (uprv_strncmp(region2, "US", 2) != 0) { 2293 errln("Incorrect result"); 2294 } 2295 } 2296 2297 char region1[1]; 2298 sts = U_ZERO_ERROR; 2299 2300 len = TimeZone::getRegion("America/Chicago", region1, sizeof(region1), sts); 2301 if (sts == U_ILLEGAL_ARGUMENT_ERROR) { 2302 dataerrln("Error calling TimeZone::getRegion"); 2303 } else { 2304 if (sts != U_BUFFER_OVERFLOW_ERROR) { 2305 errln("Expected U_BUFFER_OVERFLOW_ERROR"); 2306 } 2307 if (len != 2) { // length of "US" 2308 errln("Incorrect result length"); 2309 } 2310 } 2311 } 2312 2313 void TimeZoneTest::TestGetUnknown() { 2314 const TimeZone &unknown = TimeZone::getUnknown(); 2315 UnicodeString expectedID = UNICODE_STRING_SIMPLE("Etc/Unknown"); 2316 UnicodeString id; 2317 assertEquals("getUnknown() wrong ID", expectedID, unknown.getID(id)); 2318 assertTrue("getUnknown() wrong offset", 0 == unknown.getRawOffset()); 2319 assertFalse("getUnknown() uses DST", unknown.useDaylightTime()); 2320 } 2321 2322 void TimeZoneTest::TestGetWindowsID(void) { 2323 static const struct { 2324 const char *id; 2325 const char *winid; 2326 } TESTDATA[] = { 2327 {"America/New_York", "Eastern Standard Time"}, 2328 {"America/Montreal", "Eastern Standard Time"}, 2329 {"America/Los_Angeles", "Pacific Standard Time"}, 2330 {"America/Vancouver", "Pacific Standard Time"}, 2331 {"Asia/Shanghai", "China Standard Time"}, 2332 {"Asia/Chongqing", "China Standard Time"}, 2333 {"America/Indianapolis", "US Eastern Standard Time"}, // CLDR canonical name 2334 {"America/Indiana/Indianapolis", "US Eastern Standard Time"}, // tzdb canonical name 2335 {"Asia/Khandyga", "Yakutsk Standard Time"}, 2336 {"Australia/Eucla", ""}, // No Windows ID mapping 2337 {"Bogus", ""}, 2338 {0, 0}, 2339 }; 2340 2341 for (int32_t i = 0; TESTDATA[i].id != 0; i++) { 2342 UErrorCode sts = U_ZERO_ERROR; 2343 UnicodeString windowsID; 2344 2345 TimeZone::getWindowsID(UnicodeString(TESTDATA[i].id), windowsID, sts); 2346 assertSuccess(TESTDATA[i].id, sts); 2347 assertEquals(TESTDATA[i].id, UnicodeString(TESTDATA[i].winid), windowsID, TRUE); 2348 } 2349 } 2350 2351 void TimeZoneTest::TestGetIDForWindowsID(void) { 2352 static const struct { 2353 const char *winid; 2354 const char *region; 2355 const char *id; 2356 } TESTDATA[] = { 2357 {"Eastern Standard Time", 0, "America/New_York"}, 2358 {"Eastern Standard Time", "US", "America/New_York"}, 2359 {"Eastern Standard Time", "CA", "America/Toronto"}, 2360 {"Eastern Standard Time", "CN", "America/New_York"}, 2361 {"China Standard Time", 0, "Asia/Shanghai"}, 2362 {"China Standard Time", "CN", "Asia/Shanghai"}, 2363 {"China Standard Time", "HK", "Asia/Hong_Kong"}, 2364 {"Mid-Atlantic Standard Time", 0, ""}, // No tz database mapping 2365 {"Bogus", 0, ""}, 2366 {0, 0, 0}, 2367 }; 2368 2369 for (int32_t i = 0; TESTDATA[i].winid != 0; i++) { 2370 UErrorCode sts = U_ZERO_ERROR; 2371 UnicodeString id; 2372 2373 TimeZone::getIDForWindowsID(UnicodeString(TESTDATA[i].winid), TESTDATA[i].region, 2374 id, sts); 2375 assertSuccess(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, sts); 2376 assertEquals(UnicodeString(TESTDATA[i].winid) + "/" + TESTDATA[i].region, TESTDATA[i].id, id, TRUE); 2377 } 2378 } 2379 2380 #endif /* #if !UCONFIG_NO_FORMATTING */ 2381