michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 2007-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "utypeinfo.h" // for 'typeid' to work michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/rbtz.h" michael@0: #include "unicode/gregocal.h" michael@0: #include "uvector.h" michael@0: #include "gregoimp.h" michael@0: #include "cmemory.h" michael@0: #include "umutex.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: /** michael@0: * A struct representing a time zone transition michael@0: */ michael@0: struct Transition { michael@0: UDate time; michael@0: TimeZoneRule* from; michael@0: TimeZoneRule* to; michael@0: }; michael@0: michael@0: static UBool compareRules(UVector* rules1, UVector* rules2) { michael@0: if (rules1 == NULL && rules2 == NULL) { michael@0: return TRUE; michael@0: } else if (rules1 == NULL || rules2 == NULL) { michael@0: return FALSE; michael@0: } michael@0: int32_t size = rules1->size(); michael@0: if (size != rules2->size()) { michael@0: return FALSE; michael@0: } michael@0: for (int32_t i = 0; i < size; i++) { michael@0: TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i); michael@0: TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i); michael@0: if (*r1 != *r2) { michael@0: return FALSE; michael@0: } michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone) michael@0: michael@0: RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule) michael@0: : BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRules(NULL), michael@0: fHistoricTransitions(NULL), fUpToDate(FALSE) { michael@0: } michael@0: michael@0: RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source) michael@0: : BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()), michael@0: fHistoricTransitions(NULL), fUpToDate(FALSE) { michael@0: fHistoricRules = copyRules(source.fHistoricRules); michael@0: fFinalRules = copyRules(source.fFinalRules); michael@0: if (source.fUpToDate) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: complete(status); michael@0: } michael@0: } michael@0: michael@0: RuleBasedTimeZone::~RuleBasedTimeZone() { michael@0: deleteTransitions(); michael@0: deleteRules(); michael@0: } michael@0: michael@0: RuleBasedTimeZone& michael@0: RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) { michael@0: if (*this != right) { michael@0: BasicTimeZone::operator=(right); michael@0: deleteRules(); michael@0: fInitialRule = right.fInitialRule->clone(); michael@0: fHistoricRules = copyRules(right.fHistoricRules); michael@0: fFinalRules = copyRules(right.fFinalRules); michael@0: deleteTransitions(); michael@0: fUpToDate = FALSE; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::operator==(const TimeZone& that) const { michael@0: if (this == &that) { michael@0: return TRUE; michael@0: } michael@0: if (typeid(*this) != typeid(that) michael@0: || BasicTimeZone::operator==(that) == FALSE) { michael@0: return FALSE; michael@0: } michael@0: RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that; michael@0: if (*fInitialRule != *(rbtz->fInitialRule)) { michael@0: return FALSE; michael@0: } michael@0: if (compareRules(fHistoricRules, rbtz->fHistoricRules) michael@0: && compareRules(fFinalRules, rbtz->fFinalRules)) { michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::operator!=(const TimeZone& that) const { michael@0: return !operator==(that); michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: AnnualTimeZoneRule* atzrule = dynamic_cast(rule); michael@0: if (atzrule != NULL && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { michael@0: // A final rule michael@0: if (fFinalRules == NULL) { michael@0: fFinalRules = new UVector(status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: } else if (fFinalRules->size() >= 2) { michael@0: // Cannot handle more than two final rules michael@0: status = U_INVALID_STATE_ERROR; michael@0: return; michael@0: } michael@0: fFinalRules->addElement((void*)rule, status); michael@0: } else { michael@0: // Non-final rule michael@0: if (fHistoricRules == NULL) { michael@0: fHistoricRules = new UVector(status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: } michael@0: fHistoricRules->addElement((void*)rule, status); michael@0: } michael@0: // Mark dirty, so transitions are recalculated at next complete() call michael@0: fUpToDate = FALSE; michael@0: } michael@0: michael@0: static UMutex gLock = U_MUTEX_INITIALIZER; michael@0: michael@0: void michael@0: RuleBasedTimeZone::completeConst(UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: umtx_lock(&gLock); michael@0: if (!fUpToDate) { michael@0: RuleBasedTimeZone *ncThis = const_cast(this); michael@0: ncThis->complete(status); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::complete(UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: if (fUpToDate) { michael@0: return; michael@0: } michael@0: // Make sure either no final rules or a pair of AnnualTimeZoneRules michael@0: // are available. michael@0: if (fFinalRules != NULL && fFinalRules->size() != 2) { michael@0: status = U_INVALID_STATE_ERROR; michael@0: return; michael@0: } michael@0: michael@0: UBool *done = NULL; michael@0: // Create a TimezoneTransition and add to the list michael@0: if (fHistoricRules != NULL || fFinalRules != NULL) { michael@0: TimeZoneRule *curRule = fInitialRule; michael@0: UDate lastTransitionTime = MIN_MILLIS; michael@0: michael@0: // Build the transition array which represents historical time zone michael@0: // transitions. michael@0: if (fHistoricRules != NULL && fHistoricRules->size() > 0) { michael@0: int32_t i; michael@0: int32_t historicCount = fHistoricRules->size(); michael@0: done = (UBool*)uprv_malloc(sizeof(UBool) * historicCount); michael@0: if (done == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: goto cleanup; michael@0: } michael@0: for (i = 0; i < historicCount; i++) { michael@0: done[i] = FALSE; michael@0: } michael@0: while (TRUE) { michael@0: int32_t curStdOffset = curRule->getRawOffset(); michael@0: int32_t curDstSavings = curRule->getDSTSavings(); michael@0: UDate nextTransitionTime = MAX_MILLIS; michael@0: TimeZoneRule *nextRule = NULL; michael@0: TimeZoneRule *r = NULL; michael@0: UBool avail; michael@0: UDate tt; michael@0: UnicodeString curName, name; michael@0: curRule->getName(curName); michael@0: michael@0: for (i = 0; i < historicCount; i++) { michael@0: if (done[i]) { michael@0: continue; michael@0: } michael@0: r = (TimeZoneRule*)fHistoricRules->elementAt(i); michael@0: avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); michael@0: if (!avail) { michael@0: // No more transitions from this rule - skip this rule next time michael@0: done[i] = TRUE; michael@0: } else { michael@0: r->getName(name); michael@0: if (*r == *curRule || michael@0: (name == curName && r->getRawOffset() == curRule->getRawOffset() michael@0: && r->getDSTSavings() == curRule->getDSTSavings())) { michael@0: continue; michael@0: } michael@0: if (tt < nextTransitionTime) { michael@0: nextTransitionTime = tt; michael@0: nextRule = r; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (nextRule == NULL) { michael@0: // Check if all historic rules are done michael@0: UBool bDoneAll = TRUE; michael@0: for (int32_t j = 0; j < historicCount; j++) { michael@0: if (!done[j]) { michael@0: bDoneAll = FALSE; michael@0: break; michael@0: } michael@0: } michael@0: if (bDoneAll) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (fFinalRules != NULL) { michael@0: // Check if one of final rules has earlier transition date michael@0: for (i = 0; i < 2 /* fFinalRules->size() */; i++) { michael@0: TimeZoneRule *fr = (TimeZoneRule*)fFinalRules->elementAt(i); michael@0: if (*fr == *curRule) { michael@0: continue; michael@0: } michael@0: r = (TimeZoneRule*)fFinalRules->elementAt(i); michael@0: avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt); michael@0: if (avail) { michael@0: if (tt < nextTransitionTime) { michael@0: nextTransitionTime = tt; michael@0: nextRule = r; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (nextRule == NULL) { michael@0: // Nothing more michael@0: break; michael@0: } michael@0: michael@0: if (fHistoricTransitions == NULL) { michael@0: fHistoricTransitions = new UVector(status); michael@0: if (U_FAILURE(status)) { michael@0: goto cleanup; michael@0: } michael@0: } michael@0: Transition *trst = (Transition*)uprv_malloc(sizeof(Transition)); michael@0: if (trst == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: goto cleanup; michael@0: } michael@0: trst->time = nextTransitionTime; michael@0: trst->from = curRule; michael@0: trst->to = nextRule; michael@0: fHistoricTransitions->addElement(trst, status); michael@0: if (U_FAILURE(status)) { michael@0: goto cleanup; michael@0: } michael@0: lastTransitionTime = nextTransitionTime; michael@0: curRule = nextRule; michael@0: } michael@0: } michael@0: if (fFinalRules != NULL) { michael@0: if (fHistoricTransitions == NULL) { michael@0: fHistoricTransitions = new UVector(status); michael@0: if (U_FAILURE(status)) { michael@0: goto cleanup; michael@0: } michael@0: } michael@0: // Append the first transition for each michael@0: TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0); michael@0: TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1); michael@0: UDate tt0, tt1; michael@0: UBool avail0 = rule0->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt0); michael@0: UBool avail1 = rule1->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt1); michael@0: if (!avail0 || !avail1) { michael@0: // Should not happen, because both rules are permanent michael@0: status = U_INVALID_STATE_ERROR; michael@0: goto cleanup; michael@0: } michael@0: Transition *final0 = (Transition*)uprv_malloc(sizeof(Transition)); michael@0: if (final0 == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: goto cleanup; michael@0: } michael@0: Transition *final1 = (Transition*)uprv_malloc(sizeof(Transition)); michael@0: if (final1 == NULL) { michael@0: uprv_free(final0); michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: goto cleanup; michael@0: } michael@0: if (tt0 < tt1) { michael@0: final0->time = tt0; michael@0: final0->from = curRule; michael@0: final0->to = rule0; michael@0: rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSavings(), false, final1->time); michael@0: final1->from = rule0; michael@0: final1->to = rule1; michael@0: } else { michael@0: final0->time = tt1; michael@0: final0->from = curRule; michael@0: final0->to = rule1; michael@0: rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSavings(), false, final1->time); michael@0: final1->from = rule1; michael@0: final1->to = rule0; michael@0: } michael@0: fHistoricTransitions->addElement(final0, status); michael@0: if (U_FAILURE(status)) { michael@0: goto cleanup; michael@0: } michael@0: fHistoricTransitions->addElement(final1, status); michael@0: if (U_FAILURE(status)) { michael@0: goto cleanup; michael@0: } michael@0: } michael@0: } michael@0: fUpToDate = TRUE; michael@0: if (done != NULL) { michael@0: uprv_free(done); michael@0: } michael@0: return; michael@0: michael@0: cleanup: michael@0: deleteTransitions(); michael@0: if (done != NULL) { michael@0: uprv_free(done); michael@0: } michael@0: fUpToDate = FALSE; michael@0: } michael@0: michael@0: TimeZone* michael@0: RuleBasedTimeZone::clone(void) const { michael@0: return new RuleBasedTimeZone(*this); michael@0: } michael@0: michael@0: int32_t michael@0: RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, michael@0: uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return 0; michael@0: } michael@0: if (month < UCAL_JANUARY || month > UCAL_DECEMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return 0; michael@0: } else { michael@0: return getOffset(era, year, month, day, dayOfWeek, millis, michael@0: Grego::monthLength(year, month), status); michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, michael@0: uint8_t /*dayOfWeek*/, int32_t millis, michael@0: int32_t /*monthLength*/, UErrorCode& status) const { michael@0: // dayOfWeek and monthLength are unused michael@0: if (U_FAILURE(status)) { michael@0: return 0; michael@0: } michael@0: if (era == GregorianCalendar::BC) { michael@0: // Convert to extended year michael@0: year = 1 - year; michael@0: } michael@0: int32_t rawOffset, dstOffset; michael@0: UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis; michael@0: getOffsetInternal(time, TRUE, kDaylight, kStandard, rawOffset, dstOffset, status); michael@0: if (U_FAILURE(status)) { michael@0: return 0; michael@0: } michael@0: return (rawOffset + dstOffset); michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, michael@0: int32_t& dstOffset, UErrorCode& status) const { michael@0: getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status); michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, michael@0: int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const { michael@0: getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * The internal getOffset implementation michael@0: */ michael@0: void michael@0: RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local, michael@0: int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt, michael@0: int32_t& rawOffset, int32_t& dstOffset, michael@0: UErrorCode& status) const { michael@0: rawOffset = 0; michael@0: dstOffset = 0; michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: if (!fUpToDate) { michael@0: // Transitions are not yet resolved. We cannot do it here michael@0: // because this method is const. Thus, do nothing and return michael@0: // error status. michael@0: status = U_INVALID_STATE_ERROR; michael@0: return; michael@0: } michael@0: const TimeZoneRule *rule = NULL; michael@0: if (fHistoricTransitions == NULL) { michael@0: rule = fInitialRule; michael@0: } else { michael@0: UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0), michael@0: local, NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: if (date < tstart) { michael@0: rule = fInitialRule; michael@0: } else { michael@0: int32_t idx = fHistoricTransitions->size() - 1; michael@0: UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), michael@0: local, NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: if (date > tend) { michael@0: if (fFinalRules != NULL) { michael@0: rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: } michael@0: if (rule == NULL) { michael@0: // no final rules or the given time is before the first transition michael@0: // specified by the final rules -> use the last rule michael@0: rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; michael@0: } michael@0: } else { michael@0: // Find a historical transition michael@0: while (idx >= 0) { michael@0: if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx), michael@0: local, NonExistingTimeOpt, DuplicatedTimeOpt)) { michael@0: break; michael@0: } michael@0: idx--; michael@0: } michael@0: rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to; michael@0: } michael@0: } michael@0: } michael@0: if (rule != NULL) { michael@0: rawOffset = rule->getRawOffset(); michael@0: dstOffset = rule->getDSTSavings(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) { michael@0: // We don't support this operation at this moment. michael@0: // Nothing to do! michael@0: } michael@0: michael@0: int32_t michael@0: RuleBasedTimeZone::getRawOffset(void) const { michael@0: // Note: This implementation returns standard GMT offset michael@0: // as of current time. michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t raw, dst; michael@0: getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND, michael@0: FALSE, raw, dst, status); michael@0: return raw; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::useDaylightTime(void) const { michael@0: // Note: This implementation returns true when michael@0: // daylight saving time is used as of now or michael@0: // after the next transition. michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: UDate now = uprv_getUTCtime() * U_MILLIS_PER_SECOND; michael@0: int32_t raw, dst; michael@0: getOffset(now, FALSE, raw, dst, status); michael@0: if (dst != 0) { michael@0: return TRUE; michael@0: } michael@0: // If DST is not used now, check if DST is used after the next transition michael@0: UDate time; michael@0: TimeZoneRule *from, *to; michael@0: UBool avail = findNext(now, FALSE, time, from, to); michael@0: if (avail && to->getDSTSavings() != 0) { michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: int32_t raw, dst; michael@0: getOffset(date, FALSE, raw, dst, status); michael@0: if (dst != 0) { michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::hasSameRules(const TimeZone& other) const { michael@0: if (this == &other) { michael@0: return TRUE; michael@0: } michael@0: if (typeid(*this) != typeid(other)) { michael@0: return FALSE; michael@0: } michael@0: const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other; michael@0: if (*fInitialRule != *(that.fInitialRule)) { michael@0: return FALSE; michael@0: } michael@0: if (compareRules(fHistoricRules, that.fHistoricRules) michael@0: && compareRules(fFinalRules, that.fFinalRules)) { michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: completeConst(status); michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: UDate transitionTime; michael@0: TimeZoneRule *fromRule, *toRule; michael@0: UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule); michael@0: if (found) { michael@0: result.setTime(transitionTime); michael@0: result.setFrom((const TimeZoneRule&)*fromRule); michael@0: result.setTo((const TimeZoneRule&)*toRule); michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: completeConst(status); michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: UDate transitionTime; michael@0: TimeZoneRule *fromRule, *toRule; michael@0: UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule); michael@0: if (found) { michael@0: result.setTime(transitionTime); michael@0: result.setFrom((const TimeZoneRule&)*fromRule); michael@0: result.setTo((const TimeZoneRule&)*toRule); michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: int32_t michael@0: RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) const { michael@0: int32_t count = 0; michael@0: if (fHistoricRules != NULL) { michael@0: count += fHistoricRules->size(); michael@0: } michael@0: if (fFinalRules != NULL) { michael@0: count += fFinalRules->size(); michael@0: } michael@0: return count; michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, michael@0: const TimeZoneRule* trsrules[], michael@0: int32_t& trscount, michael@0: UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: // Initial rule michael@0: initial = fInitialRule; michael@0: michael@0: // Transition rules michael@0: int32_t cnt = 0; michael@0: int32_t idx; michael@0: if (fHistoricRules != NULL && cnt < trscount) { michael@0: int32_t historicCount = fHistoricRules->size(); michael@0: idx = 0; michael@0: while (cnt < trscount && idx < historicCount) { michael@0: trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++); michael@0: } michael@0: } michael@0: if (fFinalRules != NULL && cnt < trscount) { michael@0: int32_t finalCount = fFinalRules->size(); michael@0: idx = 0; michael@0: while (cnt < trscount && idx < finalCount) { michael@0: trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++); michael@0: } michael@0: } michael@0: // Set the result length michael@0: trscount = cnt; michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::deleteRules(void) { michael@0: delete fInitialRule; michael@0: fInitialRule = NULL; michael@0: if (fHistoricRules != NULL) { michael@0: while (!fHistoricRules->isEmpty()) { michael@0: delete (TimeZoneRule*)(fHistoricRules->orphanElementAt(0)); michael@0: } michael@0: delete fHistoricRules; michael@0: fHistoricRules = NULL; michael@0: } michael@0: if (fFinalRules != NULL) { michael@0: while (!fFinalRules->isEmpty()) { michael@0: delete (AnnualTimeZoneRule*)(fFinalRules->orphanElementAt(0)); michael@0: } michael@0: delete fFinalRules; michael@0: fFinalRules = NULL; michael@0: } michael@0: } michael@0: michael@0: void michael@0: RuleBasedTimeZone::deleteTransitions(void) { michael@0: if (fHistoricTransitions != NULL) { michael@0: while (!fHistoricTransitions->isEmpty()) { michael@0: Transition *trs = (Transition*)fHistoricTransitions->orphanElementAt(0); michael@0: uprv_free(trs); michael@0: } michael@0: delete fHistoricTransitions; michael@0: } michael@0: fHistoricTransitions = NULL; michael@0: } michael@0: michael@0: UVector* michael@0: RuleBasedTimeZone::copyRules(UVector* source) { michael@0: if (source == NULL) { michael@0: return NULL; michael@0: } michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: int32_t size = source->size(); michael@0: UVector *rules = new UVector(size, ec); michael@0: if (U_FAILURE(ec)) { michael@0: return NULL; michael@0: } michael@0: int32_t i; michael@0: for (i = 0; i < size; i++) { michael@0: rules->addElement(((TimeZoneRule*)source->elementAt(i))->clone(), ec); michael@0: if (U_FAILURE(ec)) { michael@0: break; michael@0: } michael@0: } michael@0: if (U_FAILURE(ec)) { michael@0: // In case of error, clean up michael@0: for (i = 0; i < rules->size(); i++) { michael@0: TimeZoneRule *rule = (TimeZoneRule*)rules->orphanElementAt(i); michael@0: delete rule; michael@0: } michael@0: delete rules; michael@0: return NULL; michael@0: } michael@0: return rules; michael@0: } michael@0: michael@0: TimeZoneRule* michael@0: RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local, michael@0: int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { michael@0: if (fFinalRules == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0); michael@0: AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1); michael@0: if (fr0 == NULL || fr1 == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: UDate start0, start1; michael@0: UDate base; michael@0: int32_t localDelta; michael@0: michael@0: base = date; michael@0: if (local) { michael@0: localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(), michael@0: fr0->getRawOffset(), fr0->getDSTSavings(), michael@0: NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: base -= localDelta; michael@0: } michael@0: UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0); michael@0: michael@0: base = date; michael@0: if (local) { michael@0: localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(), michael@0: fr1->getRawOffset(), fr1->getDSTSavings(), michael@0: NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: base -= localDelta; michael@0: } michael@0: UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1); michael@0: michael@0: if (!avail0 || !avail1) { michael@0: if (avail0) { michael@0: return fr0; michael@0: } else if (avail1) { michael@0: return fr1; michael@0: } michael@0: // Both rules take effect after the given time michael@0: return NULL; michael@0: } michael@0: michael@0: return (start0 > start1) ? fr0 : fr1; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime, michael@0: TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { michael@0: if (fHistoricTransitions == NULL) { michael@0: return FALSE; michael@0: } michael@0: UBool isFinal = FALSE; michael@0: UBool found = FALSE; michael@0: Transition result; michael@0: Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); michael@0: UDate tt = tzt->time; michael@0: if (tt > base || (inclusive && tt == base)) { michael@0: result = *tzt; michael@0: found = TRUE; michael@0: } else { michael@0: int32_t idx = fHistoricTransitions->size() - 1; michael@0: tzt = (Transition*)fHistoricTransitions->elementAt(idx); michael@0: tt = tzt->time; michael@0: if (inclusive && tt == base) { michael@0: result = *tzt; michael@0: found = TRUE; michael@0: } else if (tt <= base) { michael@0: if (fFinalRules != NULL) { michael@0: // Find a transion time with finalRules michael@0: TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); michael@0: TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); michael@0: UDate start0, start1; michael@0: UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); michael@0: UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); michael@0: // avail0/avail1 should be always TRUE michael@0: if (!avail0 && !avail1) { michael@0: return FALSE; michael@0: } michael@0: if (!avail1 || start0 < start1) { michael@0: result.time = start0; michael@0: result.from = r1; michael@0: result.to = r0; michael@0: } else { michael@0: result.time = start1; michael@0: result.from = r0; michael@0: result.to = r1; michael@0: } michael@0: isFinal = TRUE; michael@0: found = TRUE; michael@0: } michael@0: } else { michael@0: // Find a transition within the historic transitions michael@0: idx--; michael@0: Transition *prev = tzt; michael@0: while (idx > 0) { michael@0: tzt = (Transition*)fHistoricTransitions->elementAt(idx); michael@0: tt = tzt->time; michael@0: if (tt < base || (!inclusive && tt == base)) { michael@0: break; michael@0: } michael@0: idx--; michael@0: prev = tzt; michael@0: } michael@0: result.time = prev->time; michael@0: result.from = prev->from; michael@0: result.to = prev->to; michael@0: found = TRUE; michael@0: } michael@0: } michael@0: if (found) { michael@0: // For now, this implementation ignore transitions with only zone name changes. michael@0: if (result.from->getRawOffset() == result.to->getRawOffset() michael@0: && result.from->getDSTSavings() == result.to->getDSTSavings()) { michael@0: if (isFinal) { michael@0: return FALSE; michael@0: } else { michael@0: // No offset changes. Try next one if not final michael@0: return findNext(result.time, FALSE /* always exclusive */, michael@0: transitionTime, fromRule, toRule); michael@0: } michael@0: } michael@0: transitionTime = result.time; michael@0: fromRule = result.from; michael@0: toRule = result.to; michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime, michael@0: TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const { michael@0: if (fHistoricTransitions == NULL) { michael@0: return FALSE; michael@0: } michael@0: UBool found = FALSE; michael@0: Transition result; michael@0: Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0); michael@0: UDate tt = tzt->time; michael@0: if (inclusive && tt == base) { michael@0: result = *tzt; michael@0: found = TRUE; michael@0: } else if (tt < base) { michael@0: int32_t idx = fHistoricTransitions->size() - 1; michael@0: tzt = (Transition*)fHistoricTransitions->elementAt(idx); michael@0: tt = tzt->time; michael@0: if (inclusive && tt == base) { michael@0: result = *tzt; michael@0: found = TRUE; michael@0: } else if (tt < base) { michael@0: if (fFinalRules != NULL) { michael@0: // Find a transion time with finalRules michael@0: TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0); michael@0: TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1); michael@0: UDate start0, start1; michael@0: UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0); michael@0: UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1); michael@0: // avail0/avail1 should be always TRUE michael@0: if (!avail0 && !avail1) { michael@0: return FALSE; michael@0: } michael@0: if (!avail1 || start0 > start1) { michael@0: result.time = start0; michael@0: result.from = r1; michael@0: result.to = r0; michael@0: } else { michael@0: result.time = start1; michael@0: result.from = r0; michael@0: result.to = r1; michael@0: } michael@0: } else { michael@0: result = *tzt; michael@0: } michael@0: found = TRUE; michael@0: } else { michael@0: // Find a transition within the historic transitions michael@0: idx--; michael@0: while (idx >= 0) { michael@0: tzt = (Transition*)fHistoricTransitions->elementAt(idx); michael@0: tt = tzt->time; michael@0: if (tt < base || (inclusive && tt == base)) { michael@0: break; michael@0: } michael@0: idx--; michael@0: } michael@0: result = *tzt; michael@0: found = TRUE; michael@0: } michael@0: } michael@0: if (found) { michael@0: // For now, this implementation ignore transitions with only zone name changes. michael@0: if (result.from->getRawOffset() == result.to->getRawOffset() michael@0: && result.from->getDSTSavings() == result.to->getDSTSavings()) { michael@0: // No offset changes. Try next one if not final michael@0: return findPrev(result.time, FALSE /* always exclusive */, michael@0: transitionTime, fromRule, toRule); michael@0: } michael@0: transitionTime = result.time; michael@0: fromRule = result.from; michael@0: toRule = result.to; michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UDate michael@0: RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local, michael@0: int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { michael@0: UDate time = transition->time; michael@0: if (local) { michael@0: time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(), michael@0: transition->to->getRawOffset(), transition->to->getDSTSavings(), michael@0: NonExistingTimeOpt, DuplicatedTimeOpt); michael@0: } michael@0: return time; michael@0: } michael@0: michael@0: int32_t michael@0: RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter, michael@0: int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const { michael@0: int32_t delta = 0; michael@0: michael@0: int32_t offsetBefore = rawBefore + dstBefore; michael@0: int32_t offsetAfter = rawAfter + dstAfter; michael@0: michael@0: UBool dstToStd = (dstBefore != 0) && (dstAfter == 0); michael@0: UBool stdToDst = (dstBefore == 0) && (dstAfter != 0); michael@0: michael@0: if (offsetAfter - offsetBefore >= 0) { michael@0: // Positive transition, which makes a non-existing local time range michael@0: if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd) michael@0: || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { michael@0: delta = offsetBefore; michael@0: } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst) michael@0: || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { michael@0: delta = offsetAfter; michael@0: } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) { michael@0: delta = offsetBefore; michael@0: } else { michael@0: // Interprets the time with rule before the transition, michael@0: // default for non-existing time range michael@0: delta = offsetAfter; michael@0: } michael@0: } else { michael@0: // Negative transition, which makes a duplicated local time range michael@0: if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd) michael@0: || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) { michael@0: delta = offsetAfter; michael@0: } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst) michael@0: || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) { michael@0: delta = offsetBefore; michael@0: } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) { michael@0: delta = offsetBefore; michael@0: } else { michael@0: // Interprets the time with rule after the transition, michael@0: // default for duplicated local time range michael@0: delta = offsetAfter; michael@0: } michael@0: } michael@0: return delta; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof michael@0: