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