OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ******************************************************************************* |
| 3 * Copyright (C) 2007-2010, International Business Machines Corporation and |
| 4 * others. All Rights Reserved. |
| 5 ******************************************************************************* |
| 6 */ |
| 7 |
| 8 #include <typeinfo> // for 'typeid' to work |
| 9 |
| 10 #include "unicode/utypes.h" |
| 11 |
| 12 #if !UCONFIG_NO_FORMATTING |
| 13 |
| 14 #include "unicode/rbtz.h" |
| 15 #include "unicode/gregocal.h" |
| 16 #include "uvector.h" |
| 17 #include "gregoimp.h" |
| 18 #include "cmemory.h" |
| 19 |
| 20 U_NAMESPACE_BEGIN |
| 21 |
| 22 /** |
| 23 * A struct representing a time zone transition |
| 24 */ |
| 25 struct Transition { |
| 26 UDate time; |
| 27 TimeZoneRule* from; |
| 28 TimeZoneRule* to; |
| 29 }; |
| 30 |
| 31 static UBool compareRules(UVector* rules1, UVector* rules2) { |
| 32 if (rules1 == NULL && rules2 == NULL) { |
| 33 return TRUE; |
| 34 } else if (rules1 == NULL || rules2 == NULL) { |
| 35 return FALSE; |
| 36 } |
| 37 int32_t size = rules1->size(); |
| 38 if (size != rules2->size()) { |
| 39 return FALSE; |
| 40 } |
| 41 for (int32_t i = 0; i < size; i++) { |
| 42 TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i); |
| 43 TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i); |
| 44 if (*r1 != *r2) { |
| 45 return FALSE; |
| 46 } |
| 47 } |
| 48 return TRUE; |
| 49 } |
| 50 |
| 51 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone) |
| 52 |
| 53 RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRul
e* initialRule) |
| 54 : BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRule
s(NULL), |
| 55 fHistoricTransitions(NULL), fUpToDate(FALSE) { |
| 56 } |
| 57 |
| 58 RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source) |
| 59 : BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()), |
| 60 fHistoricTransitions(NULL), fUpToDate(FALSE) { |
| 61 fHistoricRules = copyRules(source.fHistoricRules); |
| 62 fFinalRules = copyRules(source.fFinalRules); |
| 63 if (source.fUpToDate) { |
| 64 UErrorCode status = U_ZERO_ERROR; |
| 65 complete(status); |
| 66 } |
| 67 } |
| 68 |
| 69 RuleBasedTimeZone::~RuleBasedTimeZone() { |
| 70 deleteTransitions(); |
| 71 deleteRules(); |
| 72 } |
| 73 |
| 74 RuleBasedTimeZone& |
| 75 RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) { |
| 76 if (*this != right) { |
| 77 BasicTimeZone::operator=(right); |
| 78 deleteRules(); |
| 79 fInitialRule = right.fInitialRule->clone(); |
| 80 fHistoricRules = copyRules(right.fHistoricRules); |
| 81 fFinalRules = copyRules(right.fFinalRules); |
| 82 deleteTransitions(); |
| 83 fUpToDate = FALSE; |
| 84 } |
| 85 return *this; |
| 86 } |
| 87 |
| 88 UBool |
| 89 RuleBasedTimeZone::operator==(const TimeZone& that) const { |
| 90 if (this == &that) { |
| 91 return TRUE; |
| 92 } |
| 93 if (typeid(*this) != typeid(that) |
| 94 || BasicTimeZone::operator==(that) == FALSE) { |
| 95 return FALSE; |
| 96 } |
| 97 RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that; |
| 98 if (*fInitialRule != *(rbtz->fInitialRule)) { |
| 99 return FALSE; |
| 100 } |
| 101 if (compareRules(fHistoricRules, rbtz->fHistoricRules) |
| 102 && compareRules(fFinalRules, rbtz->fFinalRules)) { |
| 103 return TRUE; |
| 104 } |
| 105 return FALSE; |
| 106 } |
| 107 |
| 108 UBool |
| 109 RuleBasedTimeZone::operator!=(const TimeZone& that) const { |
| 110 return !operator==(that); |
| 111 } |
| 112 |
| 113 void |
| 114 RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) { |
| 115 if (U_FAILURE(status)) { |
| 116 return; |
| 117 } |
| 118 AnnualTimeZoneRule* atzrule = dynamic_cast<AnnualTimeZoneRule*>(rule); |
| 119 if (atzrule != NULL && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
) { |
| 120 // A final rule |
| 121 if (fFinalRules == NULL) { |
| 122 fFinalRules = new UVector(status); |
| 123 if (U_FAILURE(status)) { |
| 124 return; |
| 125 } |
| 126 } else if (fFinalRules->size() >= 2) { |
| 127 // Cannot handle more than two final rules |
| 128 status = U_INVALID_STATE_ERROR; |
| 129 return; |
| 130 } |
| 131 fFinalRules->addElement((void*)rule, status); |
| 132 } else { |
| 133 // Non-final rule |
| 134 if (fHistoricRules == NULL) { |
| 135 fHistoricRules = new UVector(status); |
| 136 if (U_FAILURE(status)) { |
| 137 return; |
| 138 } |
| 139 } |
| 140 fHistoricRules->addElement((void*)rule, status); |
| 141 } |
| 142 // Mark dirty, so transitions are recalculated at next complete() call |
| 143 fUpToDate = FALSE; |
| 144 } |
| 145 |
| 146 void |
| 147 RuleBasedTimeZone::complete(UErrorCode& status) { |
| 148 if (U_FAILURE(status)) { |
| 149 return; |
| 150 } |
| 151 if (fUpToDate) { |
| 152 return; |
| 153 } |
| 154 // Make sure either no final rules or a pair of AnnualTimeZoneRules |
| 155 // are available. |
| 156 if (fFinalRules != NULL && fFinalRules->size() != 2) { |
| 157 status = U_INVALID_STATE_ERROR; |
| 158 return; |
| 159 } |
| 160 |
| 161 UBool *done = NULL; |
| 162 // Create a TimezoneTransition and add to the list |
| 163 if (fHistoricRules != NULL || fFinalRules != NULL) { |
| 164 TimeZoneRule *curRule = fInitialRule; |
| 165 UDate lastTransitionTime = MIN_MILLIS; |
| 166 |
| 167 // Build the transition array which represents historical time zone |
| 168 // transitions. |
| 169 if (fHistoricRules != NULL && fHistoricRules->size() > 0) { |
| 170 int32_t i; |
| 171 int32_t historicCount = fHistoricRules->size(); |
| 172 done = (UBool*)uprv_malloc(sizeof(UBool) * historicCount); |
| 173 if (done == NULL) { |
| 174 status = U_MEMORY_ALLOCATION_ERROR; |
| 175 goto cleanup; |
| 176 } |
| 177 for (i = 0; i < historicCount; i++) { |
| 178 done[i] = FALSE; |
| 179 } |
| 180 while (TRUE) { |
| 181 int32_t curStdOffset = curRule->getRawOffset(); |
| 182 int32_t curDstSavings = curRule->getDSTSavings(); |
| 183 UDate nextTransitionTime = MAX_MILLIS; |
| 184 TimeZoneRule *nextRule = NULL; |
| 185 TimeZoneRule *r = NULL; |
| 186 UBool avail; |
| 187 UDate tt; |
| 188 UnicodeString curName, name; |
| 189 curRule->getName(curName); |
| 190 |
| 191 for (i = 0; i < historicCount; i++) { |
| 192 if (done[i]) { |
| 193 continue; |
| 194 } |
| 195 r = (TimeZoneRule*)fHistoricRules->elementAt(i); |
| 196 avail = r->getNextStart(lastTransitionTime, curStdOffset, cu
rDstSavings, false, tt); |
| 197 if (!avail) { |
| 198 // No more transitions from this rule - skip this rule n
ext time |
| 199 done[i] = TRUE; |
| 200 } else { |
| 201 r->getName(name); |
| 202 if (*r == *curRule || |
| 203 (name == curName && r->getRawOffset() == curRule->ge
tRawOffset() |
| 204 && r->getDSTSavings() == curRule->getDSTSavings()))
{ |
| 205 continue; |
| 206 } |
| 207 if (tt < nextTransitionTime) { |
| 208 nextTransitionTime = tt; |
| 209 nextRule = r; |
| 210 } |
| 211 } |
| 212 } |
| 213 |
| 214 if (nextRule == NULL) { |
| 215 // Check if all historic rules are done |
| 216 UBool bDoneAll = TRUE; |
| 217 for (int32_t j = 0; j < historicCount; j++) { |
| 218 if (!done[j]) { |
| 219 bDoneAll = FALSE; |
| 220 break; |
| 221 } |
| 222 } |
| 223 if (bDoneAll) { |
| 224 break; |
| 225 } |
| 226 } |
| 227 |
| 228 if (fFinalRules != NULL) { |
| 229 // Check if one of final rules has earlier transition date |
| 230 for (i = 0; i < 2 /* fFinalRules->size() */; i++) { |
| 231 TimeZoneRule *fr = (TimeZoneRule*)fFinalRules->elementAt
(i); |
| 232 if (*fr == *curRule) { |
| 233 continue; |
| 234 } |
| 235 r = (TimeZoneRule*)fFinalRules->elementAt(i); |
| 236 avail = r->getNextStart(lastTransitionTime, curStdOffset
, curDstSavings, false, tt); |
| 237 if (avail) { |
| 238 if (tt < nextTransitionTime) { |
| 239 nextTransitionTime = tt; |
| 240 nextRule = r; |
| 241 } |
| 242 } |
| 243 } |
| 244 } |
| 245 |
| 246 if (nextRule == NULL) { |
| 247 // Nothing more |
| 248 break; |
| 249 } |
| 250 |
| 251 if (fHistoricTransitions == NULL) { |
| 252 fHistoricTransitions = new UVector(status); |
| 253 if (U_FAILURE(status)) { |
| 254 goto cleanup; |
| 255 } |
| 256 } |
| 257 Transition *trst = (Transition*)uprv_malloc(sizeof(Transition)); |
| 258 if (trst == NULL) { |
| 259 status = U_MEMORY_ALLOCATION_ERROR; |
| 260 goto cleanup; |
| 261 } |
| 262 trst->time = nextTransitionTime; |
| 263 trst->from = curRule; |
| 264 trst->to = nextRule; |
| 265 fHistoricTransitions->addElement(trst, status); |
| 266 if (U_FAILURE(status)) { |
| 267 goto cleanup; |
| 268 } |
| 269 lastTransitionTime = nextTransitionTime; |
| 270 curRule = nextRule; |
| 271 } |
| 272 } |
| 273 if (fFinalRules != NULL) { |
| 274 if (fHistoricTransitions == NULL) { |
| 275 fHistoricTransitions = new UVector(status); |
| 276 if (U_FAILURE(status)) { |
| 277 goto cleanup; |
| 278 } |
| 279 } |
| 280 // Append the first transition for each |
| 281 TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0); |
| 282 TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1); |
| 283 UDate tt0, tt1; |
| 284 UBool avail0 = rule0->getNextStart(lastTransitionTime, curRule->getR
awOffset(), curRule->getDSTSavings(), false, tt0); |
| 285 UBool avail1 = rule1->getNextStart(lastTransitionTime, curRule->getR
awOffset(), curRule->getDSTSavings(), false, tt1); |
| 286 if (!avail0 || !avail1) { |
| 287 // Should not happen, because both rules are permanent |
| 288 status = U_INVALID_STATE_ERROR; |
| 289 goto cleanup; |
| 290 } |
| 291 Transition *final0 = (Transition*)uprv_malloc(sizeof(Transition)); |
| 292 if (final0 == NULL) { |
| 293 status = U_MEMORY_ALLOCATION_ERROR; |
| 294 goto cleanup; |
| 295 } |
| 296 Transition *final1 = (Transition*)uprv_malloc(sizeof(Transition)); |
| 297 if (final1 == NULL) { |
| 298 uprv_free(final0); |
| 299 status = U_MEMORY_ALLOCATION_ERROR; |
| 300 goto cleanup; |
| 301 } |
| 302 if (tt0 < tt1) { |
| 303 final0->time = tt0; |
| 304 final0->from = curRule; |
| 305 final0->to = rule0; |
| 306 rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSav
ings(), false, final1->time); |
| 307 final1->from = rule0; |
| 308 final1->to = rule1; |
| 309 } else { |
| 310 final0->time = tt1; |
| 311 final0->from = curRule; |
| 312 final0->to = rule1; |
| 313 rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSav
ings(), false, final1->time); |
| 314 final1->from = rule1; |
| 315 final1->to = rule0; |
| 316 } |
| 317 fHistoricTransitions->addElement(final0, status); |
| 318 if (U_FAILURE(status)) { |
| 319 goto cleanup; |
| 320 } |
| 321 fHistoricTransitions->addElement(final1, status); |
| 322 if (U_FAILURE(status)) { |
| 323 goto cleanup; |
| 324 } |
| 325 } |
| 326 } |
| 327 fUpToDate = TRUE; |
| 328 if (done != NULL) { |
| 329 uprv_free(done); |
| 330 } |
| 331 return; |
| 332 |
| 333 cleanup: |
| 334 deleteTransitions(); |
| 335 if (done != NULL) { |
| 336 uprv_free(done); |
| 337 } |
| 338 fUpToDate = FALSE; |
| 339 } |
| 340 |
| 341 TimeZone* |
| 342 RuleBasedTimeZone::clone(void) const { |
| 343 return new RuleBasedTimeZone(*this); |
| 344 } |
| 345 |
| 346 int32_t |
| 347 RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t d
ay, |
| 348 uint8_t dayOfWeek, int32_t millis, UErrorCode& stat
us) const { |
| 349 if (U_FAILURE(status)) { |
| 350 return 0; |
| 351 } |
| 352 if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { |
| 353 status = U_ILLEGAL_ARGUMENT_ERROR; |
| 354 return 0; |
| 355 } else { |
| 356 return getOffset(era, year, month, day, dayOfWeek, millis, |
| 357 Grego::monthLength(year, month), status); |
| 358 } |
| 359 } |
| 360 |
| 361 int32_t |
| 362 RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t d
ay, |
| 363 uint8_t /*dayOfWeek*/, int32_t millis, |
| 364 int32_t /*monthLength*/, UErrorCode& status) const
{ |
| 365 // dayOfWeek and monthLength are unused |
| 366 if (U_FAILURE(status)) { |
| 367 return 0; |
| 368 } |
| 369 if (era == GregorianCalendar::BC) { |
| 370 // Convert to extended year |
| 371 year = 1 - year; |
| 372 } |
| 373 int32_t rawOffset, dstOffset; |
| 374 UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY
+ millis; |
| 375 getOffsetInternal(time, TRUE, kDaylight, kStandard, rawOffset, dstOffset, st
atus); |
| 376 if (U_FAILURE(status)) { |
| 377 return 0; |
| 378 } |
| 379 return (rawOffset + dstOffset); |
| 380 } |
| 381 |
| 382 void |
| 383 RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, |
| 384 int32_t& dstOffset, UErrorCode& status) const { |
| 385 getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, statu
s); |
| 386 } |
| 387 |
| 388 void |
| 389 RuleBasedTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, in
t32_t duplicatedTimeOpt, |
| 390 int32_t& rawOffset, int32_t& dstOffset, UE
rrorCode& status) /*const*/ { |
| 391 getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffs
et, dstOffset, status); |
| 392 } |
| 393 |
| 394 |
| 395 /* |
| 396 * The internal getOffset implementation |
| 397 */ |
| 398 void |
| 399 RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local, |
| 400 int32_t NonExistingTimeOpt, int32_t Duplica
tedTimeOpt, |
| 401 int32_t& rawOffset, int32_t& dstOffset, |
| 402 UErrorCode& status) const { |
| 403 rawOffset = 0; |
| 404 dstOffset = 0; |
| 405 |
| 406 if (U_FAILURE(status)) { |
| 407 return; |
| 408 } |
| 409 if (!fUpToDate) { |
| 410 // Transitions are not yet resolved. We cannot do it here |
| 411 // because this method is const. Thus, do nothing and return |
| 412 // error status. |
| 413 status = U_INVALID_STATE_ERROR; |
| 414 return; |
| 415 } |
| 416 const TimeZoneRule *rule = NULL; |
| 417 if (fHistoricTransitions == NULL) { |
| 418 rule = fInitialRule; |
| 419 } else { |
| 420 UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elem
entAt(0), |
| 421 local, NonExistingTimeOpt, DuplicatedTimeOpt); |
| 422 if (date < tstart) { |
| 423 rule = fInitialRule; |
| 424 } else { |
| 425 int32_t idx = fHistoricTransitions->size() - 1; |
| 426 UDate tend = getTransitionTime((Transition*)fHistoricTransitions->el
ementAt(idx), |
| 427 local, NonExistingTimeOpt, DuplicatedTimeOpt); |
| 428 if (date > tend) { |
| 429 if (fFinalRules != NULL) { |
| 430 rule = findRuleInFinal(date, local, NonExistingTimeOpt, Dupl
icatedTimeOpt); |
| 431 } else { |
| 432 // no final rule, use the last rule |
| 433 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->
to; |
| 434 } |
| 435 } else { |
| 436 // Find a historical transition |
| 437 while (idx >= 0) { |
| 438 if (date >= getTransitionTime((Transition*)fHistoricTransiti
ons->elementAt(idx), |
| 439 local, NonExistingTimeOpt, DuplicatedTimeOpt)) { |
| 440 break; |
| 441 } |
| 442 idx--; |
| 443 } |
| 444 rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; |
| 445 } |
| 446 } |
| 447 } |
| 448 if (rule != NULL) { |
| 449 rawOffset = rule->getRawOffset(); |
| 450 dstOffset = rule->getDSTSavings(); |
| 451 } |
| 452 } |
| 453 |
| 454 void |
| 455 RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) { |
| 456 // We don't support this operation at this moment. |
| 457 // Nothing to do! |
| 458 } |
| 459 |
| 460 int32_t |
| 461 RuleBasedTimeZone::getRawOffset(void) const { |
| 462 // Note: This implementation returns standard GMT offset |
| 463 // as of current time. |
| 464 UErrorCode status = U_ZERO_ERROR; |
| 465 int32_t raw, dst; |
| 466 getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND, |
| 467 FALSE, raw, dst, status); |
| 468 return raw; |
| 469 } |
| 470 |
| 471 UBool |
| 472 RuleBasedTimeZone::useDaylightTime(void) const { |
| 473 // Note: This implementation returns true when |
| 474 // daylight saving time is used as of now or |
| 475 // after the next transition. |
| 476 UErrorCode status = U_ZERO_ERROR; |
| 477 UDate now = uprv_getUTCtime() * U_MILLIS_PER_SECOND; |
| 478 int32_t raw, dst; |
| 479 getOffset(now, FALSE, raw, dst, status); |
| 480 if (dst != 0) { |
| 481 return TRUE; |
| 482 } |
| 483 // If DST is not used now, check if DST is used after the next transition |
| 484 UDate time; |
| 485 TimeZoneRule *from, *to; |
| 486 UBool avail = findNext(now, FALSE, time, from, to); |
| 487 if (avail && to->getDSTSavings() != 0) { |
| 488 return TRUE; |
| 489 } |
| 490 return FALSE; |
| 491 } |
| 492 |
| 493 UBool |
| 494 RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const { |
| 495 if (U_FAILURE(status)) { |
| 496 return FALSE; |
| 497 } |
| 498 int32_t raw, dst; |
| 499 getOffset(date, FALSE, raw, dst, status); |
| 500 if (dst != 0) { |
| 501 return TRUE; |
| 502 } |
| 503 return FALSE; |
| 504 } |
| 505 |
| 506 UBool |
| 507 RuleBasedTimeZone::hasSameRules(const TimeZone& other) const { |
| 508 if (this == &other) { |
| 509 return TRUE; |
| 510 } |
| 511 if (typeid(*this) != typeid(other)) { |
| 512 return FALSE; |
| 513 } |
| 514 const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other; |
| 515 if (*fInitialRule != *(that.fInitialRule)) { |
| 516 return FALSE; |
| 517 } |
| 518 if (compareRules(fHistoricRules, that.fHistoricRules) |
| 519 && compareRules(fFinalRules, that.fFinalRules)) { |
| 520 return TRUE; |
| 521 } |
| 522 return FALSE; |
| 523 } |
| 524 |
| 525 UBool |
| 526 RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransi
tion& result) /*const*/ { |
| 527 UErrorCode status = U_ZERO_ERROR; |
| 528 complete(status); |
| 529 if (U_FAILURE(status)) { |
| 530 return FALSE; |
| 531 } |
| 532 UDate transitionTime; |
| 533 TimeZoneRule *fromRule, *toRule; |
| 534 UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule); |
| 535 if (found) { |
| 536 result.setTime(transitionTime); |
| 537 result.setFrom((const TimeZoneRule&)*fromRule); |
| 538 result.setTo((const TimeZoneRule&)*toRule); |
| 539 return TRUE; |
| 540 } |
| 541 return FALSE; |
| 542 } |
| 543 |
| 544 UBool |
| 545 RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTr
ansition& result) /*const*/ { |
| 546 UErrorCode status = U_ZERO_ERROR; |
| 547 complete(status); |
| 548 if (U_FAILURE(status)) { |
| 549 return FALSE; |
| 550 } |
| 551 UDate transitionTime; |
| 552 TimeZoneRule *fromRule, *toRule; |
| 553 UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule); |
| 554 if (found) { |
| 555 result.setTime(transitionTime); |
| 556 result.setFrom((const TimeZoneRule&)*fromRule); |
| 557 result.setTo((const TimeZoneRule&)*toRule); |
| 558 return TRUE; |
| 559 } |
| 560 return FALSE; |
| 561 } |
| 562 |
| 563 int32_t |
| 564 RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) /*const*/ { |
| 565 int32_t count = 0; |
| 566 if (fHistoricRules != NULL) { |
| 567 count += fHistoricRules->size(); |
| 568 } |
| 569 if (fFinalRules != NULL) { |
| 570 count += fFinalRules->size(); |
| 571 } |
| 572 return count; |
| 573 } |
| 574 |
| 575 void |
| 576 RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, |
| 577 const TimeZoneRule* trsrules[], |
| 578 int32_t& trscount, |
| 579 UErrorCode& status) /*const*/ { |
| 580 if (U_FAILURE(status)) { |
| 581 return; |
| 582 } |
| 583 // Initial rule |
| 584 initial = fInitialRule; |
| 585 |
| 586 // Transition rules |
| 587 int32_t cnt = 0; |
| 588 int32_t idx; |
| 589 if (fHistoricRules != NULL && cnt < trscount) { |
| 590 int32_t historicCount = fHistoricRules->size(); |
| 591 idx = 0; |
| 592 while (cnt < trscount && idx < historicCount) { |
| 593 trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx
++); |
| 594 } |
| 595 } |
| 596 if (fFinalRules != NULL && cnt < trscount) { |
| 597 int32_t finalCount = fFinalRules->size(); |
| 598 idx = 0; |
| 599 while (cnt < trscount && idx < finalCount) { |
| 600 trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++)
; |
| 601 } |
| 602 } |
| 603 // Set the result length |
| 604 trscount = cnt; |
| 605 } |
| 606 |
| 607 void |
| 608 RuleBasedTimeZone::deleteRules(void) { |
| 609 delete fInitialRule; |
| 610 fInitialRule = NULL; |
| 611 if (fHistoricRules != NULL) { |
| 612 while (!fHistoricRules->isEmpty()) { |
| 613 delete (TimeZoneRule*)(fHistoricRules->orphanElementAt(0)); |
| 614 } |
| 615 delete fHistoricRules; |
| 616 fHistoricRules = NULL; |
| 617 } |
| 618 if (fFinalRules != NULL) { |
| 619 while (!fFinalRules->isEmpty()) { |
| 620 delete (AnnualTimeZoneRule*)(fFinalRules->orphanElementAt(0)); |
| 621 } |
| 622 delete fFinalRules; |
| 623 fFinalRules = NULL; |
| 624 } |
| 625 } |
| 626 |
| 627 void |
| 628 RuleBasedTimeZone::deleteTransitions(void) { |
| 629 if (fHistoricTransitions != NULL) { |
| 630 while (!fHistoricTransitions->isEmpty()) { |
| 631 Transition *trs = (Transition*)fHistoricTransitions->orphanElementAt
(0); |
| 632 uprv_free(trs); |
| 633 } |
| 634 delete fHistoricTransitions; |
| 635 } |
| 636 fHistoricTransitions = NULL; |
| 637 } |
| 638 |
| 639 UVector* |
| 640 RuleBasedTimeZone::copyRules(UVector* source) { |
| 641 if (source == NULL) { |
| 642 return NULL; |
| 643 } |
| 644 UErrorCode ec = U_ZERO_ERROR; |
| 645 int32_t size = source->size(); |
| 646 UVector *rules = new UVector(size, ec); |
| 647 if (U_FAILURE(ec)) { |
| 648 return NULL; |
| 649 } |
| 650 int32_t i; |
| 651 for (i = 0; i < size; i++) { |
| 652 rules->addElement(((TimeZoneRule*)source->elementAt(i))->clone(), ec); |
| 653 if (U_FAILURE(ec)) { |
| 654 break; |
| 655 } |
| 656 } |
| 657 if (U_FAILURE(ec)) { |
| 658 // In case of error, clean up |
| 659 for (i = 0; i < rules->size(); i++) { |
| 660 TimeZoneRule *rule = (TimeZoneRule*)rules->orphanElementAt(i); |
| 661 delete rule; |
| 662 } |
| 663 delete rules; |
| 664 return NULL; |
| 665 } |
| 666 return rules; |
| 667 } |
| 668 |
| 669 TimeZoneRule* |
| 670 RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local, |
| 671 int32_t NonExistingTimeOpt, int32_t Duplicate
dTimeOpt) const { |
| 672 if (fFinalRules == NULL) { |
| 673 return NULL; |
| 674 } |
| 675 |
| 676 AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0); |
| 677 AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1); |
| 678 if (fr0 == NULL || fr1 == NULL) { |
| 679 return NULL; |
| 680 } |
| 681 |
| 682 UDate start0, start1; |
| 683 UDate base; |
| 684 int32_t localDelta; |
| 685 |
| 686 base = date; |
| 687 if (local) { |
| 688 localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(), |
| 689 fr0->getRawOffset(), fr0->getDSTSavings(), |
| 690 NonExistingTimeOpt, DuplicatedTimeOpt); |
| 691 base -= localDelta; |
| 692 } |
| 693 UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTS
avings(), TRUE, start0); |
| 694 |
| 695 base = date; |
| 696 if (local) { |
| 697 localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(), |
| 698 fr1->getRawOffset(), fr1->getDSTSavings(), |
| 699 NonExistingTimeOpt, DuplicatedTimeOpt); |
| 700 base -= localDelta; |
| 701 } |
| 702 UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTS
avings(), TRUE, start1); |
| 703 |
| 704 if (avail0 && (!avail1 || start0 > start1)) { |
| 705 return fr0; |
| 706 } else if (avail1) { |
| 707 return fr1; |
| 708 } |
| 709 return NULL; |
| 710 } |
| 711 |
| 712 UBool |
| 713 RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime, |
| 714 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) cons
t { |
| 715 if (fHistoricTransitions == NULL) { |
| 716 return FALSE; |
| 717 } |
| 718 UBool isFinal = FALSE; |
| 719 UBool found = FALSE; |
| 720 Transition result; |
| 721 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); |
| 722 UDate tt = tzt->time; |
| 723 if (tt > base || (inclusive && tt == base)) { |
| 724 result = *tzt; |
| 725 found = TRUE; |
| 726 } else { |
| 727 int32_t idx = fHistoricTransitions->size() - 1; |
| 728 tzt = (Transition*)fHistoricTransitions->elementAt(idx); |
| 729 tt = tzt->time; |
| 730 if (inclusive && tt == base) { |
| 731 result = *tzt; |
| 732 found = TRUE; |
| 733 } else if (tt <= base) { |
| 734 if (fFinalRules != NULL) { |
| 735 // Find a transion time with finalRules |
| 736 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); |
| 737 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); |
| 738 UDate start0, start1; |
| 739 UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->ge
tDSTSavings(), inclusive, start0); |
| 740 UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->ge
tDSTSavings(), inclusive, start1); |
| 741 // avail0/avail1 should be always TRUE |
| 742 if (!avail0 && !avail1) { |
| 743 return FALSE; |
| 744 } |
| 745 if (!avail1 || start0 < start1) { |
| 746 result.time = start0; |
| 747 result.from = r1; |
| 748 result.to = r0; |
| 749 } else { |
| 750 result.time = start1; |
| 751 result.from = r0; |
| 752 result.to = r1; |
| 753 } |
| 754 isFinal = TRUE; |
| 755 found = TRUE; |
| 756 } |
| 757 } else { |
| 758 // Find a transition within the historic transitions |
| 759 idx--; |
| 760 Transition *prev = tzt; |
| 761 while (idx > 0) { |
| 762 tzt = (Transition*)fHistoricTransitions->elementAt(idx); |
| 763 tt = tzt->time; |
| 764 if (tt < base || (!inclusive && tt == base)) { |
| 765 break; |
| 766 } |
| 767 idx--; |
| 768 prev = tzt; |
| 769 } |
| 770 result.time = prev->time; |
| 771 result.from = prev->from; |
| 772 result.to = prev->to; |
| 773 found = TRUE; |
| 774 } |
| 775 } |
| 776 if (found) { |
| 777 // For now, this implementation ignore transitions with only zone name c
hanges. |
| 778 if (result.from->getRawOffset() == result.to->getRawOffset() |
| 779 && result.from->getDSTSavings() == result.to->getDSTSavings()) { |
| 780 if (isFinal) { |
| 781 return FALSE; |
| 782 } else { |
| 783 // No offset changes. Try next one if not final |
| 784 return findNext(result.time, FALSE /* always exclusive */, |
| 785 transitionTime, fromRule, toRule); |
| 786 } |
| 787 } |
| 788 transitionTime = result.time; |
| 789 fromRule = result.from; |
| 790 toRule = result.to; |
| 791 return TRUE; |
| 792 } |
| 793 return FALSE; |
| 794 } |
| 795 |
| 796 UBool |
| 797 RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime, |
| 798 TimeZoneRule*& fromRule, TimeZoneRule*& toRule) cons
t { |
| 799 if (fHistoricTransitions == NULL) { |
| 800 return FALSE; |
| 801 } |
| 802 UBool found = FALSE; |
| 803 Transition result; |
| 804 Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); |
| 805 UDate tt = tzt->time; |
| 806 if (inclusive && tt == base) { |
| 807 result = *tzt; |
| 808 found = TRUE; |
| 809 } else if (tt < base) { |
| 810 int32_t idx = fHistoricTransitions->size() - 1; |
| 811 tzt = (Transition*)fHistoricTransitions->elementAt(idx); |
| 812 tt = tzt->time; |
| 813 if (inclusive && tt == base) { |
| 814 result = *tzt; |
| 815 found = TRUE; |
| 816 } else if (tt < base) { |
| 817 if (fFinalRules != NULL) { |
| 818 // Find a transion time with finalRules |
| 819 TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); |
| 820 TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); |
| 821 UDate start0, start1; |
| 822 UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1
->getDSTSavings(), inclusive, start0); |
| 823 UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0
->getDSTSavings(), inclusive, start1); |
| 824 // avail0/avail1 should be always TRUE |
| 825 if (!avail0 && !avail1) { |
| 826 return FALSE; |
| 827 } |
| 828 if (!avail1 || start0 > start1) { |
| 829 result.time = start0; |
| 830 result.from = r1; |
| 831 result.to = r0; |
| 832 } else { |
| 833 result.time = start1; |
| 834 result.from = r0; |
| 835 result.to = r1; |
| 836 } |
| 837 } else { |
| 838 result = *tzt; |
| 839 } |
| 840 found = TRUE; |
| 841 } else { |
| 842 // Find a transition within the historic transitions |
| 843 idx--; |
| 844 while (idx >= 0) { |
| 845 tzt = (Transition*)fHistoricTransitions->elementAt(idx); |
| 846 tt = tzt->time; |
| 847 if (tt < base || (inclusive && tt == base)) { |
| 848 break; |
| 849 } |
| 850 idx--; |
| 851 } |
| 852 result = *tzt; |
| 853 found = TRUE; |
| 854 } |
| 855 } |
| 856 if (found) { |
| 857 // For now, this implementation ignore transitions with only zone name c
hanges. |
| 858 if (result.from->getRawOffset() == result.to->getRawOffset() |
| 859 && result.from->getDSTSavings() == result.to->getDSTSavings()) { |
| 860 // No offset changes. Try next one if not final |
| 861 return findPrev(result.time, FALSE /* always exclusive */, |
| 862 transitionTime, fromRule, toRule); |
| 863 } |
| 864 transitionTime = result.time; |
| 865 fromRule = result.from; |
| 866 toRule = result.to; |
| 867 return TRUE; |
| 868 } |
| 869 return FALSE; |
| 870 } |
| 871 |
| 872 UDate |
| 873 RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local, |
| 874 int32_t NonExistingTimeOpt, int32_t Duplica
tedTimeOpt) const { |
| 875 UDate time = transition->time; |
| 876 if (local) { |
| 877 time += getLocalDelta(transition->from->getRawOffset(), transition->from
->getDSTSavings(), |
| 878 transition->to->getRawOffset(), transition->to->ge
tDSTSavings(), |
| 879 NonExistingTimeOpt, DuplicatedTimeOpt); |
| 880 } |
| 881 return time; |
| 882 } |
| 883 |
| 884 int32_t |
| 885 RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t r
awAfter, int32_t dstAfter, |
| 886 int32_t NonExistingTimeOpt, int32_t DuplicatedTimeO
pt) const { |
| 887 int32_t delta = 0; |
| 888 |
| 889 int32_t offsetBefore = rawBefore + dstBefore; |
| 890 int32_t offsetAfter = rawAfter + dstAfter; |
| 891 |
| 892 UBool dstToStd = (dstBefore != 0) && (dstAfter == 0); |
| 893 UBool stdToDst = (dstBefore == 0) && (dstAfter != 0); |
| 894 |
| 895 if (offsetAfter - offsetBefore >= 0) { |
| 896 // Positive transition, which makes a non-existing local time range |
| 897 if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) |
| 898 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)
) { |
| 899 delta = offsetBefore; |
| 900 } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) |
| 901 || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)
) { |
| 902 delta = offsetAfter; |
| 903 } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { |
| 904 delta = offsetBefore; |
| 905 } else { |
| 906 // Interprets the time with rule before the transition, |
| 907 // default for non-existing time range |
| 908 delta = offsetAfter; |
| 909 } |
| 910 } else { |
| 911 // Negative transition, which makes a duplicated local time range |
| 912 if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) |
| 913 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst))
{ |
| 914 delta = offsetAfter; |
| 915 } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) |
| 916 || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd))
{ |
| 917 delta = offsetBefore; |
| 918 } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { |
| 919 delta = offsetBefore; |
| 920 } else { |
| 921 // Interprets the time with rule after the transition, |
| 922 // default for duplicated local time range |
| 923 delta = offsetAfter; |
| 924 } |
| 925 } |
| 926 return delta; |
| 927 } |
| 928 |
| 929 U_NAMESPACE_END |
| 930 |
| 931 #endif /* #if !UCONFIG_NO_FORMATTING */ |
| 932 |
| 933 //eof |
| 934 |
OLD | NEW |