tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

rbtz.cpp (32783B)


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