michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 1997-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************************* michael@0: * michael@0: * File SIMPLETZ.H michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 12/05/96 clhuang Creation. michael@0: * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and michael@0: * testing. michael@0: * 07/29/97 aliu Ported source bodies back from Java version with michael@0: * numerous feature enhancements and bug fixes. michael@0: * 08/10/98 stephen JDK 1.2 sync. michael@0: * 09/17/98 stephen Fixed getOffset() for last hour of year and DST michael@0: * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule michael@0: * methods that take TimeMode. Whitespace cleanup. michael@0: ******************************************************************************** michael@0: */ michael@0: michael@0: #include "utypeinfo.h" // for 'typeid' to work michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/simpletz.h" michael@0: #include "unicode/gregocal.h" michael@0: #include "unicode/smpdtfmt.h" michael@0: michael@0: #include "gregoimp.h" michael@0: #include "umutex.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone) michael@0: michael@0: // Use only for decodeStartRule() and decodeEndRule() where the year is not michael@0: // available. Set February to 29 days to accomodate rules with that date michael@0: // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE). michael@0: // The compareToRule() method adjusts to February 28 in non-leap years. michael@0: // michael@0: // For actual getOffset() calculations, use Grego::monthLength() and michael@0: // Grego::previousMonthLength() which take leap years into account. michael@0: // We handle leap years assuming always michael@0: // Gregorian, since we know they didn't have daylight time when michael@0: // Gregorian calendar started. michael@0: const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; michael@0: michael@0: static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)" michael@0: static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)" michael@0: michael@0: michael@0: // ***************************************************************************** michael@0: // class SimpleTimeZone michael@0: // ***************************************************************************** michael@0: michael@0: michael@0: SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID) michael@0: : BasicTimeZone(ID), michael@0: startMonth(0), michael@0: startDay(0), michael@0: startDayOfWeek(0), michael@0: startTime(0), michael@0: startTimeMode(WALL_TIME), michael@0: endTimeMode(WALL_TIME), michael@0: endMonth(0), michael@0: endDay(0), michael@0: endDayOfWeek(0), michael@0: endTime(0), michael@0: startYear(0), michael@0: rawOffset(rawOffsetGMT), michael@0: useDaylight(FALSE), michael@0: startMode(DOM_MODE), michael@0: endMode(DOM_MODE), michael@0: dstSavings(U_MILLIS_PER_HOUR) michael@0: { michael@0: clearTransitionRules(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, michael@0: int8_t savingsStartMonth, int8_t savingsStartDay, michael@0: int8_t savingsStartDayOfWeek, int32_t savingsStartTime, michael@0: int8_t savingsEndMonth, int8_t savingsEndDay, michael@0: int8_t savingsEndDayOfWeek, int32_t savingsEndTime, michael@0: UErrorCode& status) michael@0: : BasicTimeZone(ID) michael@0: { michael@0: clearTransitionRules(); michael@0: construct(rawOffsetGMT, michael@0: savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, michael@0: savingsStartTime, WALL_TIME, michael@0: savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, michael@0: savingsEndTime, WALL_TIME, michael@0: U_MILLIS_PER_HOUR, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, michael@0: int8_t savingsStartMonth, int8_t savingsStartDay, michael@0: int8_t savingsStartDayOfWeek, int32_t savingsStartTime, michael@0: int8_t savingsEndMonth, int8_t savingsEndDay, michael@0: int8_t savingsEndDayOfWeek, int32_t savingsEndTime, michael@0: int32_t savingsDST, UErrorCode& status) michael@0: : BasicTimeZone(ID) michael@0: { michael@0: clearTransitionRules(); michael@0: construct(rawOffsetGMT, michael@0: savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, michael@0: savingsStartTime, WALL_TIME, michael@0: savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, michael@0: savingsEndTime, WALL_TIME, michael@0: savingsDST, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, michael@0: int8_t savingsStartMonth, int8_t savingsStartDay, michael@0: int8_t savingsStartDayOfWeek, int32_t savingsStartTime, michael@0: TimeMode savingsStartTimeMode, michael@0: int8_t savingsEndMonth, int8_t savingsEndDay, michael@0: int8_t savingsEndDayOfWeek, int32_t savingsEndTime, michael@0: TimeMode savingsEndTimeMode, michael@0: int32_t savingsDST, UErrorCode& status) michael@0: : BasicTimeZone(ID) michael@0: { michael@0: clearTransitionRules(); michael@0: construct(rawOffsetGMT, michael@0: savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, michael@0: savingsStartTime, savingsStartTimeMode, michael@0: savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, michael@0: savingsEndTime, savingsEndTimeMode, michael@0: savingsDST, status); michael@0: } michael@0: michael@0: /** michael@0: * Internal construction method. michael@0: */ michael@0: void SimpleTimeZone::construct(int32_t rawOffsetGMT, michael@0: int8_t savingsStartMonth, michael@0: int8_t savingsStartDay, michael@0: int8_t savingsStartDayOfWeek, michael@0: int32_t savingsStartTime, michael@0: TimeMode savingsStartTimeMode, michael@0: int8_t savingsEndMonth, michael@0: int8_t savingsEndDay, michael@0: int8_t savingsEndDayOfWeek, michael@0: int32_t savingsEndTime, michael@0: TimeMode savingsEndTimeMode, michael@0: int32_t savingsDST, michael@0: UErrorCode& status) michael@0: { michael@0: this->rawOffset = rawOffsetGMT; michael@0: this->startMonth = savingsStartMonth; michael@0: this->startDay = savingsStartDay; michael@0: this->startDayOfWeek = savingsStartDayOfWeek; michael@0: this->startTime = savingsStartTime; michael@0: this->startTimeMode = savingsStartTimeMode; michael@0: this->endMonth = savingsEndMonth; michael@0: this->endDay = savingsEndDay; michael@0: this->endDayOfWeek = savingsEndDayOfWeek; michael@0: this->endTime = savingsEndTime; michael@0: this->endTimeMode = savingsEndTimeMode; michael@0: this->dstSavings = savingsDST; michael@0: this->startYear = 0; michael@0: this->startMode = DOM_MODE; michael@0: this->endMode = DOM_MODE; michael@0: michael@0: decodeRules(status); michael@0: michael@0: if (savingsDST <= 0) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: SimpleTimeZone::~SimpleTimeZone() michael@0: { michael@0: deleteTransitionRules(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. michael@0: SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source) michael@0: : BasicTimeZone(source) michael@0: { michael@0: *this = source; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. michael@0: SimpleTimeZone & michael@0: SimpleTimeZone::operator=(const SimpleTimeZone &right) michael@0: { michael@0: if (this != &right) michael@0: { michael@0: TimeZone::operator=(right); michael@0: rawOffset = right.rawOffset; michael@0: startMonth = right.startMonth; michael@0: startDay = right.startDay; michael@0: startDayOfWeek = right.startDayOfWeek; michael@0: startTime = right.startTime; michael@0: startTimeMode = right.startTimeMode; michael@0: startMode = right.startMode; michael@0: endMonth = right.endMonth; michael@0: endDay = right.endDay; michael@0: endDayOfWeek = right.endDayOfWeek; michael@0: endTime = right.endTime; michael@0: endTimeMode = right.endTimeMode; michael@0: endMode = right.endMode; michael@0: startYear = right.startYear; michael@0: dstSavings = right.dstSavings; michael@0: useDaylight = right.useDaylight; michael@0: clearTransitionRules(); michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: UBool michael@0: SimpleTimeZone::operator==(const TimeZone& that) const michael@0: { michael@0: return ((this == &that) || michael@0: (typeid(*this) == typeid(that) && michael@0: TimeZone::operator==(that) && michael@0: hasSameRules(that))); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: // Called by TimeZone::createDefault() inside a Mutex - be careful. michael@0: TimeZone* michael@0: SimpleTimeZone::clone() const michael@0: { michael@0: return new SimpleTimeZone(*this); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Sets the daylight savings starting year, that is, the year this time zone began michael@0: * observing its specified daylight savings time rules. The time zone is considered michael@0: * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't michael@0: * support historical daylight-savings-time rules. michael@0: * @param year the daylight savings starting year. michael@0: */ michael@0: void michael@0: SimpleTimeZone::setStartYear(int32_t year) michael@0: { michael@0: startYear = year; michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings michael@0: * Time starts at the first Sunday in April, at 2 AM in standard time. michael@0: * Therefore, you can set the start rule by calling: michael@0: * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000); michael@0: * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate michael@0: * the exact starting date. Their exact meaning depend on their respective signs, michael@0: * allowing various types of rules to be constructed, as follows: michael@0: * @param month the daylight savings starting month. Month is 0-based. michael@0: * eg, 0 for January. michael@0: * @param dayOfWeekInMonth the daylight savings starting michael@0: * day-of-week-in-month. Please see the member description for an example. michael@0: * @param dayOfWeek the daylight savings starting day-of-week. Please see michael@0: * the member description for an example. michael@0: * @param time the daylight savings starting time. Please see the member michael@0: * description for an example. michael@0: */ michael@0: michael@0: void michael@0: SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, michael@0: int32_t time, TimeMode mode, UErrorCode& status) michael@0: { michael@0: startMonth = (int8_t)month; michael@0: startDay = (int8_t)dayOfWeekInMonth; michael@0: startDayOfWeek = (int8_t)dayOfWeek; michael@0: startTime = time; michael@0: startTimeMode = mode; michael@0: decodeStartRule(status); michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, michael@0: int32_t time, TimeMode mode, UErrorCode& status) michael@0: { michael@0: setStartRule(month, dayOfMonth, 0, time, mode, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, michael@0: int32_t time, TimeMode mode, UBool after, UErrorCode& status) michael@0: { michael@0: setStartRule(month, after ? dayOfMonth : -dayOfMonth, michael@0: -dayOfWeek, time, mode, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Sets the daylight savings ending rule. For example, in the U.S., Daylight michael@0: * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time. michael@0: * Therefore, you can set the end rule by calling: michael@0: * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000); michael@0: * Various other types of rules can be specified by manipulating the dayOfWeek michael@0: * and dayOfWeekInMonth parameters. For complete details, see the documentation michael@0: * for setStartRule(). michael@0: * @param month the daylight savings ending month. Month is 0-based. michael@0: * eg, 0 for January. michael@0: * @param dayOfWeekInMonth the daylight savings ending michael@0: * day-of-week-in-month. See setStartRule() for a complete explanation. michael@0: * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule() michael@0: * for a complete explanation. michael@0: * @param time the daylight savings ending time. Please see the member michael@0: * description for an example. michael@0: */ michael@0: michael@0: void michael@0: SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, michael@0: int32_t time, TimeMode mode, UErrorCode& status) michael@0: { michael@0: endMonth = (int8_t)month; michael@0: endDay = (int8_t)dayOfWeekInMonth; michael@0: endDayOfWeek = (int8_t)dayOfWeek; michael@0: endTime = time; michael@0: endTimeMode = mode; michael@0: decodeEndRule(status); michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, michael@0: int32_t time, TimeMode mode, UErrorCode& status) michael@0: { michael@0: setEndRule(month, dayOfMonth, 0, time, mode, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, michael@0: int32_t time, TimeMode mode, UBool after, UErrorCode& status) michael@0: { michael@0: setEndRule(month, after ? dayOfMonth : -dayOfMonth, michael@0: -dayOfWeek, time, mode, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: int32_t michael@0: SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, michael@0: uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const michael@0: { michael@0: // Check the month before calling Grego::monthLength(). This michael@0: // duplicates the test that occurs in the 7-argument getOffset(), michael@0: // however, this is unavoidable. We don't mind because this method, in michael@0: // fact, should not be called; internal code should always call the michael@0: // 7-argument getOffset(), and outside code should use Calendar.get(int michael@0: // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of michael@0: // this method because it's public API. - liu 8/10/98 michael@0: if(month < UCAL_JANUARY || month > UCAL_DECEMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status); michael@0: } michael@0: michael@0: int32_t michael@0: SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, michael@0: uint8_t dayOfWeek, int32_t millis, michael@0: int32_t /*monthLength*/, UErrorCode& status) const michael@0: { michael@0: // Check the month before calling Grego::monthLength(). This michael@0: // duplicates a test that occurs in the 9-argument getOffset(), michael@0: // however, this is unavoidable. We don't mind because this method, in michael@0: // fact, should not be called; internal code should always call the michael@0: // 9-argument getOffset(), and outside code should use Calendar.get(int michael@0: // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of michael@0: // this method because it's public API. - liu 8/10/98 michael@0: if (month < UCAL_JANUARY michael@0: || month > UCAL_DECEMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return -1; michael@0: } michael@0: michael@0: // We ignore monthLength because it can be derived from year and month. michael@0: // This is so that February in leap years is calculated correctly. michael@0: // We keep this argument in this function for backwards compatibility. michael@0: return getOffset(era, year, month, day, dayOfWeek, millis, michael@0: Grego::monthLength(year, month), michael@0: Grego::previousMonthLength(year, month), michael@0: status); michael@0: } michael@0: michael@0: int32_t michael@0: SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, michael@0: uint8_t dayOfWeek, int32_t millis, michael@0: int32_t monthLength, int32_t prevMonthLength, michael@0: UErrorCode& status) const michael@0: { michael@0: if(U_FAILURE(status)) return 0; michael@0: michael@0: if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) michael@0: || month < UCAL_JANUARY michael@0: || month > UCAL_DECEMBER michael@0: || day < 1 michael@0: || day > monthLength michael@0: || dayOfWeek < UCAL_SUNDAY michael@0: || dayOfWeek > UCAL_SATURDAY michael@0: || millis < 0 michael@0: || millis >= U_MILLIS_PER_DAY michael@0: || monthLength < 28 michael@0: || monthLength > 31 michael@0: || prevMonthLength < 28 michael@0: || prevMonthLength > 31) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return -1; michael@0: } michael@0: michael@0: int32_t result = rawOffset; michael@0: michael@0: // Bail out if we are before the onset of daylight savings time michael@0: if(!useDaylight || year < startYear || era != GregorianCalendar::AD) michael@0: return result; michael@0: michael@0: // Check for southern hemisphere. We assume that the start and end michael@0: // month are different. michael@0: UBool southern = (startMonth > endMonth); michael@0: michael@0: // Compare the date to the starting and ending rules.+1 = date>rule, -1 michael@0: // = date= 0)) { michael@0: endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, michael@0: (int8_t)day, (int8_t)dayOfWeek, millis, michael@0: endTimeMode == WALL_TIME ? dstSavings : michael@0: (endTimeMode == UTC_TIME ? -rawOffset : 0), michael@0: endMode, (int8_t)endMonth, (int8_t)endDayOfWeek, michael@0: (int8_t)endDay, endTime); michael@0: } michael@0: michael@0: // Check for both the northern and southern hemisphere cases. We michael@0: // assume that in the northern hemisphere, the start rule is before the michael@0: // end rule within the calendar year, and vice versa for the southern michael@0: // hemisphere. michael@0: if ((!southern && (startCompare >= 0 && endCompare < 0)) || michael@0: (southern && (startCompare >= 0 || endCompare < 0))) michael@0: result += dstSavings; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, michael@0: int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: rawOffsetGMT = getRawOffset(); michael@0: int32_t year, month, dom, dow; michael@0: double day = uprv_floor(date / U_MILLIS_PER_DAY); michael@0: int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); michael@0: michael@0: Grego::dayToFields(day, year, month, dom, dow); michael@0: michael@0: savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, michael@0: (uint8_t) dow, millis, michael@0: Grego::monthLength(year, month), michael@0: status) - rawOffsetGMT; michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: UBool recalc = FALSE; michael@0: michael@0: // Now we need some adjustment michael@0: if (savingsDST > 0) { michael@0: if ((nonExistingTimeOpt & kStdDstMask) == kStandard michael@0: || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) { michael@0: date -= getDSTSavings(); michael@0: recalc = TRUE; michael@0: } michael@0: } else { michael@0: if ((duplicatedTimeOpt & kStdDstMask) == kDaylight michael@0: || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) { michael@0: date -= getDSTSavings(); michael@0: recalc = TRUE; michael@0: } michael@0: } michael@0: if (recalc) { michael@0: day = uprv_floor(date / U_MILLIS_PER_DAY); michael@0: millis = (int32_t) (date - day * U_MILLIS_PER_DAY); michael@0: Grego::dayToFields(day, year, month, dom, dow); michael@0: savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, michael@0: (uint8_t) dow, millis, michael@0: Grego::monthLength(year, month), michael@0: status) - rawOffsetGMT; michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Compare a given date in the year to a rule. Return 1, 0, or -1, depending michael@0: * on whether the date is after, equal to, or before the rule date. The michael@0: * millis are compared directly against the ruleMillis, so any michael@0: * standard-daylight adjustments must be handled by the caller. michael@0: * michael@0: * @return 1 if the date is after the rule date, -1 if the date is before michael@0: * the rule date, or 0 if the date is equal to the rule date. michael@0: */ michael@0: int32_t michael@0: SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen, michael@0: int8_t dayOfMonth, michael@0: int8_t dayOfWeek, int32_t millis, int32_t millisDelta, michael@0: EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek, michael@0: int8_t ruleDay, int32_t ruleMillis) michael@0: { michael@0: // Make adjustments for startTimeMode and endTimeMode michael@0: millis += millisDelta; michael@0: while (millis >= U_MILLIS_PER_DAY) { michael@0: millis -= U_MILLIS_PER_DAY; michael@0: ++dayOfMonth; michael@0: dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based michael@0: if (dayOfMonth > monthLen) { michael@0: dayOfMonth = 1; michael@0: /* When incrementing the month, it is desirible to overflow michael@0: * from DECEMBER to DECEMBER+1, since we use the result to michael@0: * compare against a real month. Wraparound of the value michael@0: * leads to bug 4173604. */ michael@0: ++month; michael@0: } michael@0: } michael@0: while (millis < 0) { michael@0: millis += U_MILLIS_PER_DAY; michael@0: --dayOfMonth; michael@0: dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based michael@0: if (dayOfMonth < 1) { michael@0: dayOfMonth = prevMonthLen; michael@0: --month; michael@0: } michael@0: } michael@0: michael@0: // first compare months. If they're different, we don't have to worry about days michael@0: // and times michael@0: if (month < ruleMonth) return -1; michael@0: else if (month > ruleMonth) return 1; michael@0: michael@0: // calculate the actual day of month for the rule michael@0: int32_t ruleDayOfMonth = 0; michael@0: michael@0: // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days. michael@0: if (ruleDay > monthLen) { michael@0: ruleDay = monthLen; michael@0: } michael@0: michael@0: switch (ruleMode) michael@0: { michael@0: // if the mode is day-of-month, the day of month is given michael@0: case DOM_MODE: michael@0: ruleDayOfMonth = ruleDay; michael@0: break; michael@0: michael@0: // if the mode is day-of-week-in-month, calculate the day-of-month from it michael@0: case DOW_IN_MONTH_MODE: michael@0: // In this case ruleDay is the day-of-week-in-month (this code is using michael@0: // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week michael@0: // of the first day of the month, so it's trusting that they're really michael@0: // consistent with each other) michael@0: if (ruleDay > 0) michael@0: ruleDayOfMonth = 1 + (ruleDay - 1) * 7 + michael@0: (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7; michael@0: michael@0: // if ruleDay is negative (we assume it's not zero here), we have to do michael@0: // the same calculation figuring backward from the last day of the month. michael@0: else michael@0: { michael@0: // (again, this code is trusting that dayOfWeek and dayOfMonth are michael@0: // consistent with each other here, since we're using them to figure michael@0: // the day of week of the first of the month) michael@0: ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 - michael@0: (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7; michael@0: } michael@0: break; michael@0: michael@0: case DOW_GE_DOM_MODE: michael@0: ruleDayOfMonth = ruleDay + michael@0: (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7; michael@0: break; michael@0: michael@0: case DOW_LE_DOM_MODE: michael@0: ruleDayOfMonth = ruleDay - michael@0: (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7; michael@0: // Note at this point ruleDayOfMonth may be <1, although it will michael@0: // be >=1 for well-formed rules. michael@0: break; michael@0: } michael@0: michael@0: // now that we have a real day-in-month for the rule, we can compare days... michael@0: if (dayOfMonth < ruleDayOfMonth) return -1; michael@0: else if (dayOfMonth > ruleDayOfMonth) return 1; michael@0: michael@0: // ...and if they're equal, we compare times michael@0: if (millis < ruleMillis) return -1; michael@0: else if (millis > ruleMillis) return 1; michael@0: else return 0; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: int32_t michael@0: SimpleTimeZone::getRawOffset() const michael@0: { michael@0: return rawOffset; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setRawOffset(int32_t offsetMillis) michael@0: { michael@0: rawOffset = offsetMillis; michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) michael@0: { michael@0: if (millisSavedDuringDST <= 0) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: else { michael@0: dstSavings = millisSavedDuringDST; michael@0: } michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: int32_t michael@0: SimpleTimeZone::getDSTSavings() const michael@0: { michael@0: return dstSavings; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: UBool michael@0: SimpleTimeZone::useDaylightTime() const michael@0: { michael@0: return useDaylight; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Overrides TimeZone michael@0: * Queries if the given date is in Daylight Savings Time. michael@0: */ michael@0: UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const michael@0: { michael@0: // This method is wasteful since it creates a new GregorianCalendar and michael@0: // deletes it each time it is called. However, this is a deprecated method michael@0: // and provided only for Java compatibility as of 8/6/97 [LIU]. michael@0: if (U_FAILURE(status)) return FALSE; michael@0: GregorianCalendar *gc = new GregorianCalendar(*this, status); michael@0: /* test for NULL */ michael@0: if (gc == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return FALSE; michael@0: } michael@0: gc->setTime(date, status); michael@0: UBool result = gc->inDaylightTime(status); michael@0: delete gc; michael@0: return result; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Return true if this zone has the same rules and offset as another zone. michael@0: * @param other the TimeZone object to be compared with michael@0: * @return true if the given zone has the same rules and offset as this one michael@0: */ michael@0: UBool michael@0: SimpleTimeZone::hasSameRules(const TimeZone& other) const michael@0: { michael@0: if (this == &other) return TRUE; michael@0: if (typeid(*this) != typeid(other)) return FALSE; michael@0: SimpleTimeZone *that = (SimpleTimeZone*)&other; michael@0: return rawOffset == that->rawOffset && michael@0: useDaylight == that->useDaylight && michael@0: (!useDaylight michael@0: // Only check rules if using DST michael@0: || (dstSavings == that->dstSavings && michael@0: startMode == that->startMode && michael@0: startMonth == that->startMonth && michael@0: startDay == that->startDay && michael@0: startDayOfWeek == that->startDayOfWeek && michael@0: startTime == that->startTime && michael@0: startTimeMode == that->startTimeMode && michael@0: endMode == that->endMode && michael@0: endMonth == that->endMonth && michael@0: endDay == that->endDay && michael@0: endDayOfWeek == that->endDayOfWeek && michael@0: endTime == that->endTime && michael@0: endTimeMode == that->endTimeMode && michael@0: startYear == that->startYear)); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Rule representation michael@0: // michael@0: // We represent the following flavors of rules: michael@0: // 5 the fifth of the month michael@0: // lastSun the last Sunday in the month michael@0: // lastMon the last Monday in the month michael@0: // Sun>=8 first Sunday on or after the eighth michael@0: // Sun<=25 last Sunday on or before the 25th michael@0: // This is further complicated by the fact that we need to remain michael@0: // backward compatible with the 1.1 FCS. Finally, we need to minimize michael@0: // API changes. In order to satisfy these requirements, we support michael@0: // three representation systems, and we translate between them. michael@0: // michael@0: // INTERNAL REPRESENTATION michael@0: // This is the format SimpleTimeZone objects take after construction or michael@0: // streaming in is complete. Rules are represented directly, using an michael@0: // unencoded format. We will discuss the start rule only below; the end michael@0: // rule is analogous. michael@0: // startMode Takes on enumerated values DAY_OF_MONTH, michael@0: // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM. michael@0: // startDay The day of the month, or for DOW_IN_MONTH mode, a michael@0: // value indicating which DOW, such as +1 for first, michael@0: // +2 for second, -1 for last, etc. michael@0: // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH. michael@0: // michael@0: // ENCODED REPRESENTATION michael@0: // This is the format accepted by the constructor and by setStartRule() michael@0: // and setEndRule(). It uses various combinations of positive, negative, michael@0: // and zero values to encode the different rules. This representation michael@0: // allows us to specify all the different rule flavors without altering michael@0: // the API. michael@0: // MODE startMonth startDay startDayOfWeek michael@0: // DOW_IN_MONTH_MODE >=0 !=0 >0 michael@0: // DOM_MODE >=0 >0 ==0 michael@0: // DOW_GE_DOM_MODE >=0 >0 <0 michael@0: // DOW_LE_DOM_MODE >=0 <0 <0 michael@0: // (no DST) don't care ==0 don't care michael@0: // michael@0: // STREAMED REPRESENTATION michael@0: // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only michael@0: // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the michael@0: // flag useDaylight. When we stream an object out, we translate into an michael@0: // approximate DOW_IN_MONTH_MODE representation so the object can be parsed michael@0: // and used by 1.1 code. Following that, we write out the full michael@0: // representation separately so that contemporary code can recognize and michael@0: // parse it. The full representation is written in a "packed" format, michael@0: // consisting of a version number, a length, and an array of bytes. Future michael@0: // versions of this class may specify different versions. If they wish to michael@0: // include additional data, they should do so by storing them after the michael@0: // packed representation below. michael@0: //---------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Given a set of encoded rules in startDay and startDayOfMonth, decode michael@0: * them and set the startMode appropriately. Do the same for endDay and michael@0: * endDayOfMonth. Upon entry, the day of week variables may be zero or michael@0: * negative, in order to indicate special modes. The day of month michael@0: * variables may also be negative. Upon exit, the mode variables will be michael@0: * set, and the day of week and day of month variables will be positive. michael@0: * This method also recognizes a startDay or endDay of zero as indicating michael@0: * no DST. michael@0: */ michael@0: void michael@0: SimpleTimeZone::decodeRules(UErrorCode& status) michael@0: { michael@0: decodeStartRule(status); michael@0: decodeEndRule(status); michael@0: } michael@0: michael@0: /** michael@0: * Decode the start rule and validate the parameters. The parameters are michael@0: * expected to be in encoded form, which represents the various rule modes michael@0: * by negating or zeroing certain values. Representation formats are: michael@0: *

michael@0: *

michael@0:  *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
michael@0:  *            ------------  -----  --------  --------  ----------
michael@0:  * month       0..11        same    same      same     don't care
michael@0:  * day        -5..5         1..31   1..31    -1..-31   0
michael@0:  * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
michael@0:  * time        0..ONEDAY    same    same      same     don't care
michael@0:  * 
michael@0: * The range for month does not include UNDECIMBER since this class is michael@0: * really specific to GregorianCalendar, which does not use that month. michael@0: * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the michael@0: * end rule is an exclusive limit point. That is, the range of times that michael@0: * are in DST include those >= the start and < the end. For this reason, michael@0: * it should be possible to specify an end of ONEDAY in order to include the michael@0: * entire day. Although this is equivalent to time 0 of the following day, michael@0: * it's not always possible to specify that, for example, on December 31. michael@0: * While arguably the start range should still be 0..ONEDAY-1, we keep michael@0: * the start and end ranges the same for consistency. michael@0: */ michael@0: void michael@0: SimpleTimeZone::decodeStartRule(UErrorCode& status) michael@0: { michael@0: if(U_FAILURE(status)) return; michael@0: michael@0: useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); michael@0: if (useDaylight && dstSavings == 0) { michael@0: dstSavings = U_MILLIS_PER_HOUR; michael@0: } michael@0: if (startDay != 0) { michael@0: if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if (startTime < 0 || startTime > U_MILLIS_PER_DAY || michael@0: startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if (startDayOfWeek == 0) { michael@0: startMode = DOM_MODE; michael@0: } else { michael@0: if (startDayOfWeek > 0) { michael@0: startMode = DOW_IN_MONTH_MODE; michael@0: } else { michael@0: startDayOfWeek = (int8_t)-startDayOfWeek; michael@0: if (startDay > 0) { michael@0: startMode = DOW_GE_DOM_MODE; michael@0: } else { michael@0: startDay = (int8_t)-startDay; michael@0: startMode = DOW_LE_DOM_MODE; michael@0: } michael@0: } michael@0: if (startDayOfWeek > UCAL_SATURDAY) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: if (startMode == DOW_IN_MONTH_MODE) { michael@0: if (startDay < -5 || startDay > 5) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Decode the end rule and validate the parameters. This method is exactly michael@0: * analogous to decodeStartRule(). michael@0: * @see decodeStartRule michael@0: */ michael@0: void michael@0: SimpleTimeZone::decodeEndRule(UErrorCode& status) michael@0: { michael@0: if(U_FAILURE(status)) return; michael@0: michael@0: useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); michael@0: if (useDaylight && dstSavings == 0) { michael@0: dstSavings = U_MILLIS_PER_HOUR; michael@0: } michael@0: if (endDay != 0) { michael@0: if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if (endTime < 0 || endTime > U_MILLIS_PER_DAY || michael@0: endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if (endDayOfWeek == 0) { michael@0: endMode = DOM_MODE; michael@0: } else { michael@0: if (endDayOfWeek > 0) { michael@0: endMode = DOW_IN_MONTH_MODE; michael@0: } else { michael@0: endDayOfWeek = (int8_t)-endDayOfWeek; michael@0: if (endDay > 0) { michael@0: endMode = DOW_GE_DOM_MODE; michael@0: } else { michael@0: endDay = (int8_t)-endDay; michael@0: endMode = DOW_LE_DOM_MODE; michael@0: } michael@0: } michael@0: if (endDayOfWeek > UCAL_SATURDAY) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: if (endMode == DOW_IN_MONTH_MODE) { michael@0: if (endDay < -5 || endDay > 5) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { michael@0: if (!useDaylight) { michael@0: return FALSE; michael@0: } michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: checkTransitionRules(status); michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: UDate firstTransitionTime = firstTransition->getTime(); michael@0: if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) { michael@0: result = *firstTransition; michael@0: } michael@0: UDate stdDate, dstDate; michael@0: UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); michael@0: UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); michael@0: if (stdAvail && (!dstAvail || stdDate < dstDate)) { michael@0: result.setTime(stdDate); michael@0: result.setFrom((const TimeZoneRule&)*dstRule); michael@0: result.setTo((const TimeZoneRule&)*stdRule); michael@0: return TRUE; michael@0: } michael@0: if (dstAvail && (!stdAvail || dstDate < stdDate)) { michael@0: result.setTime(dstDate); michael@0: result.setFrom((const TimeZoneRule&)*stdRule); michael@0: result.setTo((const TimeZoneRule&)*dstRule); michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { michael@0: if (!useDaylight) { michael@0: return FALSE; michael@0: } michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: checkTransitionRules(status); michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: UDate firstTransitionTime = firstTransition->getTime(); michael@0: if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) { michael@0: return FALSE; michael@0: } michael@0: UDate stdDate, dstDate; michael@0: UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); michael@0: UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); michael@0: if (stdAvail && (!dstAvail || stdDate > dstDate)) { michael@0: result.setTime(stdDate); michael@0: result.setFrom((const TimeZoneRule&)*dstRule); michael@0: result.setTo((const TimeZoneRule&)*stdRule); michael@0: return TRUE; michael@0: } michael@0: if (dstAvail && (!stdAvail || dstDate > stdDate)) { michael@0: result.setTime(dstDate); michael@0: result.setFrom((const TimeZoneRule&)*stdRule); michael@0: result.setTo((const TimeZoneRule&)*dstRule); michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: void michael@0: SimpleTimeZone::clearTransitionRules(void) { michael@0: initialRule = NULL; michael@0: firstTransition = NULL; michael@0: stdRule = NULL; michael@0: dstRule = NULL; michael@0: transitionRulesInitialized = FALSE; michael@0: } michael@0: michael@0: void michael@0: SimpleTimeZone::deleteTransitionRules(void) { michael@0: if (initialRule != NULL) { michael@0: delete initialRule; michael@0: } michael@0: if (firstTransition != NULL) { michael@0: delete firstTransition; michael@0: } michael@0: if (stdRule != NULL) { michael@0: delete stdRule; michael@0: } michael@0: if (dstRule != NULL) { michael@0: delete dstRule; michael@0: } michael@0: clearTransitionRules(); michael@0: } michael@0: michael@0: /* michael@0: * Lazy transition rules initializer michael@0: * michael@0: * Note On the removal of UMTX_CHECK from checkTransitionRules(): michael@0: * michael@0: * It would be faster to have a UInitOnce as part of a SimpleTimeZone object, michael@0: * which would avoid needing to lock a mutex to check the initialization state. michael@0: * But we can't easily because simpletz.h is a public header, and including michael@0: * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers. michael@0: * michael@0: * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object, michael@0: * allocate it in the constructors. This would be a more intrusive change, but doable michael@0: * if performance turns out to be an issue. michael@0: */ michael@0: static UMutex gLock = U_MUTEX_INITIALIZER; michael@0: michael@0: void michael@0: SimpleTimeZone::checkTransitionRules(UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: umtx_lock(&gLock); michael@0: if (!transitionRulesInitialized) { michael@0: SimpleTimeZone *ncThis = const_cast(this); michael@0: ncThis->initTransitionRules(status); michael@0: } michael@0: umtx_unlock(&gLock); michael@0: } michael@0: michael@0: void michael@0: SimpleTimeZone::initTransitionRules(UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: if (transitionRulesInitialized) { michael@0: return; michael@0: } michael@0: deleteTransitionRules(); michael@0: UnicodeString tzid; michael@0: getID(tzid); michael@0: michael@0: if (useDaylight) { michael@0: DateTimeRule* dtRule; michael@0: DateTimeRule::TimeRuleType timeRuleType; michael@0: UDate firstStdStart, firstDstStart; michael@0: michael@0: // Create a TimeZoneRule for daylight saving time michael@0: timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : michael@0: ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); michael@0: switch (startMode) { michael@0: case DOM_MODE: michael@0: dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); michael@0: break; michael@0: case DOW_IN_MONTH_MODE: michael@0: dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType); michael@0: break; michael@0: case DOW_GE_DOM_MODE: michael@0: dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType); michael@0: break; michael@0: case DOW_LE_DOM_MODE: michael@0: dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType); michael@0: break; michael@0: default: michael@0: status = U_INVALID_STATE_ERROR; michael@0: return; michael@0: } michael@0: // Check for Null pointer michael@0: if (dtRule == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: // For now, use ID + "(DST)" as the name michael@0: dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(), michael@0: dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); michael@0: michael@0: // Check for Null pointer michael@0: if (dstRule == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: deleteTransitionRules(); michael@0: return; michael@0: } michael@0: michael@0: // Calculate the first DST start time michael@0: dstRule->getFirstStart(getRawOffset(), 0, firstDstStart); michael@0: michael@0: // Create a TimeZoneRule for standard time michael@0: timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : michael@0: ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); michael@0: switch (endMode) { michael@0: case DOM_MODE: michael@0: dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); michael@0: break; michael@0: case DOW_IN_MONTH_MODE: michael@0: dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); michael@0: break; michael@0: case DOW_GE_DOM_MODE: michael@0: dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType); michael@0: break; michael@0: case DOW_LE_DOM_MODE: michael@0: dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType); michael@0: break; michael@0: } michael@0: michael@0: // Check for Null pointer michael@0: if (dtRule == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: deleteTransitionRules(); michael@0: return; michael@0: } michael@0: // For now, use ID + "(STD)" as the name michael@0: stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0, michael@0: dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); michael@0: michael@0: //Check for Null pointer michael@0: if (stdRule == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: deleteTransitionRules(); michael@0: return; michael@0: } michael@0: michael@0: // Calculate the first STD start time michael@0: stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart); michael@0: michael@0: // Create a TimeZoneRule for initial time michael@0: if (firstStdStart < firstDstStart) { michael@0: initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings()); michael@0: firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule); michael@0: } else { michael@0: initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0); michael@0: firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule); michael@0: } michael@0: // Check for null pointers. michael@0: if (initialRule == NULL || firstTransition == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: deleteTransitionRules(); michael@0: return; michael@0: } michael@0: michael@0: } else { michael@0: // Create a TimeZoneRule for initial time michael@0: initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0); michael@0: // Check for null pointer. michael@0: if (initialRule == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: deleteTransitionRules(); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: transitionRulesInitialized = TRUE; michael@0: } michael@0: michael@0: int32_t michael@0: SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const { michael@0: return (useDaylight) ? 2 : 0; michael@0: } michael@0: michael@0: void michael@0: SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, michael@0: const TimeZoneRule* trsrules[], michael@0: int32_t& trscount, michael@0: UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: checkTransitionRules(status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: initial = initialRule; michael@0: int32_t cnt = 0; michael@0: if (stdRule != NULL) { michael@0: if (cnt < trscount) { michael@0: trsrules[cnt++] = stdRule; michael@0: } michael@0: if (cnt < trscount) { michael@0: trsrules[cnt++] = dstRule; michael@0: } michael@0: } michael@0: trscount = cnt; michael@0: } michael@0: michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof