| /* |
| ******************************************************************************* |
| * Copyright (C) 1997-1999, 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 "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #include "unicode/simpletz.h" |
| #include "unicode/gregocal.h" |
| #include "tzdat.h" |
| |
| U_NAMESPACE_BEGIN |
| |
| const char SimpleTimeZone::fgClassID = 0; // Value is irrelevant |
| |
| // WARNING: assumes that no rule is measured from the end of February, |
| // since we don't handle leap years. Could handle assuming always |
| // Gregorian, since we know they didn't have daylight time when |
| // Gregorian calendar started. |
| const int8_t SimpleTimeZone::staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31}; |
| |
| // ***************************************************************************** |
| // class SimpleTimeZone |
| // ***************************************************************************** |
| |
| |
| SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID) |
| : TimeZone(ID), |
| startMonth(0), |
| startDay(0), |
| startDayOfWeek(0), |
| startTime(0), |
| startTimeMode(WALL_TIME), |
| endTimeMode(WALL_TIME), |
| endMonth(0), |
| endDay(0), |
| endDayOfWeek(0), |
| endTime(0), |
| startYear(0), |
| rawOffset(rawOffsetGMT), |
| useDaylight(FALSE), |
| startMode(DOM_MODE), |
| endMode(DOM_MODE), |
| dstSavings(U_MILLIS_PER_HOUR) |
| { |
| } |
| |
| // ------------------------------------- |
| |
| SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
| int8_t savingsStartMonth, int8_t savingsStartDay, |
| int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
| int8_t savingsEndMonth, int8_t savingsEndDay, |
| int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
| UErrorCode& status) |
| : TimeZone(ID) |
| { |
| construct(rawOffsetGMT, |
| savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
| savingsStartTime, WALL_TIME, |
| savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
| savingsEndTime, WALL_TIME, |
| U_MILLIS_PER_HOUR, status); |
| } |
| |
| // ------------------------------------- |
| |
| SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
| int8_t savingsStartMonth, int8_t savingsStartDay, |
| int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
| int8_t savingsEndMonth, int8_t savingsEndDay, |
| int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
| int32_t savingsDST, UErrorCode& status) |
| : TimeZone(ID) |
| { |
| construct(rawOffsetGMT, |
| savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
| savingsStartTime, WALL_TIME, |
| savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
| savingsEndTime, WALL_TIME, |
| savingsDST, status); |
| } |
| |
| // ------------------------------------- |
| |
| SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
| int8_t savingsStartMonth, int8_t savingsStartDay, |
| int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
| TimeMode savingsStartTimeMode, |
| int8_t savingsEndMonth, int8_t savingsEndDay, |
| int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
| TimeMode savingsEndTimeMode, |
| int32_t savingsDST, UErrorCode& status) |
| : TimeZone(ID) |
| { |
| construct(rawOffsetGMT, |
| savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
| savingsStartTime, savingsStartTimeMode, |
| savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
| savingsEndTime, savingsEndTimeMode, |
| savingsDST, status); |
| } |
| |
| /** |
| * Construct from memory-mapped data. For private use by TimeZone. |
| */ |
| SimpleTimeZone::SimpleTimeZone(const StandardZone& stdZone, |
| const UnicodeString& ID) |
| : TimeZone(ID) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| construct(stdZone.gmtOffset, |
| 0, 0, 0, 0, WALL_TIME, |
| 0, 0, 0, 0, WALL_TIME, |
| 0, status); |
| } |
| |
| /** |
| * Construct from memory-mapped data. For private use by TimeZone. |
| */ |
| SimpleTimeZone::SimpleTimeZone(const DSTZone& dstZone, |
| const UnicodeString& ID) |
| : TimeZone(ID) |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| construct(dstZone.gmtOffset, |
| dstZone.onsetRule.month, dstZone.onsetRule.dowim, |
| dstZone.onsetRule.dow, |
| dstZone.onsetRule.time * (int32_t)60000, |
| (TimeMode)dstZone.onsetRule.mode, |
| dstZone.ceaseRule.month, dstZone.ceaseRule.dowim, |
| dstZone.ceaseRule.dow, |
| dstZone.ceaseRule.time * (int32_t)60000, |
| (TimeMode)dstZone.ceaseRule.mode, |
| dstZone.dstSavings * (int32_t)60000, status); |
| } |
| |
| /** |
| * Internal construction method. |
| */ |
| void SimpleTimeZone::construct(int32_t rawOffsetGMT, |
| int8_t savingsStartMonth, |
| int8_t savingsStartDay, |
| int8_t savingsStartDayOfWeek, |
| int32_t savingsStartTime, |
| TimeMode savingsStartTimeMode, |
| int8_t savingsEndMonth, |
| int8_t savingsEndDay, |
| int8_t savingsEndDayOfWeek, |
| int32_t savingsEndTime, |
| TimeMode savingsEndTimeMode, |
| int32_t savingsDST, |
| UErrorCode& status) |
| { |
| this->rawOffset = rawOffsetGMT; |
| this->startMonth = savingsStartMonth; |
| this->startDay = savingsStartDay; |
| this->startDayOfWeek = savingsStartDayOfWeek; |
| this->startTime = savingsStartTime; |
| this->startTimeMode = savingsStartTimeMode; |
| this->endMonth = savingsEndMonth; |
| this->endDay = savingsEndDay; |
| this->endDayOfWeek = savingsEndDayOfWeek; |
| this->endTime = savingsEndTime; |
| this->endTimeMode = savingsEndTimeMode; |
| this->dstSavings = savingsDST; |
| this->startYear = 0; |
| this->startMode = DOM_MODE; |
| this->endMode = DOM_MODE; |
| |
| decodeRules(status); |
| |
| if (savingsDST <= 0) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| } |
| } |
| |
| // ------------------------------------- |
| |
| SimpleTimeZone::~SimpleTimeZone() |
| { |
| } |
| |
| // ------------------------------------- |
| |
| // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. |
| SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source) |
| : TimeZone(source) |
| { |
| *this = source; |
| } |
| |
| // ------------------------------------- |
| |
| // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. |
| SimpleTimeZone & |
| SimpleTimeZone::operator=(const SimpleTimeZone &right) |
| { |
| if (this != &right) |
| { |
| TimeZone::operator=(right); |
| rawOffset = right.rawOffset; |
| startMonth = right.startMonth; |
| startDay = right.startDay; |
| startDayOfWeek = right.startDayOfWeek; |
| startTime = right.startTime; |
| startTimeMode = right.startTimeMode; |
| startMode = right.startMode; |
| endMonth = right.endMonth; |
| endDay = right.endDay; |
| endDayOfWeek = right.endDayOfWeek; |
| endTime = right.endTime; |
| endTimeMode = right.endTimeMode; |
| endMode = right.endMode; |
| startYear = right.startYear; |
| dstSavings = right.dstSavings; |
| useDaylight = right.useDaylight; |
| } |
| return *this; |
| } |
| |
| // ------------------------------------- |
| |
| UBool |
| SimpleTimeZone::operator==(const TimeZone& that) const |
| { |
| return ((this == &that) || |
| (getDynamicClassID() == that.getDynamicClassID() && |
| TimeZone::operator==(that) && |
| hasSameRules(that))); |
| } |
| |
| // ------------------------------------- |
| |
| // Called by TimeZone::createDefault() inside a Mutex - be careful. |
| TimeZone* |
| SimpleTimeZone::clone() const |
| { |
| return new SimpleTimeZone(*this); |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Sets the daylight savings starting year, that is, the year this time zone began |
| * observing its specified daylight savings time rules. The time zone is considered |
| * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't |
| * support historical daylight-savings-time rules. |
| * @param year the daylight savings starting year. |
| */ |
| void |
| SimpleTimeZone::setStartYear(int32_t year) |
| { |
| startYear = year; |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings |
| * Time starts at the first Sunday in April, at 2 AM in standard time. |
| * Therefore, you can set the start rule by calling: |
| * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000); |
| * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate |
| * the exact starting date. Their exact meaning depend on their respective signs, |
| * allowing various types of rules to be constructed, as follows:<ul> |
| * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the |
| * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday |
| * of the month). |
| * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify |
| * the day of week in the month counting backward from the end of the month. |
| * (e.g., (-1, MONDAY) is the last Monday in the month) |
| * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth |
| * specifies the day of the month, regardless of what day of the week it is. |
| * (e.g., (10, 0) is the tenth day of the month) |
| * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth |
| * specifies the day of the month counting backward from the end of the |
| * month, regardless of what day of the week it is (e.g., (-2, 0) is the |
| * next-to-last day of the month). |
| * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the |
| * first specified day of the week on or after the specfied day of the month. |
| * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month |
| * [or the 15th itself if the 15th is a Sunday].) |
| * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the |
| * last specified day of the week on or before the specified day of the month. |
| * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month |
| * [or the 20th itself if the 20th is a Tuesday].)</ul> |
| * @param month the daylight savings starting month. Month is 0-based. |
| * eg, 0 for January. |
| * @param dayOfWeekInMonth the daylight savings starting |
| * day-of-week-in-month. Please see the member description for an example. |
| * @param dayOfWeek the daylight savings starting day-of-week. Please see |
| * the member description for an example. |
| * @param time the daylight savings starting time. Please see the member |
| * description for an example. |
| */ |
| |
| void |
| SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, |
| int32_t time, TimeMode mode, UErrorCode& status) |
| { |
| startMonth = (int8_t)month; |
| startDay = (int8_t)dayOfWeekInMonth; |
| startDayOfWeek = (int8_t)dayOfWeek; |
| startTime = time; |
| startTimeMode = mode; |
| decodeStartRule(status); |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, |
| int32_t time, TimeMode mode, UErrorCode& status) |
| { |
| setStartRule(month, dayOfMonth, 0, time, mode, status); |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, |
| int32_t time, TimeMode mode, UBool after, UErrorCode& status) |
| { |
| setStartRule(month, after ? dayOfMonth : -dayOfMonth, |
| -dayOfWeek, time, mode, status); |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Sets the daylight savings ending rule. For example, in the U.S., Daylight |
| * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time. |
| * Therefore, you can set the end rule by calling: |
| * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000); |
| * Various other types of rules can be specified by manipulating the dayOfWeek |
| * and dayOfWeekInMonth parameters. For complete details, see the documentation |
| * for setStartRule(). |
| * @param month the daylight savings ending month. Month is 0-based. |
| * eg, 0 for January. |
| * @param dayOfWeekInMonth the daylight savings ending |
| * day-of-week-in-month. See setStartRule() for a complete explanation. |
| * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule() |
| * for a complete explanation. |
| * @param time the daylight savings ending time. Please see the member |
| * description for an example. |
| */ |
| |
| void |
| SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, |
| int32_t time, TimeMode mode, UErrorCode& status) |
| { |
| endMonth = (int8_t)month; |
| endDay = (int8_t)dayOfWeekInMonth; |
| endDayOfWeek = (int8_t)dayOfWeek; |
| endTime = time; |
| endTimeMode = mode; |
| decodeEndRule(status); |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, |
| int32_t time, TimeMode mode, UErrorCode& status) |
| { |
| setEndRule(month, dayOfMonth, 0, time, mode, status); |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, |
| int32_t time, TimeMode mode, UBool after, UErrorCode& status) |
| { |
| setEndRule(month, after ? dayOfMonth : -dayOfMonth, |
| -dayOfWeek, time, mode, status); |
| } |
| |
| // ------------------------------------- |
| #ifdef ICU_TIMEZONE_USE_DEPRECATES |
| // deprecated version |
| int32_t |
| SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, |
| uint8_t dayOfWeek, int32_t millis) const |
| { |
| UErrorCode status = U_ZERO_ERROR; |
| return getOffset(era, year, month, day, dayOfWeek, millis, status); |
| } |
| #endif |
| |
| 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 |
| { |
| // Check the month before indexing into staticMonthLength. 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 |
| // 7-argument getOffset(), and outside code should use Calendar.get(int |
| // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of |
| // this method because it's public API. - liu 8/10/98 |
| if(month < Calendar::JANUARY || month > Calendar::DECEMBER) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return 0; |
| } |
| |
| return getOffset(era, year, month, day, dayOfWeek, millis, staticMonthLength[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 monthLength, UErrorCode& status) const { |
| // Check the month before indexing into staticMonthLength. 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 |
| // 9-argument getOffset(), and outside code should use Calendar.get(int |
| // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of |
| // this method because it's public API. - liu 8/10/98 |
| if (month < Calendar::JANUARY |
| || month > Calendar::DECEMBER) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return -1; |
| } |
| |
| // TODO FIX We don't handle leap years yet! |
| int32_t prevMonthLength = (month >= 1) ? staticMonthLength[month - 1] : 31; |
| |
| return getOffset(era, year, month, day, dayOfWeek, millis, |
| monthLength, prevMonthLength, 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 monthLength, int32_t prevMonthLength, |
| UErrorCode& status) const |
| { |
| if(U_FAILURE(status)) return 0; |
| |
| if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) |
| || month < Calendar::JANUARY |
| || month > Calendar::DECEMBER |
| || day < 1 |
| || day > monthLength |
| || dayOfWeek < Calendar::SUNDAY |
| || dayOfWeek > Calendar::SATURDAY |
| || millis < 0 |
| || millis >= U_MILLIS_PER_DAY |
| || monthLength < 28 |
| || monthLength > 31) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return -1; |
| } |
| |
| int32_t result = rawOffset; |
| |
| // Bail out if we are before the onset of daylight savings time |
| if(!useDaylight || year < startYear || era != GregorianCalendar::AD) |
| return result; |
| |
| // Check for southern hemisphere. We assume that the start and end |
| // month are different. |
| UBool southern = (startMonth > endMonth); |
| |
| // Compare the date to the starting and ending rules.+1 = date>rule, -1 |
| // = date<rule, 0 = date==rule. |
| int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, |
| (int8_t)day, (int8_t)dayOfWeek, millis, |
| startTimeMode == UTC_TIME ? -rawOffset : 0, |
| startMode, (int8_t)startMonth, (int8_t)startDayOfWeek, |
| (int8_t)startDay, startTime); |
| int32_t endCompare = 0; |
| |
| /* We don't always have to compute endCompare. For many instances, |
| * startCompare is enough to determine if we are in DST or not. In the |
| * northern hemisphere, if we are before the start rule, we can't have |
| * DST. In the southern hemisphere, if we are after the start rule, we |
| * must have DST. This is reflected in the way the next if statement |
| * (not the one immediately following) short circuits. */ |
| if(southern != (startCompare >= 0)) { |
| endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, |
| (int8_t)day, (int8_t)dayOfWeek, millis, |
| endTimeMode == WALL_TIME ? dstSavings : |
| (endTimeMode == UTC_TIME ? -rawOffset : 0), |
| endMode, (int8_t)endMonth, (int8_t)endDayOfWeek, |
| (int8_t)endDay, endTime); |
| } |
| |
| // Check for both the northern and southern hemisphere cases. We |
| // assume that in the northern hemisphere, the start rule is before the |
| // end rule within the calendar year, and vice versa for the southern |
| // hemisphere. |
| if ((!southern && (startCompare >= 0 && endCompare < 0)) || |
| (southern && (startCompare >= 0 || endCompare < 0))) |
| result += dstSavings; |
| |
| return result; |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Compare a given date in the year to a rule. Return 1, 0, or -1, depending |
| * on whether the date is after, equal to, or before the rule date. The |
| * millis are compared directly against the ruleMillis, so any |
| * standard-daylight adjustments must be handled by the caller. |
| * |
| * @return 1 if the date is after the rule date, -1 if the date is before |
| * the rule date, or 0 if the date is equal to the rule date. |
| */ |
| int32_t |
| SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen, |
| int8_t dayOfMonth, |
| int8_t dayOfWeek, int32_t millis, int32_t millisDelta, |
| EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek, |
| int8_t ruleDay, int32_t ruleMillis) |
| { |
| // Make adjustments for startTimeMode and endTimeMode |
| millis += millisDelta; |
| while (millis >= U_MILLIS_PER_DAY) { |
| millis -= U_MILLIS_PER_DAY; |
| ++dayOfMonth; |
| dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based |
| if (dayOfMonth > monthLen) { |
| dayOfMonth = 1; |
| /* When incrementing the month, it is desirible to overflow |
| * from DECEMBER to DECEMBER+1, since we use the result to |
| * compare against a real month. Wraparound of the value |
| * leads to bug 4173604. */ |
| ++month; |
| } |
| } |
| while (millis < 0) { |
| millis += U_MILLIS_PER_DAY; |
| --dayOfMonth; |
| dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based |
| if (dayOfMonth < 1) { |
| dayOfMonth = prevMonthLen; |
| --month; |
| } |
| } |
| |
| // first compare months. If they're different, we don't have to worry about days |
| // and times |
| if (month < ruleMonth) return -1; |
| else if (month > ruleMonth) return 1; |
| |
| // calculate the actual day of month for the rule |
| int32_t ruleDayOfMonth = 0; |
| switch (ruleMode) |
| { |
| // if the mode is day-of-month, the day of month is given |
| case DOM_MODE: |
| ruleDayOfMonth = ruleDay; |
| break; |
| |
| // if the mode is day-of-week-in-month, calculate the day-of-month from it |
| case DOW_IN_MONTH_MODE: |
| // In this case ruleDay is the day-of-week-in-month (this code is using |
| // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week |
| // of the first day of the month, so it's trusting that they're really |
| // consistent with each other) |
| if (ruleDay > 0) |
| ruleDayOfMonth = 1 + (ruleDay - 1) * 7 + |
| (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7; |
| |
| // 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 |
| { |
| // (again, this code is trusting that dayOfMonth 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 - |
| (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7; |
| } |
| break; |
| |
| case DOW_GE_DOM_MODE: |
| ruleDayOfMonth = ruleDay + |
| (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7; |
| break; |
| |
| case DOW_LE_DOM_MODE: |
| ruleDayOfMonth = ruleDay - |
| (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7; |
| // Note at this point ruleDayOfMonth may be <1, although it will |
| // be >=1 for well-formed rules. |
| break; |
| } |
| |
| // now that we have a real day-in-month for the rule, we can compare days... |
| if (dayOfMonth < ruleDayOfMonth) return -1; |
| else if (dayOfMonth > ruleDayOfMonth) return 1; |
| |
| // ...and if they're equal, we compare times |
| if (millis < ruleMillis) return -1; |
| else if (millis > ruleMillis) return 1; |
| else return 0; |
| } |
| |
| // ------------------------------------- |
| |
| int32_t |
| SimpleTimeZone::getRawOffset() const |
| { |
| return rawOffset; |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setRawOffset(int32_t offsetMillis) |
| { |
| rawOffset = offsetMillis; |
| } |
| |
| // ------------------------------------- |
| |
| void |
| SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) |
| { |
| if (millisSavedDuringDST <= 0) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| } |
| else { |
| dstSavings = millisSavedDuringDST; |
| } |
| } |
| |
| // ------------------------------------- |
| |
| int32_t |
| SimpleTimeZone::getDSTSavings() const |
| { |
| return dstSavings; |
| } |
| |
| // ------------------------------------- |
| |
| UBool |
| SimpleTimeZone::useDaylightTime() const |
| { |
| return useDaylight; |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Overrides TimeZone |
| * Queries if the given date is in Daylight Savings Time. |
| */ |
| UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const |
| { |
| // This method is wasteful since it creates a new GregorianCalendar and |
| // deletes it each time it is called. However, this is a deprecated method |
| // and provided only for Java compatibility as of 8/6/97 [LIU]. |
| if (U_FAILURE(status)) return FALSE; |
| GregorianCalendar *gc = new GregorianCalendar(*this, status); |
| /* test for NULL */ |
| if (gc == 0) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| return FALSE; |
| } |
| gc->setTime(date, status); |
| UBool result = gc->inDaylightTime(status); |
| delete gc; |
| return result; |
| } |
| |
| // ------------------------------------- |
| |
| /** |
| * Return true if this zone has the same rules and offset as another zone. |
| * @param other the TimeZone object to be compared with |
| * @return true if the given zone has the same rules and offset as this one |
| */ |
| UBool |
| SimpleTimeZone::hasSameRules(const TimeZone& other) const |
| { |
| if (this == &other) return TRUE; |
| if (other.getDynamicClassID() != SimpleTimeZone::getStaticClassID()) return FALSE; |
| SimpleTimeZone *that = (SimpleTimeZone*)&other; |
| return rawOffset == that->rawOffset && |
| useDaylight == that->useDaylight && |
| (!useDaylight |
| // Only check rules if using DST |
| || (dstSavings == that->dstSavings && |
| startMode == that->startMode && |
| startMonth == that->startMonth && |
| startDay == that->startDay && |
| startDayOfWeek == that->startDayOfWeek && |
| startTime == that->startTime && |
| startTimeMode == that->startTimeMode && |
| endMode == that->endMode && |
| endMonth == that->endMonth && |
| endDay == that->endDay && |
| endDayOfWeek == that->endDayOfWeek && |
| endTime == that->endTime && |
| endTimeMode == that->endTimeMode && |
| startYear == that->startYear)); |
| } |
| |
| // ------------------------------------- |
| |
| //---------------------------------------------------------------------- |
| // Rule representation |
| // |
| // We represent the following flavors of rules: |
| // 5 the fifth of the month |
| // lastSun the last Sunday in the month |
| // lastMon the last Monday in the month |
| // Sun>=8 first Sunday on or after the eighth |
| // Sun<=25 last Sunday on or before the 25th |
| // This is further complicated by the fact that we need to remain |
| // backward compatible with the 1.1 FCS. Finally, we need to minimize |
| // API changes. In order to satisfy these requirements, we support |
| // three representation systems, and we translate between them. |
| // |
| // INTERNAL REPRESENTATION |
| // This is the format SimpleTimeZone objects take after construction or |
| // streaming in is complete. Rules are represented directly, using an |
| // unencoded format. We will discuss the start rule only below; the end |
| // rule is analogous. |
| // startMode Takes on enumerated values DAY_OF_MONTH, |
| // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM. |
| // startDay The day of the month, or for DOW_IN_MONTH mode, a |
| // value indicating which DOW, such as +1 for first, |
| // +2 for second, -1 for last, etc. |
| // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH. |
| // |
| // ENCODED REPRESENTATION |
| // This is the format accepted by the constructor and by setStartRule() |
| // and setEndRule(). It uses various combinations of positive, negative, |
| // and zero values to encode the different rules. This representation |
| // allows us to specify all the different rule flavors without altering |
| // the API. |
| // MODE startMonth startDay startDayOfWeek |
| // DOW_IN_MONTH_MODE >=0 !=0 >0 |
| // DOM_MODE >=0 >0 ==0 |
| // DOW_GE_DOM_MODE >=0 >0 <0 |
| // DOW_LE_DOM_MODE >=0 <0 <0 |
| // (no DST) don't care ==0 don't care |
| // |
| // STREAMED REPRESENTATION |
| // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only |
| // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the |
| // flag useDaylight. When we stream an object out, we translate into an |
| // approximate DOW_IN_MONTH_MODE representation so the object can be parsed |
| // and used by 1.1 code. Following that, we write out the full |
| // representation separately so that contemporary code can recognize and |
| // parse it. The full representation is written in a "packed" format, |
| // consisting of a version number, a length, and an array of bytes. Future |
| // versions of this class may specify different versions. If they wish to |
| // include additional data, they should do so by storing them after the |
| // packed representation below. |
| //---------------------------------------------------------------------- |
| |
| /** |
| * Given a set of encoded rules in startDay and startDayOfMonth, decode |
| * them and set the startMode appropriately. Do the same for endDay and |
| * endDayOfMonth. Upon entry, the day of week variables may be zero or |
| * negative, in order to indicate special modes. The day of month |
| * variables may also be negative. Upon exit, the mode variables will be |
| * set, and the day of week and day of month variables will be positive. |
| * This method also recognizes a startDay or endDay of zero as indicating |
| * no DST. |
| */ |
| void |
| SimpleTimeZone::decodeRules(UErrorCode& status) |
| { |
| decodeStartRule(status); |
| decodeEndRule(status); |
| } |
| |
| /** |
| * Decode the start rule and validate the parameters. The parameters are |
| * expected to be in encoded form, which represents the various rule modes |
| * by negating or zeroing certain values. Representation formats are: |
| * <p> |
| * <pre> |
| * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST |
| * ------------ ----- -------- -------- ---------- |
| * month 0..11 same same same don't care |
| * day -5..5 1..31 1..31 -1..-31 0 |
| * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care |
| * time 0..ONEDAY same same same don't care |
| * </pre> |
| * The range for month does not include UNDECIMBER since this class is |
| * really specific to GregorianCalendar, which does not use that month. |
| * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the |
| * end rule is an exclusive limit point. That is, the range of times that |
| * are in DST include those >= the start and < the end. For this reason, |
| * it should be possible to specify an end of ONEDAY in order to include the |
| * entire day. Although this is equivalent to time 0 of the following day, |
| * it's not always possible to specify that, for example, on December 31. |
| * While arguably the start range should still be 0..ONEDAY-1, we keep |
| * the start and end ranges the same for consistency. |
| */ |
| void |
| SimpleTimeZone::decodeStartRule(UErrorCode& status) |
| { |
| if(U_FAILURE(status)) return; |
| |
| useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); |
| if (useDaylight && dstSavings == 0) { |
| dstSavings = U_MILLIS_PER_HOUR; |
| } |
| if (startDay != 0) { |
| if (startMonth < Calendar::JANUARY || startMonth > Calendar::DECEMBER) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| if (startTime < 0 || startTime > U_MILLIS_PER_DAY || |
| startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| if (startDayOfWeek == 0) { |
| startMode = DOM_MODE; |
| } else { |
| if (startDayOfWeek > 0) { |
| startMode = DOW_IN_MONTH_MODE; |
| } else { |
| startDayOfWeek = (int8_t)-startDayOfWeek; |
| if (startDay > 0) { |
| startMode = DOW_GE_DOM_MODE; |
| } else { |
| startDay = (int8_t)-startDay; |
| startMode = DOW_LE_DOM_MODE; |
| } |
| } |
| if (startDayOfWeek > Calendar::SATURDAY) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } |
| if (startMode == DOW_IN_MONTH_MODE) { |
| if (startDay < -5 || startDay > 5) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } else if (startDay > staticMonthLength[startMonth]) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Decode the end rule and validate the parameters. This method is exactly |
| * analogous to decodeStartRule(). |
| * @see decodeStartRule |
| */ |
| void |
| SimpleTimeZone::decodeEndRule(UErrorCode& status) |
| { |
| if(U_FAILURE(status)) return; |
| |
| useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); |
| if (useDaylight && dstSavings == 0) { |
| dstSavings = U_MILLIS_PER_HOUR; |
| } |
| if (endDay != 0) { |
| if (endMonth < Calendar::JANUARY || endMonth > Calendar::DECEMBER) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| if (endTime < 0 || endTime > U_MILLIS_PER_DAY || |
| endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| if (endDayOfWeek == 0) { |
| endMode = DOM_MODE; |
| } else { |
| if (endDayOfWeek > 0) { |
| endMode = DOW_IN_MONTH_MODE; |
| } else { |
| endDayOfWeek = (int8_t)-endDayOfWeek; |
| if (endDay > 0) { |
| endMode = DOW_GE_DOM_MODE; |
| } else { |
| endDay = (int8_t)-endDay; |
| endMode = DOW_LE_DOM_MODE; |
| } |
| } |
| if (endDayOfWeek > Calendar::SATURDAY) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } |
| if (endMode == DOW_IN_MONTH_MODE) { |
| if (endDay < -5 || endDay > 5) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } else if (endDay > staticMonthLength[endMonth]) { |
| status = U_ILLEGAL_ARGUMENT_ERROR; |
| return; |
| } |
| } |
| } |
| |
| U_NAMESPACE_END |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |
| |
| //eof |