Home | History | Annotate | Download | only in i18n
      1 /*
      2  *******************************************************************************
      3  * Copyright (C) 1997-2013, International Business Machines Corporation and
      4  * others. All Rights Reserved.
      5  *******************************************************************************
      6  *
      7  * File SIMPLETZ.H
      8  *
      9  * Modification History:
     10  *
     11  *   Date        Name        Description
     12  *   12/05/96    clhuang     Creation.
     13  *   04/21/97    aliu        Fixed miscellaneous bugs found by inspection and
     14  *                           testing.
     15  *   07/29/97    aliu        Ported source bodies back from Java version with
     16  *                           numerous feature enhancements and bug fixes.
     17  *   08/10/98    stephen     JDK 1.2 sync.
     18  *   09/17/98    stephen     Fixed getOffset() for last hour of year and DST
     19  *   12/02/99    aliu        Added TimeMode and constructor and setStart/EndRule
     20  *                           methods that take TimeMode. Whitespace cleanup.
     21  ********************************************************************************
     22  */
     23 
     24 #include "utypeinfo.h"  // for 'typeid' to work
     25 
     26 #include "unicode/utypes.h"
     27 
     28 #if !UCONFIG_NO_FORMATTING
     29 
     30 #include "unicode/simpletz.h"
     31 #include "unicode/gregocal.h"
     32 #include "unicode/smpdtfmt.h"
     33 
     34 #include "gregoimp.h"
     35 #include "umutex.h"
     36 
     37 U_NAMESPACE_BEGIN
     38 
     39 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
     40 
     41 // Use only for decodeStartRule() and decodeEndRule() where the year is not
     42 // available. Set February to 29 days to accomodate rules with that date
     43 // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
     44 // The compareToRule() method adjusts to February 28 in non-leap years.
     45 //
     46 // For actual getOffset() calculations, use Grego::monthLength() and
     47 // Grego::previousMonthLength() which take leap years into account.
     48 // We handle leap years assuming always
     49 // Gregorian, since we know they didn't have daylight time when
     50 // Gregorian calendar started.
     51 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
     52 
     53 static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)"
     54 static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)"
     55 
     56 
     57 // *****************************************************************************
     58 // class SimpleTimeZone
     59 // *****************************************************************************
     60 
     61 
     62 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
     63 :   BasicTimeZone(ID),
     64     startMonth(0),
     65     startDay(0),
     66     startDayOfWeek(0),
     67     startTime(0),
     68     startTimeMode(WALL_TIME),
     69     endTimeMode(WALL_TIME),
     70     endMonth(0),
     71     endDay(0),
     72     endDayOfWeek(0),
     73     endTime(0),
     74     startYear(0),
     75     rawOffset(rawOffsetGMT),
     76     useDaylight(FALSE),
     77     startMode(DOM_MODE),
     78     endMode(DOM_MODE),
     79     dstSavings(U_MILLIS_PER_HOUR)
     80 {
     81     clearTransitionRules();
     82 }
     83 
     84 // -------------------------------------
     85 
     86 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
     87     int8_t savingsStartMonth, int8_t savingsStartDay,
     88     int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
     89     int8_t savingsEndMonth, int8_t savingsEndDay,
     90     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     91     UErrorCode& status)
     92 :   BasicTimeZone(ID)
     93 {
     94     clearTransitionRules();
     95     construct(rawOffsetGMT,
     96               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
     97               savingsStartTime, WALL_TIME,
     98               savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
     99               savingsEndTime, WALL_TIME,
    100               U_MILLIS_PER_HOUR, status);
    101 }
    102 
    103 // -------------------------------------
    104 
    105 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
    106     int8_t savingsStartMonth, int8_t savingsStartDay,
    107     int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
    108     int8_t savingsEndMonth, int8_t savingsEndDay,
    109     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
    110     int32_t savingsDST, UErrorCode& status)
    111 :   BasicTimeZone(ID)
    112 {
    113     clearTransitionRules();
    114     construct(rawOffsetGMT,
    115               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
    116               savingsStartTime, WALL_TIME,
    117               savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
    118               savingsEndTime, WALL_TIME,
    119               savingsDST, status);
    120 }
    121 
    122 // -------------------------------------
    123 
    124 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
    125     int8_t savingsStartMonth, int8_t savingsStartDay,
    126     int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
    127     TimeMode savingsStartTimeMode,
    128     int8_t savingsEndMonth, int8_t savingsEndDay,
    129     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
    130     TimeMode savingsEndTimeMode,
    131     int32_t savingsDST, UErrorCode& status)
    132 :   BasicTimeZone(ID)
    133 {
    134     clearTransitionRules();
    135     construct(rawOffsetGMT,
    136               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
    137               savingsStartTime, savingsStartTimeMode,
    138               savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
    139               savingsEndTime, savingsEndTimeMode,
    140               savingsDST, status);
    141 }
    142 
    143 /**
    144  * Internal construction method.
    145  */
    146 void SimpleTimeZone::construct(int32_t rawOffsetGMT,
    147                                int8_t savingsStartMonth,
    148                                int8_t savingsStartDay,
    149                                int8_t savingsStartDayOfWeek,
    150                                int32_t savingsStartTime,
    151                                TimeMode savingsStartTimeMode,
    152                                int8_t savingsEndMonth,
    153                                int8_t savingsEndDay,
    154                                int8_t savingsEndDayOfWeek,
    155                                int32_t savingsEndTime,
    156                                TimeMode savingsEndTimeMode,
    157                                int32_t savingsDST,
    158                                UErrorCode& status)
    159 {
    160     this->rawOffset      = rawOffsetGMT;
    161     this->startMonth     = savingsStartMonth;
    162     this->startDay       = savingsStartDay;
    163     this->startDayOfWeek = savingsStartDayOfWeek;
    164     this->startTime      = savingsStartTime;
    165     this->startTimeMode  = savingsStartTimeMode;
    166     this->endMonth       = savingsEndMonth;
    167     this->endDay         = savingsEndDay;
    168     this->endDayOfWeek   = savingsEndDayOfWeek;
    169     this->endTime        = savingsEndTime;
    170     this->endTimeMode    = savingsEndTimeMode;
    171     this->dstSavings     = savingsDST;
    172     this->startYear      = 0;
    173     this->startMode      = DOM_MODE;
    174     this->endMode        = DOM_MODE;
    175 
    176     decodeRules(status);
    177 
    178     if (savingsDST <= 0) {
    179         status = U_ILLEGAL_ARGUMENT_ERROR;
    180     }
    181 }
    182 
    183 // -------------------------------------
    184 
    185 SimpleTimeZone::~SimpleTimeZone()
    186 {
    187     deleteTransitionRules();
    188 }
    189 
    190 // -------------------------------------
    191 
    192 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
    193 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
    194 :   BasicTimeZone(source)
    195 {
    196     *this = source;
    197 }
    198 
    199 // -------------------------------------
    200 
    201 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
    202 SimpleTimeZone &
    203 SimpleTimeZone::operator=(const SimpleTimeZone &right)
    204 {
    205     if (this != &right)
    206     {
    207         TimeZone::operator=(right);
    208         rawOffset      = right.rawOffset;
    209         startMonth     = right.startMonth;
    210         startDay       = right.startDay;
    211         startDayOfWeek = right.startDayOfWeek;
    212         startTime      = right.startTime;
    213         startTimeMode  = right.startTimeMode;
    214         startMode      = right.startMode;
    215         endMonth       = right.endMonth;
    216         endDay         = right.endDay;
    217         endDayOfWeek   = right.endDayOfWeek;
    218         endTime        = right.endTime;
    219         endTimeMode    = right.endTimeMode;
    220         endMode        = right.endMode;
    221         startYear      = right.startYear;
    222         dstSavings     = right.dstSavings;
    223         useDaylight    = right.useDaylight;
    224         clearTransitionRules();
    225     }
    226     return *this;
    227 }
    228 
    229 // -------------------------------------
    230 
    231 UBool
    232 SimpleTimeZone::operator==(const TimeZone& that) const
    233 {
    234     return ((this == &that) ||
    235             (typeid(*this) == typeid(that) &&
    236             TimeZone::operator==(that) &&
    237             hasSameRules(that)));
    238 }
    239 
    240 // -------------------------------------
    241 
    242 // Called by TimeZone::createDefault() inside a Mutex - be careful.
    243 TimeZone*
    244 SimpleTimeZone::clone() const
    245 {
    246     return new SimpleTimeZone(*this);
    247 }
    248 
    249 // -------------------------------------
    250 
    251 /**
    252  * Sets the daylight savings starting year, that is, the year this time zone began
    253  * observing its specified daylight savings time rules.  The time zone is considered
    254  * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
    255  * support historical daylight-savings-time rules.
    256  * @param year the daylight savings starting year.
    257  */
    258 void
    259 SimpleTimeZone::setStartYear(int32_t year)
    260 {
    261     startYear = year;
    262     transitionRulesInitialized = FALSE;
    263 }
    264 
    265 // -------------------------------------
    266 
    267 /**
    268  * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
    269  * Time starts at the first Sunday in April, at 2 AM in standard time.
    270  * Therefore, you can set the start rule by calling:
    271  * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
    272  * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
    273  * the exact starting date.  Their exact meaning depend on their respective signs,
    274  * allowing various types of rules to be constructed, as follows:<ul>
    275  *   <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
    276  *       day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
    277  *       of the month).
    278  *   <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
    279  *       the day of week in the month counting backward from the end of the month.
    280  *       (e.g., (-1, MONDAY) is the last Monday in the month)
    281  *   <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
    282  *       specifies the day of the month, regardless of what day of the week it is.
    283  *       (e.g., (10, 0) is the tenth day of the month)
    284  *   <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
    285  *       specifies the day of the month counting backward from the end of the
    286  *       month, regardless of what day of the week it is (e.g., (-2, 0) is the
    287  *       next-to-last day of the month).
    288  *   <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
    289  *       first specified day of the week on or after the specfied day of the month.
    290  *       (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
    291  *       [or the 15th itself if the 15th is a Sunday].)
    292  *   <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
    293  *       last specified day of the week on or before the specified day of the month.
    294  *       (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
    295  *       [or the 20th itself if the 20th is a Tuesday].)</ul>
    296  * @param month the daylight savings starting month. Month is 0-based.
    297  * eg, 0 for January.
    298  * @param dayOfWeekInMonth the daylight savings starting
    299  * day-of-week-in-month. Please see the member description for an example.
    300  * @param dayOfWeek the daylight savings starting day-of-week. Please see
    301  * the member description for an example.
    302  * @param time the daylight savings starting time. Please see the member
    303  * description for an example.
    304  */
    305 
    306 void
    307 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
    308                              int32_t time, TimeMode mode, UErrorCode& status)
    309 {
    310     startMonth     = (int8_t)month;
    311     startDay       = (int8_t)dayOfWeekInMonth;
    312     startDayOfWeek = (int8_t)dayOfWeek;
    313     startTime      = time;
    314     startTimeMode  = mode;
    315     decodeStartRule(status);
    316     transitionRulesInitialized = FALSE;
    317 }
    318 
    319 // -------------------------------------
    320 
    321 void
    322 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
    323                              int32_t time, TimeMode mode, UErrorCode& status)
    324 {
    325     setStartRule(month, dayOfMonth, 0, time, mode, status);
    326 }
    327 
    328 // -------------------------------------
    329 
    330 void
    331 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
    332                              int32_t time, TimeMode mode, UBool after, UErrorCode& status)
    333 {
    334     setStartRule(month, after ? dayOfMonth : -dayOfMonth,
    335                  -dayOfWeek, time, mode, status);
    336 }
    337 
    338 // -------------------------------------
    339 
    340 /**
    341  * Sets the daylight savings ending rule. For example, in the U.S., Daylight
    342  * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
    343  * Therefore, you can set the end rule by calling:
    344  * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
    345  * Various other types of rules can be specified by manipulating the dayOfWeek
    346  * and dayOfWeekInMonth parameters.  For complete details, see the documentation
    347  * for setStartRule().
    348  * @param month the daylight savings ending month. Month is 0-based.
    349  * eg, 0 for January.
    350  * @param dayOfWeekInMonth the daylight savings ending
    351  * day-of-week-in-month. See setStartRule() for a complete explanation.
    352  * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
    353  * for a complete explanation.
    354  * @param time the daylight savings ending time. Please see the member
    355  * description for an example.
    356  */
    357 
    358 void
    359 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
    360                            int32_t time, TimeMode mode, UErrorCode& status)
    361 {
    362     endMonth     = (int8_t)month;
    363     endDay       = (int8_t)dayOfWeekInMonth;
    364     endDayOfWeek = (int8_t)dayOfWeek;
    365     endTime      = time;
    366     endTimeMode  = mode;
    367     decodeEndRule(status);
    368     transitionRulesInitialized = FALSE;
    369 }
    370 
    371 // -------------------------------------
    372 
    373 void
    374 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
    375                            int32_t time, TimeMode mode, UErrorCode& status)
    376 {
    377     setEndRule(month, dayOfMonth, 0, time, mode, status);
    378 }
    379 
    380 // -------------------------------------
    381 
    382 void
    383 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
    384                            int32_t time, TimeMode mode, UBool after, UErrorCode& status)
    385 {
    386     setEndRule(month, after ? dayOfMonth : -dayOfMonth,
    387                -dayOfWeek, time, mode, status);
    388 }
    389 
    390 // -------------------------------------
    391 
    392 int32_t
    393 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
    394                           uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
    395 {
    396     // Check the month before calling Grego::monthLength(). This
    397     // duplicates the test that occurs in the 7-argument getOffset(),
    398     // however, this is unavoidable. We don't mind because this method, in
    399     // fact, should not be called; internal code should always call the
    400     // 7-argument getOffset(), and outside code should use Calendar.get(int
    401     // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
    402     // this method because it's public API. - liu 8/10/98
    403     if(month < UCAL_JANUARY || month > UCAL_DECEMBER) {
    404         status = U_ILLEGAL_ARGUMENT_ERROR;
    405         return 0;
    406     }
    407 
    408     return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status);
    409 }
    410 
    411 int32_t
    412 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
    413                           uint8_t dayOfWeek, int32_t millis,
    414                           int32_t /*monthLength*/, UErrorCode& status) const
    415 {
    416     // Check the month before calling Grego::monthLength(). This
    417     // duplicates a test that occurs in the 9-argument getOffset(),
    418     // however, this is unavoidable. We don't mind because this method, in
    419     // fact, should not be called; internal code should always call the
    420     // 9-argument getOffset(), and outside code should use Calendar.get(int
    421     // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
    422     // this method because it's public API. - liu 8/10/98
    423     if (month < UCAL_JANUARY
    424         || month > UCAL_DECEMBER) {
    425         status = U_ILLEGAL_ARGUMENT_ERROR;
    426         return -1;
    427     }
    428 
    429     // We ignore monthLength because it can be derived from year and month.
    430     // This is so that February in leap years is calculated correctly.
    431     // We keep this argument in this function for backwards compatibility.
    432     return getOffset(era, year, month, day, dayOfWeek, millis,
    433                      Grego::monthLength(year, month),
    434                      Grego::previousMonthLength(year, month),
    435                      status);
    436 }
    437 
    438 int32_t
    439 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
    440                           uint8_t dayOfWeek, int32_t millis,
    441                           int32_t monthLength, int32_t prevMonthLength,
    442                           UErrorCode& status) const
    443 {
    444     if(U_FAILURE(status)) return 0;
    445 
    446     if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
    447         || month < UCAL_JANUARY
    448         || month > UCAL_DECEMBER
    449         || day < 1
    450         || day > monthLength
    451         || dayOfWeek < UCAL_SUNDAY
    452         || dayOfWeek > UCAL_SATURDAY
    453         || millis < 0
    454         || millis >= U_MILLIS_PER_DAY
    455         || monthLength < 28
    456         || monthLength > 31
    457         || prevMonthLength < 28
    458         || prevMonthLength > 31) {
    459         status = U_ILLEGAL_ARGUMENT_ERROR;
    460         return -1;
    461     }
    462 
    463     int32_t result = rawOffset;
    464 
    465     // Bail out if we are before the onset of daylight savings time
    466     if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
    467         return result;
    468 
    469     // Check for southern hemisphere.  We assume that the start and end
    470     // month are different.
    471     UBool southern = (startMonth > endMonth);
    472 
    473     // Compare the date to the starting and ending rules.+1 = date>rule, -1
    474     // = date<rule, 0 = date==rule.
    475     int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
    476                                          (int8_t)day, (int8_t)dayOfWeek, millis,
    477                                          startTimeMode == UTC_TIME ? -rawOffset : 0,
    478                                          startMode, (int8_t)startMonth, (int8_t)startDayOfWeek,
    479                                          (int8_t)startDay, startTime);
    480     int32_t endCompare = 0;
    481 
    482     /* We don't always have to compute endCompare.  For many instances,
    483      * startCompare is enough to determine if we are in DST or not.  In the
    484      * northern hemisphere, if we are before the start rule, we can't have
    485      * DST.  In the southern hemisphere, if we are after the start rule, we
    486      * must have DST.  This is reflected in the way the next if statement
    487      * (not the one immediately following) short circuits. */
    488     if(southern != (startCompare >= 0)) {
    489         endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
    490                                    (int8_t)day, (int8_t)dayOfWeek, millis,
    491                                    endTimeMode == WALL_TIME ? dstSavings :
    492                                     (endTimeMode == UTC_TIME ? -rawOffset : 0),
    493                                    endMode, (int8_t)endMonth, (int8_t)endDayOfWeek,
    494                                    (int8_t)endDay, endTime);
    495     }
    496 
    497     // Check for both the northern and southern hemisphere cases.  We
    498     // assume that in the northern hemisphere, the start rule is before the
    499     // end rule within the calendar year, and vice versa for the southern
    500     // hemisphere.
    501     if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
    502         (southern && (startCompare >= 0 || endCompare < 0)))
    503         result += dstSavings;
    504 
    505     return result;
    506 }
    507 
    508 void
    509 SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
    510                                    int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const {
    511     if (U_FAILURE(status)) {
    512         return;
    513     }
    514 
    515     rawOffsetGMT = getRawOffset();
    516     int32_t year, month, dom, dow;
    517     double day = uprv_floor(date / U_MILLIS_PER_DAY);
    518     int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
    519 
    520     Grego::dayToFields(day, year, month, dom, dow);
    521 
    522     savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
    523                           (uint8_t) dow, millis,
    524                           Grego::monthLength(year, month),
    525                           status) - rawOffsetGMT;
    526     if (U_FAILURE(status)) {
    527         return;
    528     }
    529 
    530     UBool recalc = FALSE;
    531 
    532     // Now we need some adjustment
    533     if (savingsDST > 0) {
    534         if ((nonExistingTimeOpt & kStdDstMask) == kStandard
    535             || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
    536             date -= getDSTSavings();
    537             recalc = TRUE;
    538         }
    539     } else {
    540         if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
    541                 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
    542             date -= getDSTSavings();
    543             recalc = TRUE;
    544         }
    545     }
    546     if (recalc) {
    547         day = uprv_floor(date / U_MILLIS_PER_DAY);
    548         millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
    549         Grego::dayToFields(day, year, month, dom, dow);
    550         savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
    551                           (uint8_t) dow, millis,
    552                           Grego::monthLength(year, month),
    553                           status) - rawOffsetGMT;
    554     }
    555 }
    556 
    557 // -------------------------------------
    558 
    559 /**
    560  * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
    561  * on whether the date is after, equal to, or before the rule date. The
    562  * millis are compared directly against the ruleMillis, so any
    563  * standard-daylight adjustments must be handled by the caller.
    564  *
    565  * @return  1 if the date is after the rule date, -1 if the date is before
    566  *          the rule date, or 0 if the date is equal to the rule date.
    567  */
    568 int32_t
    569 SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
    570                               int8_t dayOfMonth,
    571                               int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
    572                               EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
    573                               int8_t ruleDay, int32_t ruleMillis)
    574 {
    575     // Make adjustments for startTimeMode and endTimeMode
    576     millis += millisDelta;
    577     while (millis >= U_MILLIS_PER_DAY) {
    578         millis -= U_MILLIS_PER_DAY;
    579         ++dayOfMonth;
    580         dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
    581         if (dayOfMonth > monthLen) {
    582             dayOfMonth = 1;
    583             /* When incrementing the month, it is desirible to overflow
    584              * from DECEMBER to DECEMBER+1, since we use the result to
    585              * compare against a real month. Wraparound of the value
    586              * leads to bug 4173604. */
    587             ++month;
    588         }
    589     }
    590     while (millis < 0) {
    591         millis += U_MILLIS_PER_DAY;
    592         --dayOfMonth;
    593         dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
    594         if (dayOfMonth < 1) {
    595             dayOfMonth = prevMonthLen;
    596             --month;
    597         }
    598     }
    599 
    600     // first compare months.  If they're different, we don't have to worry about days
    601     // and times
    602     if (month < ruleMonth) return -1;
    603     else if (month > ruleMonth) return 1;
    604 
    605     // calculate the actual day of month for the rule
    606     int32_t ruleDayOfMonth = 0;
    607 
    608     // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
    609     if (ruleDay > monthLen) {
    610         ruleDay = monthLen;
    611     }
    612 
    613     switch (ruleMode)
    614     {
    615     // if the mode is day-of-month, the day of month is given
    616     case DOM_MODE:
    617         ruleDayOfMonth = ruleDay;
    618         break;
    619 
    620     // if the mode is day-of-week-in-month, calculate the day-of-month from it
    621     case DOW_IN_MONTH_MODE:
    622         // In this case ruleDay is the day-of-week-in-month (this code is using
    623         // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
    624         // of the first day of the month, so it's trusting that they're really
    625         // consistent with each other)
    626         if (ruleDay > 0)
    627             ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
    628                 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
    629 
    630         // if ruleDay is negative (we assume it's not zero here), we have to do
    631         // the same calculation figuring backward from the last day of the month.
    632         else
    633         {
    634             // (again, this code is trusting that dayOfWeek and dayOfMonth are
    635             // consistent with each other here, since we're using them to figure
    636             // the day of week of the first of the month)
    637             ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
    638                 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
    639         }
    640         break;
    641 
    642     case DOW_GE_DOM_MODE:
    643         ruleDayOfMonth = ruleDay +
    644             (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
    645         break;
    646 
    647     case DOW_LE_DOM_MODE:
    648         ruleDayOfMonth = ruleDay -
    649             (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
    650         // Note at this point ruleDayOfMonth may be <1, although it will
    651         // be >=1 for well-formed rules.
    652         break;
    653     }
    654 
    655     // now that we have a real day-in-month for the rule, we can compare days...
    656     if (dayOfMonth < ruleDayOfMonth) return -1;
    657     else if (dayOfMonth > ruleDayOfMonth) return 1;
    658 
    659     // ...and if they're equal, we compare times
    660     if (millis < ruleMillis) return -1;
    661     else if (millis > ruleMillis) return 1;
    662     else return 0;
    663 }
    664 
    665 // -------------------------------------
    666 
    667 int32_t
    668 SimpleTimeZone::getRawOffset() const
    669 {
    670     return rawOffset;
    671 }
    672 
    673 // -------------------------------------
    674 
    675 void
    676 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
    677 {
    678     rawOffset = offsetMillis;
    679     transitionRulesInitialized = FALSE;
    680 }
    681 
    682 // -------------------------------------
    683 
    684 void
    685 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
    686 {
    687     if (millisSavedDuringDST <= 0) {
    688         status = U_ILLEGAL_ARGUMENT_ERROR;
    689     }
    690     else {
    691         dstSavings = millisSavedDuringDST;
    692     }
    693     transitionRulesInitialized = FALSE;
    694 }
    695 
    696 // -------------------------------------
    697 
    698 int32_t
    699 SimpleTimeZone::getDSTSavings() const
    700 {
    701     return dstSavings;
    702 }
    703 
    704 // -------------------------------------
    705 
    706 UBool
    707 SimpleTimeZone::useDaylightTime() const
    708 {
    709     return useDaylight;
    710 }
    711 
    712 // -------------------------------------
    713 
    714 /**
    715  * Overrides TimeZone
    716  * Queries if the given date is in Daylight Savings Time.
    717  */
    718 UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
    719 {
    720     // This method is wasteful since it creates a new GregorianCalendar and
    721     // deletes it each time it is called.  However, this is a deprecated method
    722     // and provided only for Java compatibility as of 8/6/97 [LIU].
    723     if (U_FAILURE(status)) return FALSE;
    724     GregorianCalendar *gc = new GregorianCalendar(*this, status);
    725     /* test for NULL */
    726     if (gc == 0) {
    727         status = U_MEMORY_ALLOCATION_ERROR;
    728         return FALSE;
    729     }
    730     gc->setTime(date, status);
    731     UBool result = gc->inDaylightTime(status);
    732     delete gc;
    733     return result;
    734 }
    735 
    736 // -------------------------------------
    737 
    738 /**
    739  * Return true if this zone has the same rules and offset as another zone.
    740  * @param other the TimeZone object to be compared with
    741  * @return true if the given zone has the same rules and offset as this one
    742  */
    743 UBool
    744 SimpleTimeZone::hasSameRules(const TimeZone& other) const
    745 {
    746     if (this == &other) return TRUE;
    747     if (typeid(*this) != typeid(other)) return FALSE;
    748     SimpleTimeZone *that = (SimpleTimeZone*)&other;
    749     return rawOffset     == that->rawOffset &&
    750         useDaylight     == that->useDaylight &&
    751         (!useDaylight
    752          // Only check rules if using DST
    753          || (dstSavings     == that->dstSavings &&
    754              startMode      == that->startMode &&
    755              startMonth     == that->startMonth &&
    756              startDay       == that->startDay &&
    757              startDayOfWeek == that->startDayOfWeek &&
    758              startTime      == that->startTime &&
    759              startTimeMode  == that->startTimeMode &&
    760              endMode        == that->endMode &&
    761              endMonth       == that->endMonth &&
    762              endDay         == that->endDay &&
    763              endDayOfWeek   == that->endDayOfWeek &&
    764              endTime        == that->endTime &&
    765              endTimeMode    == that->endTimeMode &&
    766              startYear      == that->startYear));
    767 }
    768 
    769 // -------------------------------------
    770 
    771 //----------------------------------------------------------------------
    772 // Rule representation
    773 //
    774 // We represent the following flavors of rules:
    775 //       5        the fifth of the month
    776 //       lastSun  the last Sunday in the month
    777 //       lastMon  the last Monday in the month
    778 //       Sun>=8   first Sunday on or after the eighth
    779 //       Sun<=25  last Sunday on or before the 25th
    780 // This is further complicated by the fact that we need to remain
    781 // backward compatible with the 1.1 FCS.  Finally, we need to minimize
    782 // API changes.  In order to satisfy these requirements, we support
    783 // three representation systems, and we translate between them.
    784 //
    785 // INTERNAL REPRESENTATION
    786 // This is the format SimpleTimeZone objects take after construction or
    787 // streaming in is complete.  Rules are represented directly, using an
    788 // unencoded format.  We will discuss the start rule only below; the end
    789 // rule is analogous.
    790 //   startMode      Takes on enumerated values DAY_OF_MONTH,
    791 //                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
    792 //   startDay       The day of the month, or for DOW_IN_MONTH mode, a
    793 //                  value indicating which DOW, such as +1 for first,
    794 //                  +2 for second, -1 for last, etc.
    795 //   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
    796 //
    797 // ENCODED REPRESENTATION
    798 // This is the format accepted by the constructor and by setStartRule()
    799 // and setEndRule().  It uses various combinations of positive, negative,
    800 // and zero values to encode the different rules.  This representation
    801 // allows us to specify all the different rule flavors without altering
    802 // the API.
    803 //   MODE              startMonth    startDay    startDayOfWeek
    804 //   DOW_IN_MONTH_MODE >=0           !=0         >0
    805 //   DOM_MODE          >=0           >0          ==0
    806 //   DOW_GE_DOM_MODE   >=0           >0          <0
    807 //   DOW_LE_DOM_MODE   >=0           <0          <0
    808 //   (no DST)          don't care    ==0         don't care
    809 //
    810 // STREAMED REPRESENTATION
    811 // We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
    812 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
    813 // flag useDaylight.  When we stream an object out, we translate into an
    814 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
    815 // and used by 1.1 code.  Following that, we write out the full
    816 // representation separately so that contemporary code can recognize and
    817 // parse it.  The full representation is written in a "packed" format,
    818 // consisting of a version number, a length, and an array of bytes.  Future
    819 // versions of this class may specify different versions.  If they wish to
    820 // include additional data, they should do so by storing them after the
    821 // packed representation below.
    822 //----------------------------------------------------------------------
    823 
    824 /**
    825  * Given a set of encoded rules in startDay and startDayOfMonth, decode
    826  * them and set the startMode appropriately.  Do the same for endDay and
    827  * endDayOfMonth.  Upon entry, the day of week variables may be zero or
    828  * negative, in order to indicate special modes.  The day of month
    829  * variables may also be negative.  Upon exit, the mode variables will be
    830  * set, and the day of week and day of month variables will be positive.
    831  * This method also recognizes a startDay or endDay of zero as indicating
    832  * no DST.
    833  */
    834 void
    835 SimpleTimeZone::decodeRules(UErrorCode& status)
    836 {
    837     decodeStartRule(status);
    838     decodeEndRule(status);
    839 }
    840 
    841 /**
    842  * Decode the start rule and validate the parameters.  The parameters are
    843  * expected to be in encoded form, which represents the various rule modes
    844  * by negating or zeroing certain values.  Representation formats are:
    845  * <p>
    846  * <pre>
    847  *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
    848  *            ------------  -----  --------  --------  ----------
    849  * month       0..11        same    same      same     don't care
    850  * day        -5..5         1..31   1..31    -1..-31   0
    851  * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
    852  * time        0..ONEDAY    same    same      same     don't care
    853  * </pre>
    854  * The range for month does not include UNDECIMBER since this class is
    855  * really specific to GregorianCalendar, which does not use that month.
    856  * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
    857  * end rule is an exclusive limit point.  That is, the range of times that
    858  * are in DST include those >= the start and < the end.  For this reason,
    859  * it should be possible to specify an end of ONEDAY in order to include the
    860  * entire day.  Although this is equivalent to time 0 of the following day,
    861  * it's not always possible to specify that, for example, on December 31.
    862  * While arguably the start range should still be 0..ONEDAY-1, we keep
    863  * the start and end ranges the same for consistency.
    864  */
    865 void
    866 SimpleTimeZone::decodeStartRule(UErrorCode& status)
    867 {
    868     if(U_FAILURE(status)) return;
    869 
    870     useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
    871     if (useDaylight && dstSavings == 0) {
    872         dstSavings = U_MILLIS_PER_HOUR;
    873     }
    874     if (startDay != 0) {
    875         if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
    876             status = U_ILLEGAL_ARGUMENT_ERROR;
    877             return;
    878         }
    879         if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
    880             startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
    881             status = U_ILLEGAL_ARGUMENT_ERROR;
    882             return;
    883         }
    884         if (startDayOfWeek == 0) {
    885             startMode = DOM_MODE;
    886         } else {
    887             if (startDayOfWeek > 0) {
    888                 startMode = DOW_IN_MONTH_MODE;
    889             } else {
    890                 startDayOfWeek = (int8_t)-startDayOfWeek;
    891                 if (startDay > 0) {
    892                     startMode = DOW_GE_DOM_MODE;
    893                 } else {
    894                     startDay = (int8_t)-startDay;
    895                     startMode = DOW_LE_DOM_MODE;
    896                 }
    897             }
    898             if (startDayOfWeek > UCAL_SATURDAY) {
    899                 status = U_ILLEGAL_ARGUMENT_ERROR;
    900                 return;
    901             }
    902         }
    903         if (startMode == DOW_IN_MONTH_MODE) {
    904             if (startDay < -5 || startDay > 5) {
    905                 status = U_ILLEGAL_ARGUMENT_ERROR;
    906                 return;
    907             }
    908         } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
    909             status = U_ILLEGAL_ARGUMENT_ERROR;
    910             return;
    911         }
    912     }
    913 }
    914 
    915 /**
    916  * Decode the end rule and validate the parameters.  This method is exactly
    917  * analogous to decodeStartRule().
    918  * @see decodeStartRule
    919  */
    920 void
    921 SimpleTimeZone::decodeEndRule(UErrorCode& status)
    922 {
    923     if(U_FAILURE(status)) return;
    924 
    925     useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
    926     if (useDaylight && dstSavings == 0) {
    927         dstSavings = U_MILLIS_PER_HOUR;
    928     }
    929     if (endDay != 0) {
    930         if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
    931             status = U_ILLEGAL_ARGUMENT_ERROR;
    932             return;
    933         }
    934         if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
    935             endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
    936             status = U_ILLEGAL_ARGUMENT_ERROR;
    937             return;
    938         }
    939         if (endDayOfWeek == 0) {
    940             endMode = DOM_MODE;
    941         } else {
    942             if (endDayOfWeek > 0) {
    943                 endMode = DOW_IN_MONTH_MODE;
    944             } else {
    945                 endDayOfWeek = (int8_t)-endDayOfWeek;
    946                 if (endDay > 0) {
    947                     endMode = DOW_GE_DOM_MODE;
    948                 } else {
    949                     endDay = (int8_t)-endDay;
    950                     endMode = DOW_LE_DOM_MODE;
    951                 }
    952             }
    953             if (endDayOfWeek > UCAL_SATURDAY) {
    954                 status = U_ILLEGAL_ARGUMENT_ERROR;
    955                 return;
    956             }
    957         }
    958         if (endMode == DOW_IN_MONTH_MODE) {
    959             if (endDay < -5 || endDay > 5) {
    960                 status = U_ILLEGAL_ARGUMENT_ERROR;
    961                 return;
    962             }
    963         } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
    964             status = U_ILLEGAL_ARGUMENT_ERROR;
    965             return;
    966         }
    967     }
    968 }
    969 
    970 UBool
    971 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
    972     if (!useDaylight) {
    973         return FALSE;
    974     }
    975 
    976     UErrorCode status = U_ZERO_ERROR;
    977     checkTransitionRules(status);
    978     if (U_FAILURE(status)) {
    979         return FALSE;
    980     }
    981 
    982     UDate firstTransitionTime = firstTransition->getTime();
    983     if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
    984         result = *firstTransition;
    985     }
    986     UDate stdDate, dstDate;
    987     UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
    988     UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
    989     if (stdAvail && (!dstAvail || stdDate < dstDate)) {
    990         result.setTime(stdDate);
    991         result.setFrom((const TimeZoneRule&)*dstRule);
    992         result.setTo((const TimeZoneRule&)*stdRule);
    993         return TRUE;
    994     }
    995     if (dstAvail && (!stdAvail || dstDate < stdDate)) {
    996         result.setTime(dstDate);
    997         result.setFrom((const TimeZoneRule&)*stdRule);
    998         result.setTo((const TimeZoneRule&)*dstRule);
    999         return TRUE;
   1000     }
   1001     return FALSE;
   1002 }
   1003 
   1004 UBool
   1005 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
   1006     if (!useDaylight) {
   1007         return FALSE;
   1008     }
   1009 
   1010     UErrorCode status = U_ZERO_ERROR;
   1011     checkTransitionRules(status);
   1012     if (U_FAILURE(status)) {
   1013         return FALSE;
   1014     }
   1015 
   1016     UDate firstTransitionTime = firstTransition->getTime();
   1017     if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
   1018         return FALSE;
   1019     }
   1020     UDate stdDate, dstDate;
   1021     UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
   1022     UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
   1023     if (stdAvail && (!dstAvail || stdDate > dstDate)) {
   1024         result.setTime(stdDate);
   1025         result.setFrom((const TimeZoneRule&)*dstRule);
   1026         result.setTo((const TimeZoneRule&)*stdRule);
   1027         return TRUE;
   1028     }
   1029     if (dstAvail && (!stdAvail || dstDate > stdDate)) {
   1030         result.setTime(dstDate);
   1031         result.setFrom((const TimeZoneRule&)*stdRule);
   1032         result.setTo((const TimeZoneRule&)*dstRule);
   1033         return TRUE;
   1034     }
   1035     return FALSE;
   1036 }
   1037 
   1038 void
   1039 SimpleTimeZone::clearTransitionRules(void) {
   1040     initialRule = NULL;
   1041     firstTransition = NULL;
   1042     stdRule = NULL;
   1043     dstRule = NULL;
   1044     transitionRulesInitialized = FALSE;
   1045 }
   1046 
   1047 void
   1048 SimpleTimeZone::deleteTransitionRules(void) {
   1049     if (initialRule != NULL) {
   1050         delete initialRule;
   1051     }
   1052     if (firstTransition != NULL) {
   1053         delete firstTransition;
   1054     }
   1055     if (stdRule != NULL) {
   1056         delete stdRule;
   1057     }
   1058     if (dstRule != NULL) {
   1059         delete dstRule;
   1060     }
   1061     clearTransitionRules();
   1062  }
   1063 
   1064 /*
   1065  * Lazy transition rules initializer
   1066  *
   1067  *    Note On the removal of UMTX_CHECK from checkTransitionRules():
   1068  *
   1069  *         It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
   1070  *         which would avoid needing to lock a mutex to check the initialization state.
   1071  *         But we can't easily because simpletz.h is a public header, and including
   1072  *         a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
   1073  *
   1074  *         Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
   1075  *         allocate it in the constructors. This would be a more intrusive change, but doable
   1076  *         if performance turns out to be an issue.
   1077  */
   1078 static UMutex gLock = U_MUTEX_INITIALIZER;
   1079 
   1080 void
   1081 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
   1082     if (U_FAILURE(status)) {
   1083         return;
   1084     }
   1085     umtx_lock(&gLock);
   1086     if (!transitionRulesInitialized) {
   1087         SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
   1088         ncThis->initTransitionRules(status);
   1089     }
   1090     umtx_unlock(&gLock);
   1091 }
   1092 
   1093 void
   1094 SimpleTimeZone::initTransitionRules(UErrorCode& status) {
   1095     if (U_FAILURE(status)) {
   1096         return;
   1097     }
   1098     if (transitionRulesInitialized) {
   1099         return;
   1100     }
   1101     deleteTransitionRules();
   1102     UnicodeString tzid;
   1103     getID(tzid);
   1104 
   1105     if (useDaylight) {
   1106         DateTimeRule* dtRule;
   1107         DateTimeRule::TimeRuleType timeRuleType;
   1108         UDate firstStdStart, firstDstStart;
   1109 
   1110         // Create a TimeZoneRule for daylight saving time
   1111         timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
   1112             ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
   1113         switch (startMode) {
   1114         case DOM_MODE:
   1115             dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
   1116             break;
   1117         case DOW_IN_MONTH_MODE:
   1118             dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
   1119             break;
   1120         case DOW_GE_DOM_MODE:
   1121             dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
   1122             break;
   1123         case DOW_LE_DOM_MODE:
   1124             dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
   1125             break;
   1126         default:
   1127             status = U_INVALID_STATE_ERROR;
   1128             return;
   1129         }
   1130         // Check for Null pointer
   1131         if (dtRule == NULL) {
   1132             status = U_MEMORY_ALLOCATION_ERROR;
   1133             return;
   1134         }
   1135         // For now, use ID + "(DST)" as the name
   1136         dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
   1137             dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
   1138 
   1139         // Check for Null pointer
   1140         if (dstRule == NULL) {
   1141             status = U_MEMORY_ALLOCATION_ERROR;
   1142             deleteTransitionRules();
   1143             return;
   1144         }
   1145 
   1146         // Calculate the first DST start time
   1147         dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
   1148 
   1149         // Create a TimeZoneRule for standard time
   1150         timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
   1151             ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
   1152         switch (endMode) {
   1153         case DOM_MODE:
   1154             dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
   1155             break;
   1156         case DOW_IN_MONTH_MODE:
   1157             dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
   1158             break;
   1159         case DOW_GE_DOM_MODE:
   1160             dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
   1161             break;
   1162         case DOW_LE_DOM_MODE:
   1163             dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
   1164             break;
   1165         }
   1166 
   1167         // Check for Null pointer
   1168         if (dtRule == NULL) {
   1169             status = U_MEMORY_ALLOCATION_ERROR;
   1170             deleteTransitionRules();
   1171             return;
   1172         }
   1173         // For now, use ID + "(STD)" as the name
   1174         stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
   1175             dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
   1176 
   1177         //Check for Null pointer
   1178         if (stdRule == NULL) {
   1179             status = U_MEMORY_ALLOCATION_ERROR;
   1180             deleteTransitionRules();
   1181             return;
   1182         }
   1183 
   1184         // Calculate the first STD start time
   1185         stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
   1186 
   1187         // Create a TimeZoneRule for initial time
   1188         if (firstStdStart < firstDstStart) {
   1189             initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
   1190             firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
   1191         } else {
   1192             initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
   1193             firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
   1194         }
   1195         // Check for null pointers.
   1196         if (initialRule == NULL || firstTransition == NULL) {
   1197             status = U_MEMORY_ALLOCATION_ERROR;
   1198             deleteTransitionRules();
   1199             return;
   1200         }
   1201 
   1202     } else {
   1203         // Create a TimeZoneRule for initial time
   1204         initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
   1205         // Check for null pointer.
   1206         if (initialRule == NULL) {
   1207             status = U_MEMORY_ALLOCATION_ERROR;
   1208             deleteTransitionRules();
   1209             return;
   1210         }
   1211     }
   1212 
   1213     transitionRulesInitialized = TRUE;
   1214 }
   1215 
   1216 int32_t
   1217 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
   1218     return (useDaylight) ? 2 : 0;
   1219 }
   1220 
   1221 void
   1222 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
   1223                                  const TimeZoneRule* trsrules[],
   1224                                  int32_t& trscount,
   1225                                  UErrorCode& status) const {
   1226     if (U_FAILURE(status)) {
   1227         return;
   1228     }
   1229     checkTransitionRules(status);
   1230     if (U_FAILURE(status)) {
   1231         return;
   1232     }
   1233     initial = initialRule;
   1234     int32_t cnt = 0;
   1235     if (stdRule != NULL) {
   1236         if (cnt < trscount) {
   1237             trsrules[cnt++] = stdRule;
   1238         }
   1239         if (cnt < trscount) {
   1240             trsrules[cnt++] = dstRule;
   1241         }
   1242     }
   1243     trscount = cnt;
   1244 }
   1245 
   1246 
   1247 U_NAMESPACE_END
   1248 
   1249 #endif /* #if !UCONFIG_NO_FORMATTING */
   1250 
   1251 //eof
   1252