tor-browser

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

basictz.cpp (21483B)


      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 "unicode/utypes.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include "unicode/basictz.h"
     15 #include "gregoimp.h"
     16 #include "uvector.h"
     17 #include "cmemory.h"
     18 
     19 U_NAMESPACE_BEGIN
     20 
     21 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
     22 
     23 BasicTimeZone::BasicTimeZone()
     24 : TimeZone() {
     25 }
     26 
     27 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
     28 : TimeZone(id) {
     29 }
     30 
     31 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
     32 : TimeZone(source) {
     33 }
     34 
     35 BasicTimeZone::~BasicTimeZone() {
     36 }
     37 
     38 UBool
     39 BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
     40                                        UBool ignoreDstAmount, UErrorCode& status) const {
     41    if (U_FAILURE(status)) {
     42        return false;
     43    }
     44    if (hasSameRules(tz)) {
     45        return true;
     46    }
     47    // Check the offsets at the start time
     48    int32_t raw1, raw2, dst1, dst2;
     49    getOffset(start, false, raw1, dst1, status);
     50    if (U_FAILURE(status)) {
     51        return false;
     52    }
     53    tz.getOffset(start, false, raw2, dst2, status);
     54    if (U_FAILURE(status)) {
     55        return false;
     56    }
     57    if (ignoreDstAmount) {
     58        if ((raw1 + dst1 != raw2 + dst2)
     59            || (dst1 != 0 && dst2 == 0)
     60            || (dst1 == 0 && dst2 != 0)) {
     61            return false;
     62        }
     63    } else {
     64        if (raw1 != raw2 || dst1 != dst2) {
     65            return false;
     66        }            
     67    }
     68    // Check transitions in the range
     69    UDate time = start;
     70    TimeZoneTransition tr1, tr2;
     71    while (true) {
     72        UBool avail1 = getNextTransition(time, false, tr1);
     73        UBool avail2 = tz.getNextTransition(time, false, tr2);
     74 
     75        if (ignoreDstAmount) {
     76            // Skip a transition which only differ the amount of DST savings
     77            while (true) {
     78                if (avail1
     79                        && tr1.getTime() <= end
     80                        && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
     81                                == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
     82                        && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
     83                    getNextTransition(tr1.getTime(), false, tr1);
     84                } else {
     85                    break;
     86                }
     87            }
     88            while (true) {
     89                if (avail2
     90                        && tr2.getTime() <= end
     91                        && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
     92                                == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
     93                        && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
     94                    tz.getNextTransition(tr2.getTime(), false, tr2);
     95                } else {
     96                    break;
     97                }
     98            }
     99        }
    100 
    101        UBool inRange1 = (avail1 && tr1.getTime() <= end);
    102        UBool inRange2 = (avail2 && tr2.getTime() <= end);
    103        if (!inRange1 && !inRange2) {
    104            // No more transition in the range
    105            break;
    106        }
    107        if (!inRange1 || !inRange2) {
    108            return false;
    109        }
    110        if (tr1.getTime() != tr2.getTime()) {
    111            return false;
    112        }
    113        if (ignoreDstAmount) {
    114            if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
    115                        != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
    116                    || (tr1.getTo()->getDSTSavings() != 0 &&  tr2.getTo()->getDSTSavings() == 0)
    117                    || (tr1.getTo()->getDSTSavings() == 0 &&  tr2.getTo()->getDSTSavings() != 0)) {
    118                return false;
    119            }
    120        } else {
    121            if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
    122                tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
    123                return false;
    124            }
    125        }
    126        time = tr1.getTime();
    127    }
    128    return true;
    129 }
    130 
    131 void
    132 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
    133        AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
    134    initial = nullptr;
    135    std = nullptr;
    136    dst = nullptr;
    137    if (U_FAILURE(status)) {
    138        return;
    139    }
    140    int32_t initialRaw, initialDst;
    141    UnicodeString initialName;
    142 
    143    LocalPointer<AnnualTimeZoneRule> ar1;
    144    LocalPointer<AnnualTimeZoneRule> ar2;
    145    UnicodeString name;
    146 
    147    UBool avail;
    148    TimeZoneTransition tr;
    149    // Get the next transition
    150    avail = getNextTransition(date, false, tr);
    151    if (avail) {
    152        tr.getFrom()->getName(initialName);
    153        initialRaw = tr.getFrom()->getRawOffset();
    154        initialDst = tr.getFrom()->getDSTSavings();
    155 
    156        // Check if the next transition is either DST->STD or STD->DST and
    157        // within roughly 1 year from the specified date
    158        UDate nextTransitionTime = tr.getTime();
    159        if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
    160              || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
    161            && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
    162 
    163            int32_t year, mid;
    164            int8_t month, dom, dow;
    165            UDate d;
    166 
    167            // Get local wall time for the next transition time
    168            Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
    169                year, month, dom, dow, mid, status);
    170            if (U_FAILURE(status)) return;
    171            int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
    172            // Create DOW rule
    173            DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
    174            tr.getTo()->getName(name);
    175 
    176            // Note:  SimpleTimeZone does not support raw offset change.
    177            // So we always use raw offset of the given time for the rule,
    178            // even raw offset is changed.  This will result that the result
    179            // zone to return wrong offset after the transition.
    180            // When we encounter such case, we do not inspect next next
    181            // transition for another rule.
    182            ar1.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
    183                dtr, year, AnnualTimeZoneRule::MAX_YEAR));
    184 
    185            if (tr.getTo()->getRawOffset() == initialRaw) {
    186                // Get the next next transition
    187                avail = getNextTransition(nextTransitionTime, false, tr);
    188                if (avail) {
    189                    // Check if the next next transition is either DST->STD or STD->DST
    190                    // and within roughly 1 year from the next transition
    191                    if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
    192                          || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
    193                         && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
    194 
    195                        // Get local wall time for the next transition time
    196                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
    197                            year, month, dom, dow, mid, status);
    198                        if (U_FAILURE(status)) return;
    199                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
    200                        // Generate another DOW rule
    201                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
    202                        tr.getTo()->getName(name);
    203                        ar2.adoptInstead(new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
    204                            dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR));
    205 
    206                        // Make sure this rule can be applied to the specified date
    207                        avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d);
    208                        if (!avail || d > date
    209                                || initialRaw != tr.getTo()->getRawOffset()
    210                                || initialDst != tr.getTo()->getDSTSavings()) {
    211                            // We cannot use this rule as the second transition rule
    212                            ar2.adoptInstead(nullptr);
    213                        }
    214                    }
    215                }
    216            }
    217            if (ar2.isNull()) {
    218                // Try previous transition
    219                avail = getPreviousTransition(date, true, tr);
    220                if (avail) {
    221                    // Check if the previous transition is either DST->STD or STD->DST.
    222                    // The actual transition time does not matter here.
    223                    if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
    224                        || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
    225 
    226                        // Generate another DOW rule
    227                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
    228                            year, month, dom, dow, mid, status);
    229                        if (U_FAILURE(status)) return;
    230                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
    231                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
    232                        tr.getTo()->getName(name);
    233 
    234                        // second rule raw/dst offsets should match raw/dst offsets
    235                        // at the given time
    236                        ar2.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, initialDst,
    237                            dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR));
    238 
    239                        // Check if this rule start after the first rule after the specified date
    240                        avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d);
    241                        if (!avail || d <= nextTransitionTime) {
    242                            // We cannot use this rule as the second transition rule
    243                            ar2.adoptInstead(nullptr);
    244                        }
    245                    }
    246                }
    247            }
    248            if (ar2.isNull()) {
    249                // Cannot find a good pair of AnnualTimeZoneRule
    250                ar1.adoptInstead(nullptr);
    251            } else {
    252                // The initial rule should represent the rule before the previous transition
    253                ar1->getName(initialName);
    254                initialRaw = ar1->getRawOffset();
    255                initialDst = ar1->getDSTSavings();
    256            }
    257        }
    258    }
    259    else {
    260        // Try the previous one
    261        avail = getPreviousTransition(date, true, tr);
    262        if (avail) {
    263            tr.getTo()->getName(initialName);
    264            initialRaw = tr.getTo()->getRawOffset();
    265            initialDst = tr.getTo()->getDSTSavings();
    266        } else {
    267            // No transitions in the past.  Just use the current offsets
    268            getOffset(date, false, initialRaw, initialDst, status);
    269            if (U_FAILURE(status)) {
    270                return;
    271            }
    272        }
    273    }
    274    // Set the initial rule
    275    initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
    276 
    277    // Set the standard and daylight saving rules
    278    if (ar1.isValid() && ar2.isValid()) {
    279        if (ar1->getDSTSavings() != 0) {
    280            dst = ar1.orphan();
    281            std = ar2.orphan();
    282        } else {
    283            std = ar1.orphan();
    284            dst = ar2.orphan();
    285        }
    286    }
    287 }
    288 
    289 void
    290 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
    291                                     UVector*& transitionRules, UErrorCode& status) const {
    292    if (U_FAILURE(status)) {
    293        return;
    294    }
    295 
    296    const InitialTimeZoneRule *orgini;
    297    TimeZoneTransition tzt;
    298    bool avail;
    299    int32_t ruleCount;
    300    TimeZoneRule *r = nullptr;
    301    UnicodeString name;
    302    int32_t i;
    303    UDate time, t;
    304    UDate firstStart;
    305    UBool bFinalStd = false, bFinalDst = false;
    306 
    307    initial = nullptr;
    308    transitionRules = nullptr;
    309 
    310    // Original transition rules
    311    ruleCount = countTransitionRules(status);
    312    if (U_FAILURE(status)) {
    313        return;
    314    }
    315    LocalPointer<UVector> orgRules(
    316        new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status);
    317    if (U_FAILURE(status)) {
    318        return;
    319    }
    320    LocalMemory<const TimeZoneRule *> orgtrs(
    321        static_cast<const TimeZoneRule **>(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount)));
    322    if (orgtrs.isNull()) {
    323        status = U_MEMORY_ALLOCATION_ERROR;
    324        return;
    325    }
    326    getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status);
    327    if (U_FAILURE(status)) {
    328        return;
    329    }
    330    for (i = 0; i < ruleCount; i++) {
    331        LocalPointer<TimeZoneRule> lpRule(orgtrs[i]->clone(), status);
    332        orgRules->adoptElement(lpRule.orphan(), status);
    333        if (U_FAILURE(status)) {
    334            return;
    335        }
    336    }
    337 
    338    avail = getPreviousTransition(start, true, tzt);
    339    if (!avail) {
    340        // No need to filter out rules only applicable to time before the start
    341        initial = orgini->clone();
    342        if (initial == nullptr) {
    343            status = U_MEMORY_ALLOCATION_ERROR;
    344            return;
    345        }
    346        transitionRules = orgRules.orphan();
    347        return;
    348    }
    349 
    350    LocalMemory<bool> done(static_cast<bool *>(uprv_malloc(sizeof(bool)*ruleCount)));
    351    if (done.isNull()) {
    352        status = U_MEMORY_ALLOCATION_ERROR;
    353        return;
    354    }
    355    LocalPointer<UVector> filteredRules(
    356        new UVector(uprv_deleteUObject, nullptr, status), status);
    357    if (U_FAILURE(status)) {
    358        return;
    359    }
    360 
    361    // Create initial rule
    362    tzt.getTo()->getName(name);
    363    LocalPointer<InitialTimeZoneRule> res_initial(
    364        new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status);
    365    if (U_FAILURE(status)) {
    366        return;
    367    }
    368 
    369    // Mark rules which does not need to be processed
    370    for (i = 0; i < ruleCount; i++) {
    371        r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
    372        avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time);
    373        done[i] = !avail;
    374    }
    375 
    376    time = start;
    377    while (!bFinalStd || !bFinalDst) {
    378        avail = getNextTransition(time, false, tzt);
    379        if (!avail) {
    380            break;
    381        }
    382        UDate updatedTime = tzt.getTime();
    383        if (updatedTime == time) {
    384            // Can get here if rules for start & end of daylight time have exactly
    385            // the same time.  
    386            // TODO:  fix getNextTransition() to prevent it?
    387            status = U_INVALID_STATE_ERROR;
    388            return;
    389        }
    390        time = updatedTime;
    391 
    392        const TimeZoneRule *toRule = tzt.getTo();
    393        for (i = 0; i < ruleCount; i++) {
    394            r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
    395            if (*r == *toRule) {
    396                break;
    397            }
    398        }
    399        if (i >= ruleCount) {
    400            // This case should never happen
    401            status = U_INVALID_STATE_ERROR;
    402            return;
    403        }
    404        if (done[i]) {
    405            continue;
    406        }
    407        const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
    408        const AnnualTimeZoneRule *ar;
    409        if (tar != nullptr) {
    410            // Get the previous raw offset and DST savings before the very first start time
    411            TimeZoneTransition tzt0;
    412            t = start;
    413            while (true) {
    414                avail = getNextTransition(t, false, tzt0);
    415                if (!avail) {
    416                    break;
    417                }
    418                if (*(tzt0.getTo()) == *tar) {
    419                    break;
    420                }
    421                t = tzt0.getTime();
    422            }
    423            if (avail) {
    424                // Check if the entire start times to be added
    425                tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
    426                if (firstStart > start) {
    427                    // Just add the rule as is
    428                    LocalPointer<TimeArrayTimeZoneRule> lpTar(tar->clone(), status);
    429                    filteredRules->adoptElement(lpTar.orphan(), status);
    430                    if (U_FAILURE(status)) {
    431                        return;
    432                    }
    433                } else {
    434                    // Collect transitions after the start time
    435                    int32_t startTimes;
    436                    DateTimeRule::TimeRuleType timeType;
    437                    int32_t idx;
    438 
    439                    startTimes = tar->countStartTimes();
    440                    timeType = tar->getTimeType();
    441                    for (idx = 0; idx < startTimes; idx++) {
    442                        tar->getStartTimeAt(idx, t);
    443                        if (timeType == DateTimeRule::STANDARD_TIME) {
    444                            t -= tzt.getFrom()->getRawOffset();
    445                        }
    446                        if (timeType == DateTimeRule::WALL_TIME) {
    447                            t -= tzt.getFrom()->getDSTSavings();
    448                        }
    449                        if (t > start) {
    450                            break;
    451                        }
    452                    }
    453                    if (U_FAILURE(status)) {
    454                        return;
    455                    }
    456                    int32_t asize = startTimes - idx;
    457                    if (asize > 0) {
    458                        LocalMemory<UDate> newTimes(static_cast<UDate *>(uprv_malloc(sizeof(UDate) * asize)));
    459                        if (newTimes.isNull()) {
    460                            status = U_MEMORY_ALLOCATION_ERROR;
    461                            return;
    462                        }
    463                        for (int32_t newidx = 0; newidx < asize; newidx++) {
    464                            tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
    465                        }
    466                        tar->getName(name);
    467                        LocalPointer<TimeArrayTimeZoneRule> newTar(new TimeArrayTimeZoneRule(
    468                                name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status);
    469                        filteredRules->adoptElement(newTar.orphan(), status);
    470                        if (U_FAILURE(status)) {
    471                            return;
    472                        }
    473                    }
    474                }
    475            }
    476        } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != nullptr) {
    477            ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
    478            if (firstStart == tzt.getTime()) {
    479                // Just add the rule as is
    480                LocalPointer<AnnualTimeZoneRule> arClone(ar->clone(), status);
    481                filteredRules->adoptElement(arClone.orphan(), status);
    482                if (U_FAILURE(status)) {
    483                    return;
    484                }
    485            } else {
    486                // Calculate the transition year
    487                int32_t year = Grego::timeToYear(tzt.getTime(), status);
    488                if (U_FAILURE(status)) {
    489                    return;
    490                }
    491                // Re-create the rule
    492                ar->getName(name);
    493                LocalPointer<AnnualTimeZoneRule> newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
    494                    *(ar->getRule()), year, ar->getEndYear()), status);
    495                filteredRules->adoptElement(newAr.orphan(), status);
    496                if (U_FAILURE(status)) {
    497                    return;
    498                }
    499            }
    500            // check if this is a final rule
    501            if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
    502                // After bot final standard and dst rules are processed,
    503                // exit this while loop.
    504                if (ar->getDSTSavings() == 0) {
    505                    bFinalStd = true;
    506                } else {
    507                    bFinalDst = true;
    508                }
    509            }
    510        }
    511        done[i] = true;
    512    }
    513 
    514    // Set the results
    515    initial = res_initial.orphan();
    516    transitionRules = filteredRules.orphan();
    517 }
    518 
    519 void
    520 BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/,
    521                                  UTimeZoneLocalOption /*duplicatedTimeOpt*/,
    522                                  int32_t& /*rawOffset*/, int32_t& /*dstOffset*/,
    523                                  UErrorCode& status) const {
    524    if (U_FAILURE(status)) {
    525        return;
    526    }
    527    status = U_UNSUPPORTED_ERROR;
    528 }
    529 
    530 void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
    531                                       int32_t& rawOffset, int32_t& dstOffset,
    532                                       UErrorCode& status) const {
    533    getOffsetFromLocal(date, static_cast<UTimeZoneLocalOption>(nonExistingTimeOpt),
    534                       static_cast<UTimeZoneLocalOption>(duplicatedTimeOpt), rawOffset, dstOffset, status);
    535 }
    536 
    537 U_NAMESPACE_END
    538 
    539 #endif /* #if !UCONFIG_NO_FORMATTING */
    540 
    541 //eof