tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

simpletz.cpp (46808B)


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