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