summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/rbtz.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/rbtz.cpp')
-rw-r--r--intl/icu/source/i18n/rbtz.cpp959
1 files changed, 959 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/rbtz.cpp b/intl/icu/source/i18n/rbtz.cpp
new file mode 100644
index 000000000..2b35c9562
--- /dev/null
+++ b/intl/icu/source/i18n/rbtz.cpp
@@ -0,0 +1,959 @@
+// Copyright (C) 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+*******************************************************************************
+* 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
+