1266 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1266 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 2016 and later: Unicode, Inc. and others.
 | |
| // License & terms of use: http://www.unicode.org/copyright.html
 | |
| /*
 | |
|  *******************************************************************************
 | |
|  * 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"
 | |
| 
 | |
| #if !UCONFIG_NO_FORMATTING
 | |
| 
 | |
| #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)
 | |
| 
 | |
| // Use only for decodeStartRule() and decodeEndRule() where the year is not
 | |
| // available. Set February to 29 days to accommodate 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};
 | |
| 
 | |
| 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)
 | |
| :   BasicTimeZone(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)
 | |
| {
 | |
|     clearTransitionRules();
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| 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)
 | |
| :   BasicTimeZone(ID)
 | |
| {
 | |
|     clearTransitionRules();
 | |
|     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)
 | |
| :   BasicTimeZone(ID)
 | |
| {
 | |
|     clearTransitionRules();
 | |
|     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)
 | |
| :   BasicTimeZone(ID)
 | |
| {
 | |
|     clearTransitionRules();
 | |
|     construct(rawOffsetGMT,
 | |
|               savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
 | |
|               savingsStartTime, savingsStartTimeMode,
 | |
|               savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
 | |
|               savingsEndTime, savingsEndTimeMode,
 | |
|               savingsDST, 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()
 | |
| {
 | |
|     deleteTransitionRules();
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
 | |
| SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
 | |
| :   BasicTimeZone(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;
 | |
|         clearTransitionRules();
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| bool
 | |
| SimpleTimeZone::operator==(const TimeZone& that) const
 | |
| {
 | |
|     return ((this == &that) ||
 | |
|             (typeid(*this) == typeid(that) &&
 | |
|             TimeZone::operator==(that) &&
 | |
|             hasSameRules(that)));
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| // Called by TimeZone::createDefault() inside a Mutex - be careful.
 | |
| SimpleTimeZone*
 | |
| 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;
 | |
|     transitionRulesInitialized = FALSE;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| /**
 | |
|  * 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 specified 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);
 | |
|     transitionRulesInitialized = FALSE;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| 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);
 | |
|     transitionRulesInitialized = FALSE;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| 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 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
 | |
|     // 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 < UCAL_JANUARY || month > UCAL_DECEMBER) {
 | |
|         status = U_ILLEGAL_ARGUMENT_ERROR;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     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 /*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
 | |
|     // 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 < UCAL_JANUARY
 | |
|         || month > UCAL_DECEMBER) {
 | |
|         status = U_ILLEGAL_ARGUMENT_ERROR;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     // 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,
 | |
|                      Grego::monthLength(year, month),
 | |
|                      Grego::previousMonthLength(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 monthLength, int32_t prevMonthLength,
 | |
|                           UErrorCode& status) const
 | |
| {
 | |
|     if(U_FAILURE(status)) return 0;
 | |
| 
 | |
|     if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
 | |
|         || month < UCAL_JANUARY
 | |
|         || month > UCAL_DECEMBER
 | |
|         || day < 1
 | |
|         || day > monthLength
 | |
|         || dayOfWeek < UCAL_SUNDAY
 | |
|         || dayOfWeek > UCAL_SATURDAY
 | |
|         || millis < 0
 | |
|         || millis >= U_MILLIS_PER_DAY
 | |
|         || monthLength < 28
 | |
|         || monthLength > 31
 | |
|         || prevMonthLength < 28
 | |
|         || prevMonthLength > 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;
 | |
| }
 | |
| 
 | |
| void
 | |
| SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
 | |
|                                    UTimeZoneLocalOption 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;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| /**
 | |
|  * 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 desirable 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;
 | |
| 
 | |
|     // 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
 | |
|     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.
 | |
|         else
 | |
|         {
 | |
|             // (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 -
 | |
|                 (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;
 | |
|     transitionRulesInitialized = FALSE;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| void 
 | |
| SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) 
 | |
| {
 | |
|     if (millisSavedDuringDST == 0) {
 | |
|         status = U_ILLEGAL_ARGUMENT_ERROR;
 | |
|     }
 | |
|     else {
 | |
|         dstSavings = millisSavedDuringDST;
 | |
|     }
 | |
|     transitionRulesInitialized = FALSE;
 | |
| }
 | |
| 
 | |
| // -------------------------------------
 | |
| 
 | |
| 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 (typeid(*this) != typeid(other)) 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 < UCAL_JANUARY || startMonth > UCAL_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 > UCAL_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<1 || 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 < UCAL_JANUARY || endMonth > UCAL_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 > UCAL_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<1 || endDay > STATICMONTHLENGTH[endMonth]) {
 | |
|             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;
 | |
|     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 */
 | |
| 
 | |
| //eof
 |