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