]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/simpletz.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / simpletz.cpp
index 393d339d2850bbf447e624814eff5bee1846a7fe..9321fda3447d1835e5d6e94cb1eaba18ef569210 100644 (file)
@@ -1,25 +1,29 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 /*
-*******************************************************************************
-* Copyright (C) 1997-2003, International Business Machines Corporation and    *
-* others. All Rights Reserved.                                                *
-*******************************************************************************
-*
-* File SIMPLETZ.H
-*
-* Modification History:
-*
-*   Date        Name        Description
-*   12/05/96    clhuang     Creation.
-*   04/21/97    aliu        Fixed miscellaneous bugs found by inspection and
-*                           testing.
-*   07/29/97    aliu        Ported source bodies back from Java version with
-*                           numerous feature enhancements and bug fixes.
-*   08/10/98    stephen     JDK 1.2 sync.
-*   09/17/98    stephen     Fixed getOffset() for last hour of year and DST
-*   12/02/99    aliu        Added TimeMode and constructor and setStart/EndRule
-*                           methods that take TimeMode. Whitespace cleanup.
-********************************************************************************
-*/
+ *******************************************************************************
+ * Copyright (C) 1997-2013, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ *
+ * File SIMPLETZ.H
+ *
+ * Modification History:
+ *
+ *   Date        Name        Description
+ *   12/05/96    clhuang     Creation.
+ *   04/21/97    aliu        Fixed miscellaneous bugs found by inspection and
+ *                           testing.
+ *   07/29/97    aliu        Ported source bodies back from Java version with
+ *                           numerous feature enhancements and bug fixes.
+ *   08/10/98    stephen     JDK 1.2 sync.
+ *   09/17/98    stephen     Fixed getOffset() for last hour of year and DST
+ *   12/02/99    aliu        Added TimeMode and constructor and setStart/EndRule
+ *                           methods that take TimeMode. Whitespace cleanup.
+ ********************************************************************************
+ */
+
+#include "utypeinfo.h"  // for 'typeid' to work
 
 #include "unicode/utypes.h"
 
 
 #include "unicode/utypes.h"
 
 
 #include "unicode/simpletz.h"
 #include "unicode/gregocal.h"
 
 #include "unicode/simpletz.h"
 #include "unicode/gregocal.h"
+#include "unicode/smpdtfmt.h"
+
+#include "cmemory.h"
+#include "gregoimp.h"
+#include "umutex.h"
 
 U_NAMESPACE_BEGIN
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
 
 
 U_NAMESPACE_BEGIN
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
 
-// WARNING: assumes that no rule is measured from the end of February,
-// since we don't handle leap years. Could handle assuming always
+// Use only for decodeStartRule() and decodeEndRule() where the year is not
+// available. Set February to 29 days to accomodate rules with that date
+// and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
+// The compareToRule() method adjusts to February 28 in non-leap years.
+//
+// For actual getOffset() calculations, use Grego::monthLength() and
+// Grego::previousMonthLength() which take leap years into account.
+// We handle leap years assuming always
 // Gregorian, since we know they didn't have daylight time when
 // Gregorian calendar started.
 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
 
 // Gregorian, since we know they didn't have daylight time when
 // Gregorian calendar started.
 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
 
+static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)"
+static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)"
+
+
 // *****************************************************************************
 // class SimpleTimeZone
 // *****************************************************************************
 
 
 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
 // *****************************************************************************
 // class SimpleTimeZone
 // *****************************************************************************
 
 
 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
-:   TimeZone(ID),
+:   BasicTimeZone(ID),
     startMonth(0),
     startDay(0),
     startDayOfWeek(0),
     startMonth(0),
     startDay(0),
     startDayOfWeek(0),
@@ -62,6 +81,7 @@ SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
     endMode(DOM_MODE),
     dstSavings(U_MILLIS_PER_HOUR)
 {
     endMode(DOM_MODE),
     dstSavings(U_MILLIS_PER_HOUR)
 {
+    clearTransitionRules();
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -72,8 +92,9 @@ SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
     int8_t savingsEndMonth, int8_t savingsEndDay,
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     UErrorCode& status)
     int8_t savingsEndMonth, int8_t savingsEndDay,
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     UErrorCode& status)
-:   TimeZone(ID)
+:   BasicTimeZone(ID)
 {
 {
+    clearTransitionRules();
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, WALL_TIME,
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, WALL_TIME,
@@ -90,8 +111,9 @@ SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
     int8_t savingsEndMonth, int8_t savingsEndDay,
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     int32_t savingsDST, UErrorCode& status)
     int8_t savingsEndMonth, int8_t savingsEndDay,
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     int32_t savingsDST, UErrorCode& status)
-:   TimeZone(ID)
+:   BasicTimeZone(ID)
 {
 {
+    clearTransitionRules();
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, WALL_TIME,
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, WALL_TIME,
@@ -110,8 +132,9 @@ SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     TimeMode savingsEndTimeMode,
     int32_t savingsDST, UErrorCode& status)
     int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
     TimeMode savingsEndTimeMode,
     int32_t savingsDST, UErrorCode& status)
-:   TimeZone(ID)
+:   BasicTimeZone(ID)
 {
 {
+    clearTransitionRules();
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, savingsStartTimeMode,
     construct(rawOffsetGMT,
               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
               savingsStartTime, savingsStartTimeMode,
@@ -155,7 +178,7 @@ void SimpleTimeZone::construct(int32_t rawOffsetGMT,
 
     decodeRules(status);
 
 
     decodeRules(status);
 
-    if (savingsDST <= 0) {
+    if (savingsDST == 0) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
     }
 }
         status = U_ILLEGAL_ARGUMENT_ERROR;
     }
 }
@@ -164,13 +187,14 @@ void SimpleTimeZone::construct(int32_t rawOffsetGMT,
 
 SimpleTimeZone::~SimpleTimeZone()
 {
 
 SimpleTimeZone::~SimpleTimeZone()
 {
+    deleteTransitionRules();
 }
 
 // -------------------------------------
 
 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
 }
 
 // -------------------------------------
 
 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
-:   TimeZone(source)
+:   BasicTimeZone(source)
 {
     *this = source;
 }
 {
     *this = source;
 }
@@ -200,6 +224,7 @@ SimpleTimeZone::operator=(const SimpleTimeZone &right)
         startYear      = right.startYear;
         dstSavings     = right.dstSavings;
         useDaylight    = right.useDaylight;
         startYear      = right.startYear;
         dstSavings     = right.dstSavings;
         useDaylight    = right.useDaylight;
+        clearTransitionRules();
     }
     return *this;
 }
     }
     return *this;
 }
@@ -210,7 +235,7 @@ UBool
 SimpleTimeZone::operator==(const TimeZone& that) const
 {
     return ((this == &that) ||
 SimpleTimeZone::operator==(const TimeZone& that) const
 {
     return ((this == &that) ||
-            (getDynamicClassID() == that.getDynamicClassID() &&
+            (typeid(*this) == typeid(that) &&
             TimeZone::operator==(that) &&
             hasSameRules(that)));
 }
             TimeZone::operator==(that) &&
             hasSameRules(that)));
 }
@@ -237,6 +262,7 @@ void
 SimpleTimeZone::setStartYear(int32_t year)
 {
     startYear = year;
 SimpleTimeZone::setStartYear(int32_t year)
 {
     startYear = year;
+    transitionRulesInitialized = FALSE;
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -290,6 +316,7 @@ SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t da
     startTime      = time;
     startTimeMode  = mode;
     decodeStartRule(status);
     startTime      = time;
     startTimeMode  = mode;
     decodeStartRule(status);
+    transitionRulesInitialized = FALSE;
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -341,6 +368,7 @@ SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayO
     endTime      = time;
     endTimeMode  = mode;
     decodeEndRule(status);
     endTime      = time;
     endTimeMode  = mode;
     decodeEndRule(status);
+    transitionRulesInitialized = FALSE;
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -368,7 +396,7 @@ int32_t
 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
                           uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
 {
 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
                           uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
 {
-    // Check the month before indexing into STATICMONTHLENGTH. This
+    // Check the month before calling Grego::monthLength(). This
     // duplicates the test that occurs in the 7-argument getOffset(),
     // however, this is unavoidable. We don't mind because this method, in
     // fact, should not be called; internal code should always call the
     // duplicates the test that occurs in the 7-argument getOffset(),
     // however, this is unavoidable. We don't mind because this method, in
     // fact, should not be called; internal code should always call the
@@ -380,14 +408,15 @@ SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
         return 0;
     }
 
         return 0;
     }
 
-    return getOffset(era, year, month, day, dayOfWeek, millis, STATICMONTHLENGTH[month], status);
+    return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status);
 }
 
 int32_t 
 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
                           uint8_t dayOfWeek, int32_t millis, 
 }
 
 int32_t 
 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
                           uint8_t dayOfWeek, int32_t millis, 
