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