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