-                          int32_t monthLength, UErrorCode& status) const {
-    // Check the month before indexing into STATICMONTHLENGTH. This
+                          int32_t /*monthLength*/, UErrorCode& status) const
+{
+    // Check the month before calling Grego::monthLength(). This
     // duplicates a test that occurs in the 9-argument getOffset(),
     // however, this is unavoidable. We don't mind because this method, in
     // fact, should not be called; internal code should always call the
     // duplicates a test that occurs in the 9-argument getOffset(),
     // however, this is unavoidable. We don't mind because this method, in
     // fact, should not be called; internal code should always call the
@@ -400,11 +429,13 @@ SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
         return -1;
     }
 
         return -1;
     }
 
-    // TODO FIX We don't handle leap years yet!
-    int32_t prevMonthLength = (month >= 1) ? STATICMONTHLENGTH[month - 1] : 31;
-
+    // We ignore monthLength because it can be derived from year and month.
+    // This is so that February in leap years is calculated correctly.
+    // We keep this argument in this function for backwards compatibility.
     return getOffset(era, year, month, day, dayOfWeek, millis,
     return getOffset(era, year, month, day, dayOfWeek, millis,
-                     monthLength, prevMonthLength, status);
+                     Grego::monthLength(year, month),
+                     Grego::previousMonthLength(year, month),
+                     status);
 }
 
 int32_t 
 }
 
 int32_t 
@@ -477,6 +508,55 @@ SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
     return result;
 }
 
     return result;
 }
 
+void
+SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
+                                   int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    rawOffsetGMT = getRawOffset();
+    int32_t year, month, dom, dow;
+    double day = uprv_floor(date / U_MILLIS_PER_DAY);
+    int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+
+    Grego::dayToFields(day, year, month, dom, dow);
+
+    savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+                          (uint8_t) dow, millis,
+                          Grego::monthLength(year, month),
+                          status) - rawOffsetGMT;
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    UBool recalc = FALSE;
+
+    // Now we need some adjustment
+    if (savingsDST > 0) {
+        if ((nonExistingTimeOpt & kStdDstMask) == kStandard
+            || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
+            date -= getDSTSavings();
+            recalc = TRUE;
+        }
+    } else {
+        if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
+                || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
+            date -= getDSTSavings();
+            recalc = TRUE;
+        }
+    }
+    if (recalc) {
+        day = uprv_floor(date / U_MILLIS_PER_DAY);
+        millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
+        Grego::dayToFields(day, year, month, dom, dow);
+        savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
+                          (uint8_t) dow, millis,
+                          Grego::monthLength(year, month),
+                          status) - rawOffsetGMT;
+    }
+}
+
 // -------------------------------------
 
 /**
 // -------------------------------------
 
 /**
@@ -527,6 +607,12 @@ SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen
 
     // calculate the actual day of month for the rule
     int32_t ruleDayOfMonth = 0;
 
     // calculate the actual day of month for the rule
     int32_t ruleDayOfMonth = 0;
+
+    // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
+    if (ruleDay > monthLen) {
+        ruleDay = monthLen;
+    }
+
     switch (ruleMode)
     {
     // if the mode is day-of-month, the day of month is given
     switch (ruleMode)
     {
     // if the mode is day-of-month, the day of month is given
@@ -546,11 +632,9 @@ SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen
         
         // if ruleDay is negative (we assume it's not zero here), we have to do
         // the same calculation figuring backward from the last day of the month.
         
         // if ruleDay is negative (we assume it's not zero here), we have to do
         // the same calculation figuring backward from the last day of the month.
-        // (STATICMONTHLENGTH gives us that last day.  We don't take leap years
-        // into account, so this may not work right for February.)
         else
         {
         else
         {
-             // (again, this code is trusting that dayOfMonth and dayOfMonth are
+            // (again, this code is trusting that dayOfWeek and dayOfMonth are
             // consistent with each other here, since we're using them to figure
             // the day of week of the first of the month)
             ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
             // consistent with each other here, since we're using them to figure
             // the day of week of the first of the month)
             ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
@@ -595,6 +679,7 @@ void
 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
 {
     rawOffset = offsetMillis;
 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
 {
     rawOffset = offsetMillis;
+    transitionRulesInitialized = FALSE;
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -602,12 +687,13 @@ SimpleTimeZone::setRawOffset(int32_t offsetMillis)
 void 
 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) 
 {
 void 
 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) 
 {
-    if (millisSavedDuringDST <= 0) {
+    if (millisSavedDuringDST == 0) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
     }
     else {
         dstSavings = millisSavedDuringDST;
     }
         status = U_ILLEGAL_ARGUMENT_ERROR;
     }
     else {
         dstSavings = millisSavedDuringDST;
     }
+    transitionRulesInitialized = FALSE;
 }
 
 // -------------------------------------
 }
 
 // -------------------------------------
@@ -661,7 +747,7 @@ UBool
 SimpleTimeZone::hasSameRules(const TimeZone& other) const
 {
     if (this == &other) return TRUE;
 SimpleTimeZone::hasSameRules(const TimeZone& other) const
 {
     if (this == &other) return TRUE;
-    if (other.getDynamicClassID() != SimpleTimeZone::getStaticClassID()) return FALSE;
+    if (typeid(*this) != typeid(other)) return FALSE;
     SimpleTimeZone *that = (SimpleTimeZone*)&other;
     return rawOffset     == that->rawOffset &&
         useDaylight     == that->useDaylight &&
     SimpleTimeZone *that = (SimpleTimeZone*)&other;
     return rawOffset     == that->rawOffset &&
         useDaylight     == that->useDaylight &&
@@ -822,7 +908,7 @@ SimpleTimeZone::decodeStartRule(UErrorCode& status)
                 status = U_ILLEGAL_ARGUMENT_ERROR;
                 return;
             }
                 status = U_ILLEGAL_ARGUMENT_ERROR;
                 return;
             }
-        } else if (startDay > STATICMONTHLENGTH[startMonth]) {
+        } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
             status = U_ILLEGAL_ARGUMENT_ERROR;
             return;
         }
             status = U_ILLEGAL_ARGUMENT_ERROR;
             return;
         }
@@ -877,13 +963,299 @@ SimpleTimeZone::decodeEndRule(UErrorCode& status)
                 status = U_ILLEGAL_ARGUMENT_ERROR;
                 return;
             }
                 status = U_ILLEGAL_ARGUMENT_ERROR;
                 return;
             }
-        } else if (endDay > STATICMONTHLENGTH[endMonth]) {
+        } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
             status = U_ILLEGAL_ARGUMENT_ERROR;
             return;
         }
     }
 }
 
             status = U_ILLEGAL_ARGUMENT_ERROR;
             return;
         }
     }
 }
 
+UBool
+SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
+    if (!useDaylight) {
+        return FALSE;
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    checkTransitionRules(status);
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+
+    UDate firstTransitionTime = firstTransition->getTime();
+    if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
+        result = *firstTransition;
+    }
+    UDate stdDate, dstDate;
+    UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
+    UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
+    if (stdAvail && (!dstAvail || stdDate < dstDate)) {
+        result.setTime(stdDate);
+        result.setFrom((const TimeZoneRule&)*dstRule);
+        result.setTo((const TimeZoneRule&)*stdRule);
+        return TRUE;
+    }
+    if (dstAvail && (!stdAvail || dstDate < stdDate)) {
+        result.setTime(dstDate);
+        result.setFrom((const TimeZoneRule&)*stdRule);
+        result.setTo((const TimeZoneRule&)*dstRule);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+UBool
+SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
+    if (!useDaylight) {
+        return FALSE;
+    }
+
+    UErrorCode status = U_ZERO_ERROR;
+    checkTransitionRules(status);
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+
+    UDate firstTransitionTime = firstTransition->getTime();
+    if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
+        return FALSE;
+    }
+    UDate stdDate, dstDate;
+    UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
+    UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
+    if (stdAvail && (!dstAvail || stdDate > dstDate)) {
+        result.setTime(stdDate);
+        result.setFrom((const TimeZoneRule&)*dstRule);
+        result.setTo((const TimeZoneRule&)*stdRule);
+        return TRUE;
+    }
+    if (dstAvail && (!stdAvail || dstDate > stdDate)) {
+        result.setTime(dstDate);
+        result.setFrom((const TimeZoneRule&)*stdRule);
+        result.setTo((const TimeZoneRule&)*dstRule);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void
+SimpleTimeZone::clearTransitionRules(void) {
+    initialRule = NULL;
+    firstTransition = NULL;
+    stdRule = NULL;
+    dstRule = NULL;
+    transitionRulesInitialized = FALSE;
+}
+
+void
+SimpleTimeZone::deleteTransitionRules(void) {
+    if (initialRule != NULL) {
+        delete initialRule;
+    }
+    if (firstTransition != NULL) {
+        delete firstTransition;
+    }
+    if (stdRule != NULL) {
+        delete stdRule;
+    }
+    if (dstRule != NULL) {
+        delete dstRule;
+    }
+    clearTransitionRules();
+ }
+
+/*
+ * Lazy transition rules initializer
+ *
+ *    Note On the removal of UMTX_CHECK from checkTransitionRules():
+ *
+ *         It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
+ *         which would avoid needing to lock a mutex to check the initialization state.
+ *         But we can't easily because simpletz.h is a public header, and including
+ *         a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
+ *
+ *         Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
+ *         allocate it in the constructors. This would be a more intrusive change, but doable
+ *         if performance turns out to be an issue.
+ */
+
+void
+SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    static UMutex *gLock = STATIC_NEW(UMutex);
+    umtx_lock(gLock);
+    if (!transitionRulesInitialized) {
+        SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
+        ncThis->initTransitionRules(status);
+    }
+    umtx_unlock(gLock);
+}
+
+void
+SimpleTimeZone::initTransitionRules(UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (transitionRulesInitialized) {
+        return;
+    }
+    deleteTransitionRules();
+    UnicodeString tzid;
+    getID(tzid);
+
+    if (useDaylight) {
+        DateTimeRule* dtRule;
+        DateTimeRule::TimeRuleType timeRuleType;
+        UDate firstStdStart, firstDstStart;
+
+        // Create a TimeZoneRule for daylight saving time
+        timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
+            ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
+        switch (startMode) {
+        case DOM_MODE:
+            dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
+            break;
+        case DOW_IN_MONTH_MODE:
+            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
+            break;
+        case DOW_GE_DOM_MODE:
+            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
+            break;
+        case DOW_LE_DOM_MODE:
+            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
+            break;
+        default:
+            status = U_INVALID_STATE_ERROR;
+            return;
+        }
+        // Check for Null pointer
+        if (dtRule == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
+        // For now, use ID + "(DST)" as the name
+        dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
+            dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
+        
+        // Check for Null pointer
+        if (dstRule == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            deleteTransitionRules();
+            return;
+        }
+        // Calculate the first DST start time
+        dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
+
+        // Create a TimeZoneRule for standard time
+        timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
+            ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
+        switch (endMode) {
+        case DOM_MODE:
+            dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
+            break;
+        case DOW_IN_MONTH_MODE:
+            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
+            break;
+        case DOW_GE_DOM_MODE:
+            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
+            break;
+        case DOW_LE_DOM_MODE:
+            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
+            break;
+        }
+        
+        // Check for Null pointer
+        if (dtRule == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            deleteTransitionRules();
+            return;
+        }
+        // For now, use ID + "(STD)" as the name
+        stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
+            dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
+        
+        //Check for Null pointer
+        if (stdRule == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            deleteTransitionRules();
+            return;
+        }
+
+        // Calculate the first STD start time
+        stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
+
+        // Create a TimeZoneRule for initial time
+        if (firstStdStart < firstDstStart) {
+            initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
+            if (initialRule == NULL) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                deleteTransitionRules();
+                return;
+            }
+            firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
+        } else {
+            initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
+            if (initialRule == NULL) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                deleteTransitionRules();
+                return;
+            }
+            firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
+        }
+        if (firstTransition == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            deleteTransitionRules();
+            return;
+        }
+        
+    } else {
+        // Create a TimeZoneRule for initial time
+        initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
+        // Check for null pointer.
+        if (initialRule == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            deleteTransitionRules();
+            return;
+        }
+    }
+
+    transitionRulesInitialized = TRUE;
+}
+
+int32_t
+SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
+    return (useDaylight) ? 2 : 0;
+}
+
+void
+SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
+                                 const TimeZoneRule* trsrules[],
+                                 int32_t& trscount,
+                                 UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    checkTransitionRules(status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    initial = initialRule;
+    int32_t cnt = 0;
+    if (stdRule != NULL) {
+        if (cnt < trscount) {
+            trsrules[cnt++] = stdRule;
+        }
+        if (cnt < trscount) {
+            trsrules[cnt++] = dstRule;
+        }
+    }
+    trscount = cnt;
+}
+
+
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */