1 /* 2 ********************************************************************** 3 * Copyright (c) 2003-2013, International Business Machines 4 * Corporation and others. All Rights Reserved. 5 ********************************************************************** 6 * Author: Alan Liu 7 * Created: July 21 2003 8 * Since: ICU 2.8 9 ********************************************************************** 10 */ 11 12 #include "utypeinfo.h" // for 'typeid' to work 13 14 #include "olsontz.h" 15 16 #if !UCONFIG_NO_FORMATTING 17 18 #include "unicode/ures.h" 19 #include "unicode/simpletz.h" 20 #include "unicode/gregocal.h" 21 #include "gregoimp.h" 22 #include "cmemory.h" 23 #include "uassert.h" 24 #include "uvector.h" 25 #include <float.h> // DBL_MAX 26 #include "uresimp.h" // struct UResourceBundle 27 #include "zonemeta.h" 28 #include "umutex.h" 29 30 #ifdef U_DEBUG_TZ 31 # include <stdio.h> 32 # include "uresimp.h" // for debugging 33 34 static void debug_tz_loc(const char *f, int32_t l) 35 { 36 fprintf(stderr, "%s:%d: ", f, l); 37 } 38 39 static void debug_tz_msg(const char *pat, ...) 40 { 41 va_list ap; 42 va_start(ap, pat); 43 vfprintf(stderr, pat, ap); 44 fflush(stderr); 45 } 46 // must use double parens, i.e.: U_DEBUG_TZ_MSG(("four is: %d",4)); 47 #define U_DEBUG_TZ_MSG(x) {debug_tz_loc(__FILE__,__LINE__);debug_tz_msg x;} 48 #else 49 #define U_DEBUG_TZ_MSG(x) 50 #endif 51 52 static UBool arrayEqual(const void *a1, const void *a2, int32_t size) { 53 if (a1 == NULL && a2 == NULL) { 54 return TRUE; 55 } 56 if ((a1 != NULL && a2 == NULL) || (a1 == NULL && a2 != NULL)) { 57 return FALSE; 58 } 59 if (a1 == a2) { 60 return TRUE; 61 } 62 63 return (uprv_memcmp(a1, a2, size) == 0); 64 } 65 66 U_NAMESPACE_BEGIN 67 68 #define kTRANS "trans" 69 #define kTRANSPRE32 "transPre32" 70 #define kTRANSPOST32 "transPost32" 71 #define kTYPEOFFSETS "typeOffsets" 72 #define kTYPEMAP "typeMap" 73 #define kLINKS "links" 74 #define kFINALRULE "finalRule" 75 #define kFINALRAW "finalRaw" 76 #define kFINALYEAR "finalYear" 77 78 #define SECONDS_PER_DAY (24*60*60) 79 80 static const int32_t ZEROS[] = {0,0}; 81 82 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OlsonTimeZone) 83 84 /** 85 * Default constructor. Creates a time zone with an empty ID and 86 * a fixed GMT offset of zero. 87 */ 88 /*OlsonTimeZone::OlsonTimeZone() : finalYear(INT32_MAX), finalMillis(DBL_MAX), finalZone(0), transitionRulesInitialized(FALSE) { 89 clearTransitionRules(); 90 constructEmpty(); 91 }*/ 92 93 /** 94 * Construct a GMT+0 zone with no transitions. This is done when a 95 * constructor fails so the resultant object is well-behaved. 96 */ 97 void OlsonTimeZone::constructEmpty() { 98 canonicalID = NULL; 99 100 transitionCountPre32 = transitionCount32 = transitionCountPost32 = 0; 101 transitionTimesPre32 = transitionTimes32 = transitionTimesPost32 = NULL; 102 103 typeMapData = NULL; 104 105 typeCount = 1; 106 typeOffsets = ZEROS; 107 108 finalZone = NULL; 109 } 110 111 /** 112 * Construct from a resource bundle 113 * @param top the top-level zoneinfo resource bundle. This is used 114 * to lookup the rule that `res' may refer to, if there is one. 115 * @param res the resource bundle of the zone to be constructed 116 * @param ec input-output error code 117 */ 118 OlsonTimeZone::OlsonTimeZone(const UResourceBundle* top, 119 const UResourceBundle* res, 120 const UnicodeString& tzid, 121 UErrorCode& ec) : 122 BasicTimeZone(tzid), finalZone(NULL), transitionRulesInitialized(FALSE) 123 { 124 clearTransitionRules(); 125 U_DEBUG_TZ_MSG(("OlsonTimeZone(%s)\n", ures_getKey((UResourceBundle*)res))); 126 if ((top == NULL || res == NULL) && U_SUCCESS(ec)) { 127 ec = U_ILLEGAL_ARGUMENT_ERROR; 128 } 129 if (U_SUCCESS(ec)) { 130 // TODO -- clean up -- Doesn't work if res points to an alias 131 // // TODO remove nonconst casts below when ures_* API is fixed 132 // setID(ures_getKey((UResourceBundle*) res)); // cast away const 133 134 int32_t len; 135 UResourceBundle r; 136 ures_initStackObject(&r); 137 138 // Pre-32bit second transitions 139 ures_getByKey(res, kTRANSPRE32, &r, &ec); 140 transitionTimesPre32 = ures_getIntVector(&r, &len, &ec); 141 transitionCountPre32 = len >> 1; 142 if (ec == U_MISSING_RESOURCE_ERROR) { 143 // No pre-32bit transitions 144 transitionTimesPre32 = NULL; 145 transitionCountPre32 = 0; 146 ec = U_ZERO_ERROR; 147 } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { 148 ec = U_INVALID_FORMAT_ERROR; 149 } 150 151 // 32bit second transitions 152 ures_getByKey(res, kTRANS, &r, &ec); 153 transitionTimes32 = ures_getIntVector(&r, &len, &ec); 154 transitionCount32 = len; 155 if (ec == U_MISSING_RESOURCE_ERROR) { 156 // No 32bit transitions 157 transitionTimes32 = NULL; 158 transitionCount32 = 0; 159 ec = U_ZERO_ERROR; 160 } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF)) { 161 ec = U_INVALID_FORMAT_ERROR; 162 } 163 164 // Post-32bit second transitions 165 ures_getByKey(res, kTRANSPOST32, &r, &ec); 166 transitionTimesPost32 = ures_getIntVector(&r, &len, &ec); 167 transitionCountPost32 = len >> 1; 168 if (ec == U_MISSING_RESOURCE_ERROR) { 169 // No pre-32bit transitions 170 transitionTimesPost32 = NULL; 171 transitionCountPost32 = 0; 172 ec = U_ZERO_ERROR; 173 } else if (U_SUCCESS(ec) && (len < 0 || len > 0x7FFF || (len & 1) != 0) /* len must be even */) { 174 ec = U_INVALID_FORMAT_ERROR; 175 } 176 177 // Type offsets list must be of even size, with size >= 2 178 ures_getByKey(res, kTYPEOFFSETS, &r, &ec); 179 typeOffsets = ures_getIntVector(&r, &len, &ec); 180 if (U_SUCCESS(ec) && (len < 2 || len > 0x7FFE || (len & 1) != 0)) { 181 ec = U_INVALID_FORMAT_ERROR; 182 } 183 typeCount = (int16_t) len >> 1; 184 185 // Type map data must be of the same size as the transition count 186 typeMapData = NULL; 187 if (transitionCount() > 0) { 188 ures_getByKey(res, kTYPEMAP, &r, &ec); 189 typeMapData = ures_getBinary(&r, &len, &ec); 190 if (ec == U_MISSING_RESOURCE_ERROR) { 191 // no type mapping data 192 ec = U_INVALID_FORMAT_ERROR; 193 } else if (U_SUCCESS(ec) && len != transitionCount()) { 194 ec = U_INVALID_FORMAT_ERROR; 195 } 196 } 197 198 // Process final rule and data, if any 199 const UChar *ruleIdUStr = ures_getStringByKey(res, kFINALRULE, &len, &ec); 200 ures_getByKey(res, kFINALRAW, &r, &ec); 201 int32_t ruleRaw = ures_getInt(&r, &ec); 202 ures_getByKey(res, kFINALYEAR, &r, &ec); 203 int32_t ruleYear = ures_getInt(&r, &ec); 204 if (U_SUCCESS(ec)) { 205 UnicodeString ruleID(TRUE, ruleIdUStr, len); 206 UResourceBundle *rule = TimeZone::loadRule(top, ruleID, NULL, ec); 207 const int32_t *ruleData = ures_getIntVector(rule, &len, &ec); 208 if (U_SUCCESS(ec) && len == 11) { 209 UnicodeString emptyStr; 210 finalZone = new SimpleTimeZone( 211 ruleRaw * U_MILLIS_PER_SECOND, 212 emptyStr, 213 (int8_t)ruleData[0], (int8_t)ruleData[1], (int8_t)ruleData[2], 214 ruleData[3] * U_MILLIS_PER_SECOND, 215 (SimpleTimeZone::TimeMode) ruleData[4], 216 (int8_t)ruleData[5], (int8_t)ruleData[6], (int8_t)ruleData[7], 217 ruleData[8] * U_MILLIS_PER_SECOND, 218 (SimpleTimeZone::TimeMode) ruleData[9], 219 ruleData[10] * U_MILLIS_PER_SECOND, ec); 220 if (finalZone == NULL) { 221 ec = U_MEMORY_ALLOCATION_ERROR; 222 } else { 223 finalStartYear = ruleYear; 224 225 // Note: Setting finalStartYear to the finalZone is problematic. When a date is around 226 // year boundary, SimpleTimeZone may return false result when DST is observed at the 227 // beginning of year. We could apply safe margin (day or two), but when one of recurrent 228 // rules falls around year boundary, it could return false result. Without setting the 229 // start year, finalZone works fine around the year boundary of the start year. 230 231 // finalZone->setStartYear(finalStartYear); 232 233 234 // Compute the millis for Jan 1, 0:00 GMT of the finalYear 235 236 // Note: finalStartMillis is used for detecting either if 237 // historic transition data or finalZone to be used. In an 238 // extreme edge case - for example, two transitions fall into 239 // small windows of time around the year boundary, this may 240 // result incorrect offset computation. But I think it will 241 // never happen practically. Yoshito - Feb 20, 2010 242 finalStartMillis = Grego::fieldsToDay(finalStartYear, 0, 1) * U_MILLIS_PER_DAY; 243 } 244 } else { 245 ec = U_INVALID_FORMAT_ERROR; 246 } 247 ures_close(rule); 248 } else if (ec == U_MISSING_RESOURCE_ERROR) { 249 // No final zone 250 ec = U_ZERO_ERROR; 251 } 252 ures_close(&r); 253 254 // initialize canonical ID 255 canonicalID = ZoneMeta::getCanonicalCLDRID(tzid, ec); 256 } 257 258 if (U_FAILURE(ec)) { 259 constructEmpty(); 260 } 261 } 262 263 /** 264 * Copy constructor 265 */ 266 OlsonTimeZone::OlsonTimeZone(const OlsonTimeZone& other) : 267 BasicTimeZone(other), finalZone(0) { 268 *this = other; 269 } 270 271 /** 272 * Assignment operator 273 */ 274 OlsonTimeZone& OlsonTimeZone::operator=(const OlsonTimeZone& other) { 275 canonicalID = other.canonicalID; 276 277 transitionTimesPre32 = other.transitionTimesPre32; 278 transitionTimes32 = other.transitionTimes32; 279 transitionTimesPost32 = other.transitionTimesPost32; 280 281 transitionCountPre32 = other.transitionCountPre32; 282 transitionCount32 = other.transitionCount32; 283 transitionCountPost32 = other.transitionCountPost32; 284 285 typeCount = other.typeCount; 286 typeOffsets = other.typeOffsets; 287 typeMapData = other.typeMapData; 288 289 delete finalZone; 290 finalZone = (other.finalZone != 0) ? 291 (SimpleTimeZone*) other.finalZone->clone() : 0; 292 293 finalStartYear = other.finalStartYear; 294 finalStartMillis = other.finalStartMillis; 295 296 clearTransitionRules(); 297 298 return *this; 299 } 300 301 /** 302 * Destructor 303 */ 304 OlsonTimeZone::~OlsonTimeZone() { 305 deleteTransitionRules(); 306 delete finalZone; 307 } 308 309 /** 310 * Returns true if the two TimeZone objects are equal. 311 */ 312 UBool OlsonTimeZone::operator==(const TimeZone& other) const { 313 return ((this == &other) || 314 (typeid(*this) == typeid(other) && 315 TimeZone::operator==(other) && 316 hasSameRules(other))); 317 } 318 319 /** 320 * TimeZone API. 321 */ 322 TimeZone* OlsonTimeZone::clone() const { 323 return new OlsonTimeZone(*this); 324 } 325 326 /** 327 * TimeZone API. 328 */ 329 int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, 330 int32_t dom, uint8_t dow, 331 int32_t millis, UErrorCode& ec) const { 332 if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { 333 if (U_SUCCESS(ec)) { 334 ec = U_ILLEGAL_ARGUMENT_ERROR; 335 } 336 return 0; 337 } else { 338 return getOffset(era, year, month, dom, dow, millis, 339 Grego::monthLength(year, month), 340 ec); 341 } 342 } 343 344 /** 345 * TimeZone API. 346 */ 347 int32_t OlsonTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, 348 int32_t dom, uint8_t dow, 349 int32_t millis, int32_t monthLength, 350 UErrorCode& ec) const { 351 if (U_FAILURE(ec)) { 352 return 0; 353 } 354 355 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) 356 || month < UCAL_JANUARY 357 || month > UCAL_DECEMBER 358 || dom < 1 359 || dom > monthLength 360 || dow < UCAL_SUNDAY 361 || dow > UCAL_SATURDAY 362 || millis < 0 363 || millis >= U_MILLIS_PER_DAY 364 || monthLength < 28 365 || monthLength > 31) { 366 ec = U_ILLEGAL_ARGUMENT_ERROR; 367 return 0; 368 } 369 370 if (era == GregorianCalendar::BC) { 371 year = -year; 372 } 373 374 if (finalZone != NULL && year >= finalStartYear) { 375 return finalZone->getOffset(era, year, month, dom, dow, 376 millis, monthLength, ec); 377 } 378 379 // Compute local epoch millis from input fields 380 UDate date = (UDate)(Grego::fieldsToDay(year, month, dom) * U_MILLIS_PER_DAY + millis); 381 int32_t rawoff, dstoff; 382 getHistoricalOffset(date, TRUE, kDaylight, kStandard, rawoff, dstoff); 383 return rawoff + dstoff; 384 } 385 386 /** 387 * TimeZone API. 388 */ 389 void OlsonTimeZone::getOffset(UDate date, UBool local, int32_t& rawoff, 390 int32_t& dstoff, UErrorCode& ec) const { 391 if (U_FAILURE(ec)) { 392 return; 393 } 394 if (finalZone != NULL && date >= finalStartMillis) { 395 finalZone->getOffset(date, local, rawoff, dstoff, ec); 396 } else { 397 getHistoricalOffset(date, local, kFormer, kLatter, rawoff, dstoff); 398 } 399 } 400 401 void 402 OlsonTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, 403 int32_t& rawoff, int32_t& dstoff, UErrorCode& ec) const { 404 if (U_FAILURE(ec)) { 405 return; 406 } 407 if (finalZone != NULL && date >= finalStartMillis) { 408 finalZone->getOffsetFromLocal(date, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff, ec); 409 } else { 410 getHistoricalOffset(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawoff, dstoff); 411 } 412 } 413 414 415 /** 416 * TimeZone API. 417 */ 418 void OlsonTimeZone::setRawOffset(int32_t /*offsetMillis*/) { 419 // We don't support this operation, since OlsonTimeZones are 420 // immutable (except for the ID, which is in the base class). 421 422 // Nothing to do! 423 } 424 425 /** 426 * TimeZone API. 427 */ 428 int32_t OlsonTimeZone::getRawOffset() const { 429 UErrorCode ec = U_ZERO_ERROR; 430 int32_t raw, dst; 431 getOffset((double) uprv_getUTCtime() * U_MILLIS_PER_SECOND, 432 FALSE, raw, dst, ec); 433 return raw; 434 } 435 436 #if defined U_DEBUG_TZ 437 void printTime(double ms) { 438 int32_t year, month, dom, dow; 439 double millis=0; 440 double days = ClockMath::floorDivide(((double)ms), (double)U_MILLIS_PER_DAY, millis); 441 442 Grego::dayToFields(days, year, month, dom, dow); 443 U_DEBUG_TZ_MSG((" getHistoricalOffset: time %.1f (%04d.%02d.%02d+%.1fh)\n", ms, 444 year, month+1, dom, (millis/kOneHour))); 445 } 446 #endif 447 448 int64_t 449 OlsonTimeZone::transitionTimeInSeconds(int16_t transIdx) const { 450 U_ASSERT(transIdx >= 0 && transIdx < transitionCount()); 451 452 if (transIdx < transitionCountPre32) { 453 return (((int64_t)((uint32_t)transitionTimesPre32[transIdx << 1])) << 32) 454 | ((int64_t)((uint32_t)transitionTimesPre32[(transIdx << 1) + 1])); 455 } 456 457 transIdx -= transitionCountPre32; 458 if (transIdx < transitionCount32) { 459 return (int64_t)transitionTimes32[transIdx]; 460 } 461 462 transIdx -= transitionCount32; 463 return (((int64_t)((uint32_t)transitionTimesPost32[transIdx << 1])) << 32) 464 | ((int64_t)((uint32_t)transitionTimesPost32[(transIdx << 1) + 1])); 465 } 466 467 void 468 OlsonTimeZone::getHistoricalOffset(UDate date, UBool local, 469 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, 470 int32_t& rawoff, int32_t& dstoff) const { 471 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst)\n", 472 date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt)); 473 #if defined U_DEBUG_TZ 474 printTime(date*1000.0); 475 #endif 476 int16_t transCount = transitionCount(); 477 478 if (transCount > 0) { 479 double sec = uprv_floor(date / U_MILLIS_PER_SECOND); 480 if (!local && sec < transitionTimeInSeconds(0)) { 481 // Before the first transition time 482 rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; 483 dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; 484 } else { 485 // Linear search from the end is the fastest approach, since 486 // most lookups will happen at/near the end. 487 int16_t transIdx; 488 for (transIdx = transCount - 1; transIdx >= 0; transIdx--) { 489 int64_t transition = transitionTimeInSeconds(transIdx); 490 491 if (local) { 492 int32_t offsetBefore = zoneOffsetAt(transIdx - 1); 493 UBool dstBefore = dstOffsetAt(transIdx - 1) != 0; 494 495 int32_t offsetAfter = zoneOffsetAt(transIdx); 496 UBool dstAfter = dstOffsetAt(transIdx) != 0; 497 498 UBool dstToStd = dstBefore && !dstAfter; 499 UBool stdToDst = !dstBefore && dstAfter; 500 501 if (offsetAfter - offsetBefore >= 0) { 502 // Positive transition, which makes a non-existing local time range 503 if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) 504 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { 505 transition += offsetBefore; 506 } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) 507 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { 508 transition += offsetAfter; 509 } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { 510 transition += offsetBefore; 511 } else { 512 // Interprets the time with rule before the transition, 513 // default for non-existing time range 514 transition += offsetAfter; 515 } 516 } else { 517 // Negative transition, which makes a duplicated local time range 518 if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) 519 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { 520 transition += offsetAfter; 521 } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) 522 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { 523 transition += offsetBefore; 524 } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { 525 transition += offsetBefore; 526 } else { 527 // Interprets the time with rule after the transition, 528 // default for duplicated local time range 529 transition += offsetAfter; 530 } 531 } 532 } 533 if (sec >= transition) { 534 break; 535 } 536 } 537 // transIdx could be -1 when local=true 538 rawoff = rawOffsetAt(transIdx) * U_MILLIS_PER_SECOND; 539 dstoff = dstOffsetAt(transIdx) * U_MILLIS_PER_SECOND; 540 } 541 } else { 542 // No transitions, single pair of offsets only 543 rawoff = initialRawOffset() * U_MILLIS_PER_SECOND; 544 dstoff = initialDstOffset() * U_MILLIS_PER_SECOND; 545 } 546 U_DEBUG_TZ_MSG(("getHistoricalOffset(%.1f, %s, %d, %d, raw, dst) - raw=%d, dst=%d\n", 547 date, local?"T":"F", NonExistingTimeOpt, DuplicatedTimeOpt, rawoff, dstoff)); 548 } 549 550 /** 551 * TimeZone API. 552 */ 553 UBool OlsonTimeZone::useDaylightTime() const { 554 // If DST was observed in 1942 (for example) but has never been 555 // observed from 1943 to the present, most clients will expect 556 // this method to return FALSE. This method determines whether 557 // DST is in use in the current year (at any point in the year) 558 // and returns TRUE if so. 559 560 UDate current = uprv_getUTCtime(); 561 if (finalZone != NULL && current >= finalStartMillis) { 562 return finalZone->useDaylightTime(); 563 } 564 565 int32_t year, month, dom, dow, doy, mid; 566 Grego::timeToFields(current, year, month, dom, dow, doy, mid); 567 568 // Find start of this year, and start of next year 569 double start = Grego::fieldsToDay(year, 0, 1) * SECONDS_PER_DAY; 570 double limit = Grego::fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY; 571 572 // Return TRUE if DST is observed at any time during the current 573 // year. 574 for (int16_t i = 0; i < transitionCount(); ++i) { 575 double transition = (double)transitionTimeInSeconds(i); 576 if (transition >= limit) { 577 break; 578 } 579 if ((transition >= start && dstOffsetAt(i) != 0) 580 || (transition > start && dstOffsetAt(i - 1) != 0)) { 581 return TRUE; 582 } 583 } 584 return FALSE; 585 } 586 int32_t 587 OlsonTimeZone::getDSTSavings() const{ 588 if (finalZone != NULL){ 589 return finalZone->getDSTSavings(); 590 } 591 return TimeZone::getDSTSavings(); 592 } 593 /** 594 * TimeZone API. 595 */ 596 UBool OlsonTimeZone::inDaylightTime(UDate date, UErrorCode& ec) const { 597 int32_t raw, dst; 598 getOffset(date, FALSE, raw, dst, ec); 599 return dst != 0; 600 } 601 602 UBool 603 OlsonTimeZone::hasSameRules(const TimeZone &other) const { 604 if (this == &other) { 605 return TRUE; 606 } 607 const OlsonTimeZone* z = dynamic_cast<const OlsonTimeZone*>(&other); 608 if (z == NULL) { 609 return FALSE; 610 } 611 612 // [sic] pointer comparison: typeMapData points into 613 // memory-mapped or DLL space, so if two zones have the same 614 // pointer, they are equal. 615 if (typeMapData == z->typeMapData) { 616 return TRUE; 617 } 618 619 // If the pointers are not equal, the zones may still 620 // be equal if their rules and transitions are equal 621 if ((finalZone == NULL && z->finalZone != NULL) 622 || (finalZone != NULL && z->finalZone == NULL) 623 || (finalZone != NULL && z->finalZone != NULL && *finalZone != *z->finalZone)) { 624 return FALSE; 625 } 626 627 if (finalZone != NULL) { 628 if (finalStartYear != z->finalStartYear || finalStartMillis != z->finalStartMillis) { 629 return FALSE; 630 } 631 } 632 if (typeCount != z->typeCount 633 || transitionCountPre32 != z->transitionCountPre32 634 || transitionCount32 != z->transitionCount32 635 || transitionCountPost32 != z->transitionCountPost32) { 636 return FALSE; 637 } 638 639 return 640 arrayEqual(transitionTimesPre32, z->transitionTimesPre32, sizeof(transitionTimesPre32[0]) * transitionCountPre32 << 1) 641 && arrayEqual(transitionTimes32, z->transitionTimes32, sizeof(transitionTimes32[0]) * transitionCount32) 642 && arrayEqual(transitionTimesPost32, z->transitionTimesPost32, sizeof(transitionTimesPost32[0]) * transitionCountPost32 << 1) 643 && arrayEqual(typeOffsets, z->typeOffsets, sizeof(typeOffsets[0]) * typeCount << 1) 644 && arrayEqual(typeMapData, z->typeMapData, sizeof(typeMapData[0]) * transitionCount()); 645 } 646 647 void 648 OlsonTimeZone::clearTransitionRules(void) { 649 initialRule = NULL; 650 firstTZTransition = NULL; 651 firstFinalTZTransition = NULL; 652 historicRules = NULL; 653 historicRuleCount = 0; 654 finalZoneWithStartYear = NULL; 655 firstTZTransitionIdx = 0; 656 transitionRulesInitialized = FALSE; 657 } 658 659 void 660 OlsonTimeZone::deleteTransitionRules(void) { 661 if (initialRule != NULL) { 662 delete initialRule; 663 } 664 if (firstTZTransition != NULL) { 665 delete firstTZTransition; 666 } 667 if (firstFinalTZTransition != NULL) { 668 delete firstFinalTZTransition; 669 } 670 if (finalZoneWithStartYear != NULL) { 671 delete finalZoneWithStartYear; 672 } 673 if (historicRules != NULL) { 674 for (int i = 0; i < historicRuleCount; i++) { 675 if (historicRules[i] != NULL) { 676 delete historicRules[i]; 677 } 678 } 679 uprv_free(historicRules); 680 } 681 clearTransitionRules(); 682 } 683 684 /* 685 * Lazy transition rules initializer 686 */ 687 static UMutex gLock = U_MUTEX_INITIALIZER; 688 689 void 690 OlsonTimeZone::checkTransitionRules(UErrorCode& status) const { 691 if (U_FAILURE(status)) { 692 return; 693 } 694 UBool initialized; 695 UMTX_CHECK(&gLock, transitionRulesInitialized, initialized); 696 if (!initialized) { 697 umtx_lock(&gLock); 698 if (!transitionRulesInitialized) { 699 OlsonTimeZone *ncThis = const_cast<OlsonTimeZone*>(this); 700 ncThis->initTransitionRules(status); 701 } 702 umtx_unlock(&gLock); 703 } 704 } 705 706 void 707 OlsonTimeZone::initTransitionRules(UErrorCode& status) { 708 if(U_FAILURE(status)) { 709 return; 710 } 711 if (transitionRulesInitialized) { 712 return; 713 } 714 deleteTransitionRules(); 715 UnicodeString tzid; 716 getID(tzid); 717 718 UnicodeString stdName = tzid + UNICODE_STRING_SIMPLE("(STD)"); 719 UnicodeString dstName = tzid + UNICODE_STRING_SIMPLE("(DST)"); 720 721 int32_t raw, dst; 722 723 // Create initial rule 724 raw = initialRawOffset() * U_MILLIS_PER_SECOND; 725 dst = initialDstOffset() * U_MILLIS_PER_SECOND; 726 initialRule = new InitialTimeZoneRule((dst == 0 ? stdName : dstName), raw, dst); 727 // Check to make sure initialRule was created 728 if (initialRule == NULL) { 729 status = U_MEMORY_ALLOCATION_ERROR; 730 deleteTransitionRules(); 731 return; 732 } 733 734 int32_t transCount = transitionCount(); 735 if (transCount > 0) { 736 int16_t transitionIdx, typeIdx; 737 738 // We probably no longer need to check the first "real" transition 739 // here, because the new tzcode remove such transitions already. 740 // For now, keeping this code for just in case. Feb 19, 2010 Yoshito 741 firstTZTransitionIdx = 0; 742 for (transitionIdx = 0; transitionIdx < transCount; transitionIdx++) { 743 if (typeMapData[transitionIdx] != 0) { // type 0 is the initial type 744 break; 745 } 746 firstTZTransitionIdx++; 747 } 748 if (transitionIdx == transCount) { 749 // Actually no transitions... 750 } else { 751 // Build historic rule array 752 UDate* times = (UDate*)uprv_malloc(sizeof(UDate)*transCount); /* large enough to store all transition times */ 753 if (times == NULL) { 754 status = U_MEMORY_ALLOCATION_ERROR; 755 deleteTransitionRules(); 756 return; 757 } 758 for (typeIdx = 0; typeIdx < typeCount; typeIdx++) { 759 // Gather all start times for each pair of offsets 760 int32_t nTimes = 0; 761 for (transitionIdx = firstTZTransitionIdx; transitionIdx < transCount; transitionIdx++) { 762 if (typeIdx == (int16_t)typeMapData[transitionIdx]) { 763 UDate tt = (UDate)transitionTime(transitionIdx); 764 if (finalZone == NULL || tt <= finalStartMillis) { 765 // Exclude transitions after finalMillis 766 times[nTimes++] = tt; 767 } 768 } 769 } 770 if (nTimes > 0) { 771 // Create a TimeArrayTimeZoneRule 772 raw = typeOffsets[typeIdx << 1] * U_MILLIS_PER_SECOND; 773 dst = typeOffsets[(typeIdx << 1) + 1] * U_MILLIS_PER_SECOND; 774 if (historicRules == NULL) { 775 historicRuleCount = typeCount; 776 historicRules = (TimeArrayTimeZoneRule**)uprv_malloc(sizeof(TimeArrayTimeZoneRule*)*historicRuleCount); 777 if (historicRules == NULL) { 778 status = U_MEMORY_ALLOCATION_ERROR; 779 deleteTransitionRules(); 780 uprv_free(times); 781 return; 782 } 783 for (int i = 0; i < historicRuleCount; i++) { 784 // Initialize TimeArrayTimeZoneRule pointers as NULL 785 historicRules[i] = NULL; 786 } 787 } 788 historicRules[typeIdx] = new TimeArrayTimeZoneRule((dst == 0 ? stdName : dstName), 789 raw, dst, times, nTimes, DateTimeRule::UTC_TIME); 790 // Check for memory allocation error 791 if (historicRules[typeIdx] == NULL) { 792 status = U_MEMORY_ALLOCATION_ERROR; 793 deleteTransitionRules(); 794 return; 795 } 796 } 797 } 798 uprv_free(times); 799 800 // Create initial transition 801 typeIdx = (int16_t)typeMapData[firstTZTransitionIdx]; 802 firstTZTransition = new TimeZoneTransition((UDate)transitionTime(firstTZTransitionIdx), 803 *initialRule, *historicRules[typeIdx]); 804 // Check to make sure firstTZTransition was created. 805 if (firstTZTransition == NULL) { 806 status = U_MEMORY_ALLOCATION_ERROR; 807 deleteTransitionRules(); 808 return; 809 } 810 } 811 } 812 if (finalZone != NULL) { 813 // Get the first occurence of final rule starts 814 UDate startTime = (UDate)finalStartMillis; 815 TimeZoneRule *firstFinalRule = NULL; 816 817 if (finalZone->useDaylightTime()) { 818 /* 819 * Note: When an OlsonTimeZone is constructed, we should set the final year 820 * as the start year of finalZone. However, the bounday condition used for 821 * getting offset from finalZone has some problems. 822 * For now, we do not set the valid start year when the construction time 823 * and create a clone and set the start year when extracting rules. 824 */ 825 finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone(); 826 // Check to make sure finalZone was actually cloned. 827 if (finalZoneWithStartYear == NULL) { 828 status = U_MEMORY_ALLOCATION_ERROR; 829 deleteTransitionRules(); 830 return; 831 } 832 finalZoneWithStartYear->setStartYear(finalStartYear); 833 834 TimeZoneTransition tzt; 835 finalZoneWithStartYear->getNextTransition(startTime, false, tzt); 836 firstFinalRule = tzt.getTo()->clone(); 837 // Check to make sure firstFinalRule received proper clone. 838 if (firstFinalRule == NULL) { 839 status = U_MEMORY_ALLOCATION_ERROR; 840 deleteTransitionRules(); 841 return; 842 } 843 startTime = tzt.getTime(); 844 } else { 845 // final rule with no transitions 846 finalZoneWithStartYear = (SimpleTimeZone*)finalZone->clone(); 847 // Check to make sure finalZone was actually cloned. 848 if (finalZoneWithStartYear == NULL) { 849 status = U_MEMORY_ALLOCATION_ERROR; 850 deleteTransitionRules(); 851 return; 852 } 853 finalZone->getID(tzid); 854 firstFinalRule = new TimeArrayTimeZoneRule(tzid, 855 finalZone->getRawOffset(), 0, &startTime, 1, DateTimeRule::UTC_TIME); 856 // Check firstFinalRule was properly created. 857 if (firstFinalRule == NULL) { 858 status = U_MEMORY_ALLOCATION_ERROR; 859 deleteTransitionRules(); 860 return; 861 } 862 } 863 TimeZoneRule *prevRule = NULL; 864 if (transCount > 0) { 865 prevRule = historicRules[typeMapData[transCount - 1]]; 866 } 867 if (prevRule == NULL) { 868 // No historic transitions, but only finalZone available 869 prevRule = initialRule; 870 } 871 firstFinalTZTransition = new TimeZoneTransition(); 872 // Check to make sure firstFinalTZTransition was created before dereferencing 873 if (firstFinalTZTransition == NULL) { 874 status = U_MEMORY_ALLOCATION_ERROR; 875 deleteTransitionRules(); 876 return; 877 } 878 firstFinalTZTransition->setTime(startTime); 879 firstFinalTZTransition->adoptFrom(prevRule->clone()); 880 firstFinalTZTransition->adoptTo(firstFinalRule); 881 } 882 transitionRulesInitialized = TRUE; 883 } 884 885 UBool 886 OlsonTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { 887 UErrorCode status = U_ZERO_ERROR; 888 checkTransitionRules(status); 889 if (U_FAILURE(status)) { 890 return FALSE; 891 } 892 893 if (finalZone != NULL) { 894 if (inclusive && base == firstFinalTZTransition->getTime()) { 895 result = *firstFinalTZTransition; 896 return TRUE; 897 } else if (base >= firstFinalTZTransition->getTime()) { 898 if (finalZone->useDaylightTime()) { 899 //return finalZone->getNextTransition(base, inclusive, result); 900 return finalZoneWithStartYear->getNextTransition(base, inclusive, result); 901 } else { 902 // No more transitions 903 return FALSE; 904 } 905 } 906 } 907 if (historicRules != NULL) { 908 // Find a historical transition 909 int16_t transCount = transitionCount(); 910 int16_t ttidx = transCount - 1; 911 for (; ttidx >= firstTZTransitionIdx; ttidx--) { 912 UDate t = (UDate)transitionTime(ttidx); 913 if (base > t || (!inclusive && base == t)) { 914 break; 915 } 916 } 917 if (ttidx == transCount - 1) { 918 if (firstFinalTZTransition != NULL) { 919 result = *firstFinalTZTransition; 920 return TRUE; 921 } else { 922 return FALSE; 923 } 924 } else if (ttidx < firstTZTransitionIdx) { 925 result = *firstTZTransition; 926 return TRUE; 927 } else { 928 // Create a TimeZoneTransition 929 TimeZoneRule *to = historicRules[typeMapData[ttidx + 1]]; 930 TimeZoneRule *from = historicRules[typeMapData[ttidx]]; 931 UDate startTime = (UDate)transitionTime(ttidx+1); 932 933 // The transitions loaded from zoneinfo.res may contain non-transition data 934 UnicodeString fromName, toName; 935 from->getName(fromName); 936 to->getName(toName); 937 if (fromName == toName && from->getRawOffset() == to->getRawOffset() 938 && from->getDSTSavings() == to->getDSTSavings()) { 939 return getNextTransition(startTime, false, result); 940 } 941 result.setTime(startTime); 942 result.adoptFrom(from->clone()); 943 result.adoptTo(to->clone()); 944 return TRUE; 945 } 946 } 947 return FALSE; 948 } 949 950 UBool 951 OlsonTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { 952 UErrorCode status = U_ZERO_ERROR; 953 checkTransitionRules(status); 954 if (U_FAILURE(status)) { 955 return FALSE; 956 } 957 958 if (finalZone != NULL) { 959 if (inclusive && base == firstFinalTZTransition->getTime()) { 960 result = *firstFinalTZTransition; 961 return TRUE; 962 } else if (base > firstFinalTZTransition->getTime()) { 963 if (finalZone->useDaylightTime()) { 964 //return finalZone->getPreviousTransition(base, inclusive, result); 965 return finalZoneWithStartYear->getPreviousTransition(base, inclusive, result); 966 } else { 967 result = *firstFinalTZTransition; 968 return TRUE; 969 } 970 } 971 } 972 973 if (historicRules != NULL) { 974 // Find a historical transition 975 int16_t ttidx = transitionCount() - 1; 976 for (; ttidx >= firstTZTransitionIdx; ttidx--) { 977 UDate t = (UDate)transitionTime(ttidx); 978 if (base > t || (inclusive && base == t)) { 979 break; 980 } 981 } 982 if (ttidx < firstTZTransitionIdx) { 983 // No more transitions 984 return FALSE; 985 } else if (ttidx == firstTZTransitionIdx) { 986 result = *firstTZTransition; 987 return TRUE; 988 } else { 989 // Create a TimeZoneTransition 990 TimeZoneRule *to = historicRules[typeMapData[ttidx]]; 991 TimeZoneRule *from = historicRules[typeMapData[ttidx-1]]; 992 UDate startTime = (UDate)transitionTime(ttidx); 993 994 // The transitions loaded from zoneinfo.res may contain non-transition data 995 UnicodeString fromName, toName; 996 from->getName(fromName); 997 to->getName(toName); 998 if (fromName == toName && from->getRawOffset() == to->getRawOffset() 999 && from->getDSTSavings() == to->getDSTSavings()) { 1000 return getPreviousTransition(startTime, false, result); 1001 } 1002 result.setTime(startTime); 1003 result.adoptFrom(from->clone()); 1004 result.adoptTo(to->clone()); 1005 return TRUE; 1006 } 1007 } 1008 return FALSE; 1009 } 1010 1011 int32_t 1012 OlsonTimeZone::countTransitionRules(UErrorCode& status) const { 1013 if (U_FAILURE(status)) { 1014 return 0; 1015 } 1016 checkTransitionRules(status); 1017 if (U_FAILURE(status)) { 1018 return 0; 1019 } 1020 1021 int32_t count = 0; 1022 if (historicRules != NULL) { 1023 // historicRules may contain null entries when original zoneinfo data 1024 // includes non transition data. 1025 for (int32_t i = 0; i < historicRuleCount; i++) { 1026 if (historicRules[i] != NULL) { 1027 count++; 1028 } 1029 } 1030 } 1031 if (finalZone != NULL) { 1032 if (finalZone->useDaylightTime()) { 1033 count += 2; 1034 } else { 1035 count++; 1036 } 1037 } 1038 return count; 1039 } 1040 1041 void 1042 OlsonTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, 1043 const TimeZoneRule* trsrules[], 1044 int32_t& trscount, 1045 UErrorCode& status) const { 1046 if (U_FAILURE(status)) { 1047 return; 1048 } 1049 checkTransitionRules(status); 1050 if (U_FAILURE(status)) { 1051 return; 1052 } 1053 1054 // Initial rule 1055 initial = initialRule; 1056 1057 // Transition rules 1058 int32_t cnt = 0; 1059 if (historicRules != NULL && trscount > cnt) { 1060 // historicRules may contain null entries when original zoneinfo data 1061 // includes non transition data. 1062 for (int32_t i = 0; i < historicRuleCount; i++) { 1063 if (historicRules[i] != NULL) { 1064 trsrules[cnt++] = historicRules[i]; 1065 if (cnt >= trscount) { 1066 break; 1067 } 1068 } 1069 } 1070 } 1071 if (finalZoneWithStartYear != NULL && trscount > cnt) { 1072 const InitialTimeZoneRule *tmpini; 1073 int32_t tmpcnt = trscount - cnt; 1074 finalZoneWithStartYear->getTimeZoneRules(tmpini, &trsrules[cnt], tmpcnt, status); 1075 if (U_FAILURE(status)) { 1076 return; 1077 } 1078 cnt += tmpcnt; 1079 } 1080 // Set the result length 1081 trscount = cnt; 1082 } 1083 1084 U_NAMESPACE_END 1085 1086 #endif // !UCONFIG_NO_FORMATTING 1087 1088 //eof 1089