intl/icu/source/i18n/rbtz.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial