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