intl/icu/source/i18n/basictz.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial