Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2010, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include "unicode/basictz.h"
     13 #include "gregoimp.h"
     14 #include "uvector.h"
     15 #include "cmemory.h"
     16 
     17 U_NAMESPACE_BEGIN
     18 
     19 #define MILLIS_PER_YEAR (365*24*60*60*1000.0)
     20 
     21 BasicTimeZone::BasicTimeZone()
     22 : TimeZone() {
     23 }
     24 
     25 BasicTimeZone::BasicTimeZone(const UnicodeString &id)
     26 : TimeZone(id) {
     27 }
     28 
     29 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
     30 : TimeZone(source) {
     31 }
     32 
     33 BasicTimeZone::~BasicTimeZone() {
     34 }
     35 
     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);
     72 
     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         }
     98 
     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 }
    128 
    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;
    140 
    141     AnnualTimeZoneRule *ar1 = NULL;
    142     AnnualTimeZoneRule *ar2 = NULL;
    143     UnicodeString name;
    144 
    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();
    153 
    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)) {
    160 
    161             int32_t year, month, dom, dow, doy, mid;
    162             UDate d;
    163 
    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);
    171 
    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);
    180 
    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()) {
    190 
    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);
    200 
    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)) {
    221 
    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);
    228 
    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);
    233 
    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);
    273 
    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 }
    285 
    286 void
    287 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
    288                                      UVector*& transitionRules, UErrorCode& status) /*const*/ {
    289     if (U_FAILURE(status)) {
    290         return;
    291     }
    292 
    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;
    309 
    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;
    336 
    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     }
    344 
    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     }
    354 
    355     // Create initial rule
    356     tzt.getTo()->getName(name);
    357     res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
    358         tzt.getTo()->getDSTSavings());
    359 
    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     }
    366 
    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;
    382 
    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;
    428 
    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     }
    503 
    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     }
    515 
    516     initial = res_initial;
    517     transitionRules = filteredRules;
    518     return;
    519 
    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     }
    542 
    543     initial = NULL;
    544     transitionRules = NULL;
    545 }
    546 
    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 }
    555 
    556 U_NAMESPACE_END
    557 
    558 #endif /* #if !UCONFIG_NO_FORMATTING */
    559 
    560 //eof
    561