intl/icu/source/i18n/rbtz.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial