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