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