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