1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/simpletz.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1251 @@ 1.4 +/* 1.5 + ******************************************************************************* 1.6 + * Copyright (C) 1997-2013, International Business Machines Corporation and 1.7 + * others. All Rights Reserved. 1.8 + ******************************************************************************* 1.9 + * 1.10 + * File SIMPLETZ.H 1.11 + * 1.12 + * Modification History: 1.13 + * 1.14 + * Date Name Description 1.15 + * 12/05/96 clhuang Creation. 1.16 + * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and 1.17 + * testing. 1.18 + * 07/29/97 aliu Ported source bodies back from Java version with 1.19 + * numerous feature enhancements and bug fixes. 1.20 + * 08/10/98 stephen JDK 1.2 sync. 1.21 + * 09/17/98 stephen Fixed getOffset() for last hour of year and DST 1.22 + * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule 1.23 + * methods that take TimeMode. Whitespace cleanup. 1.24 + ******************************************************************************** 1.25 + */ 1.26 + 1.27 +#include "utypeinfo.h" // for 'typeid' to work 1.28 + 1.29 +#include "unicode/utypes.h" 1.30 + 1.31 +#if !UCONFIG_NO_FORMATTING 1.32 + 1.33 +#include "unicode/simpletz.h" 1.34 +#include "unicode/gregocal.h" 1.35 +#include "unicode/smpdtfmt.h" 1.36 + 1.37 +#include "gregoimp.h" 1.38 +#include "umutex.h" 1.39 + 1.40 +U_NAMESPACE_BEGIN 1.41 + 1.42 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone) 1.43 + 1.44 +// Use only for decodeStartRule() and decodeEndRule() where the year is not 1.45 +// available. Set February to 29 days to accomodate rules with that date 1.46 +// and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE). 1.47 +// The compareToRule() method adjusts to February 28 in non-leap years. 1.48 +// 1.49 +// For actual getOffset() calculations, use Grego::monthLength() and 1.50 +// Grego::previousMonthLength() which take leap years into account. 1.51 +// We handle leap years assuming always 1.52 +// Gregorian, since we know they didn't have daylight time when 1.53 +// Gregorian calendar started. 1.54 +const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 1.55 + 1.56 +static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)" 1.57 +static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)" 1.58 + 1.59 + 1.60 +// ***************************************************************************** 1.61 +// class SimpleTimeZone 1.62 +// ***************************************************************************** 1.63 + 1.64 + 1.65 +SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID) 1.66 +: BasicTimeZone(ID), 1.67 + startMonth(0), 1.68 + startDay(0), 1.69 + startDayOfWeek(0), 1.70 + startTime(0), 1.71 + startTimeMode(WALL_TIME), 1.72 + endTimeMode(WALL_TIME), 1.73 + endMonth(0), 1.74 + endDay(0), 1.75 + endDayOfWeek(0), 1.76 + endTime(0), 1.77 + startYear(0), 1.78 + rawOffset(rawOffsetGMT), 1.79 + useDaylight(FALSE), 1.80 + startMode(DOM_MODE), 1.81 + endMode(DOM_MODE), 1.82 + dstSavings(U_MILLIS_PER_HOUR) 1.83 +{ 1.84 + clearTransitionRules(); 1.85 +} 1.86 + 1.87 +// ------------------------------------- 1.88 + 1.89 +SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, 1.90 + int8_t savingsStartMonth, int8_t savingsStartDay, 1.91 + int8_t savingsStartDayOfWeek, int32_t savingsStartTime, 1.92 + int8_t savingsEndMonth, int8_t savingsEndDay, 1.93 + int8_t savingsEndDayOfWeek, int32_t savingsEndTime, 1.94 + UErrorCode& status) 1.95 +: BasicTimeZone(ID) 1.96 +{ 1.97 + clearTransitionRules(); 1.98 + construct(rawOffsetGMT, 1.99 + savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, 1.100 + savingsStartTime, WALL_TIME, 1.101 + savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, 1.102 + savingsEndTime, WALL_TIME, 1.103 + U_MILLIS_PER_HOUR, status); 1.104 +} 1.105 + 1.106 +// ------------------------------------- 1.107 + 1.108 +SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, 1.109 + int8_t savingsStartMonth, int8_t savingsStartDay, 1.110 + int8_t savingsStartDayOfWeek, int32_t savingsStartTime, 1.111 + int8_t savingsEndMonth, int8_t savingsEndDay, 1.112 + int8_t savingsEndDayOfWeek, int32_t savingsEndTime, 1.113 + int32_t savingsDST, UErrorCode& status) 1.114 +: BasicTimeZone(ID) 1.115 +{ 1.116 + clearTransitionRules(); 1.117 + construct(rawOffsetGMT, 1.118 + savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, 1.119 + savingsStartTime, WALL_TIME, 1.120 + savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, 1.121 + savingsEndTime, WALL_TIME, 1.122 + savingsDST, status); 1.123 +} 1.124 + 1.125 +// ------------------------------------- 1.126 + 1.127 +SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, 1.128 + int8_t savingsStartMonth, int8_t savingsStartDay, 1.129 + int8_t savingsStartDayOfWeek, int32_t savingsStartTime, 1.130 + TimeMode savingsStartTimeMode, 1.131 + int8_t savingsEndMonth, int8_t savingsEndDay, 1.132 + int8_t savingsEndDayOfWeek, int32_t savingsEndTime, 1.133 + TimeMode savingsEndTimeMode, 1.134 + int32_t savingsDST, UErrorCode& status) 1.135 +: BasicTimeZone(ID) 1.136 +{ 1.137 + clearTransitionRules(); 1.138 + construct(rawOffsetGMT, 1.139 + savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, 1.140 + savingsStartTime, savingsStartTimeMode, 1.141 + savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, 1.142 + savingsEndTime, savingsEndTimeMode, 1.143 + savingsDST, status); 1.144 +} 1.145 + 1.146 +/** 1.147 + * Internal construction method. 1.148 + */ 1.149 +void SimpleTimeZone::construct(int32_t rawOffsetGMT, 1.150 + int8_t savingsStartMonth, 1.151 + int8_t savingsStartDay, 1.152 + int8_t savingsStartDayOfWeek, 1.153 + int32_t savingsStartTime, 1.154 + TimeMode savingsStartTimeMode, 1.155 + int8_t savingsEndMonth, 1.156 + int8_t savingsEndDay, 1.157 + int8_t savingsEndDayOfWeek, 1.158 + int32_t savingsEndTime, 1.159 + TimeMode savingsEndTimeMode, 1.160 + int32_t savingsDST, 1.161 + UErrorCode& status) 1.162 +{ 1.163 + this->rawOffset = rawOffsetGMT; 1.164 + this->startMonth = savingsStartMonth; 1.165 + this->startDay = savingsStartDay; 1.166 + this->startDayOfWeek = savingsStartDayOfWeek; 1.167 + this->startTime = savingsStartTime; 1.168 + this->startTimeMode = savingsStartTimeMode; 1.169 + this->endMonth = savingsEndMonth; 1.170 + this->endDay = savingsEndDay; 1.171 + this->endDayOfWeek = savingsEndDayOfWeek; 1.172 + this->endTime = savingsEndTime; 1.173 + this->endTimeMode = savingsEndTimeMode; 1.174 + this->dstSavings = savingsDST; 1.175 + this->startYear = 0; 1.176 + this->startMode = DOM_MODE; 1.177 + this->endMode = DOM_MODE; 1.178 + 1.179 + decodeRules(status); 1.180 + 1.181 + if (savingsDST <= 0) { 1.182 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.183 + } 1.184 +} 1.185 + 1.186 +// ------------------------------------- 1.187 + 1.188 +SimpleTimeZone::~SimpleTimeZone() 1.189 +{ 1.190 + deleteTransitionRules(); 1.191 +} 1.192 + 1.193 +// ------------------------------------- 1.194 + 1.195 +// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. 1.196 +SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source) 1.197 +: BasicTimeZone(source) 1.198 +{ 1.199 + *this = source; 1.200 +} 1.201 + 1.202 +// ------------------------------------- 1.203 + 1.204 +// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. 1.205 +SimpleTimeZone & 1.206 +SimpleTimeZone::operator=(const SimpleTimeZone &right) 1.207 +{ 1.208 + if (this != &right) 1.209 + { 1.210 + TimeZone::operator=(right); 1.211 + rawOffset = right.rawOffset; 1.212 + startMonth = right.startMonth; 1.213 + startDay = right.startDay; 1.214 + startDayOfWeek = right.startDayOfWeek; 1.215 + startTime = right.startTime; 1.216 + startTimeMode = right.startTimeMode; 1.217 + startMode = right.startMode; 1.218 + endMonth = right.endMonth; 1.219 + endDay = right.endDay; 1.220 + endDayOfWeek = right.endDayOfWeek; 1.221 + endTime = right.endTime; 1.222 + endTimeMode = right.endTimeMode; 1.223 + endMode = right.endMode; 1.224 + startYear = right.startYear; 1.225 + dstSavings = right.dstSavings; 1.226 + useDaylight = right.useDaylight; 1.227 + clearTransitionRules(); 1.228 + } 1.229 + return *this; 1.230 +} 1.231 + 1.232 +// ------------------------------------- 1.233 + 1.234 +UBool 1.235 +SimpleTimeZone::operator==(const TimeZone& that) const 1.236 +{ 1.237 + return ((this == &that) || 1.238 + (typeid(*this) == typeid(that) && 1.239 + TimeZone::operator==(that) && 1.240 + hasSameRules(that))); 1.241 +} 1.242 + 1.243 +// ------------------------------------- 1.244 + 1.245 +// Called by TimeZone::createDefault() inside a Mutex - be careful. 1.246 +TimeZone* 1.247 +SimpleTimeZone::clone() const 1.248 +{ 1.249 + return new SimpleTimeZone(*this); 1.250 +} 1.251 + 1.252 +// ------------------------------------- 1.253 + 1.254 +/** 1.255 + * Sets the daylight savings starting year, that is, the year this time zone began 1.256 + * observing its specified daylight savings time rules. The time zone is considered 1.257 + * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't 1.258 + * support historical daylight-savings-time rules. 1.259 + * @param year the daylight savings starting year. 1.260 + */ 1.261 +void 1.262 +SimpleTimeZone::setStartYear(int32_t year) 1.263 +{ 1.264 + startYear = year; 1.265 + transitionRulesInitialized = FALSE; 1.266 +} 1.267 + 1.268 +// ------------------------------------- 1.269 + 1.270 +/** 1.271 + * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings 1.272 + * Time starts at the first Sunday in April, at 2 AM in standard time. 1.273 + * Therefore, you can set the start rule by calling: 1.274 + * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000); 1.275 + * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate 1.276 + * the exact starting date. Their exact meaning depend on their respective signs, 1.277 + * allowing various types of rules to be constructed, as follows:<ul> 1.278 + * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the 1.279 + * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday 1.280 + * of the month). 1.281 + * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify 1.282 + * the day of week in the month counting backward from the end of the month. 1.283 + * (e.g., (-1, MONDAY) is the last Monday in the month) 1.284 + * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth 1.285 + * specifies the day of the month, regardless of what day of the week it is. 1.286 + * (e.g., (10, 0) is the tenth day of the month) 1.287 + * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth 1.288 + * specifies the day of the month counting backward from the end of the 1.289 + * month, regardless of what day of the week it is (e.g., (-2, 0) is the 1.290 + * next-to-last day of the month). 1.291 + * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the 1.292 + * first specified day of the week on or after the specfied day of the month. 1.293 + * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month 1.294 + * [or the 15th itself if the 15th is a Sunday].) 1.295 + * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the 1.296 + * last specified day of the week on or before the specified day of the month. 1.297 + * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month 1.298 + * [or the 20th itself if the 20th is a Tuesday].)</ul> 1.299 + * @param month the daylight savings starting month. Month is 0-based. 1.300 + * eg, 0 for January. 1.301 + * @param dayOfWeekInMonth the daylight savings starting 1.302 + * day-of-week-in-month. Please see the member description for an example. 1.303 + * @param dayOfWeek the daylight savings starting day-of-week. Please see 1.304 + * the member description for an example. 1.305 + * @param time the daylight savings starting time. Please see the member 1.306 + * description for an example. 1.307 + */ 1.308 + 1.309 +void 1.310 +SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, 1.311 + int32_t time, TimeMode mode, UErrorCode& status) 1.312 +{ 1.313 + startMonth = (int8_t)month; 1.314 + startDay = (int8_t)dayOfWeekInMonth; 1.315 + startDayOfWeek = (int8_t)dayOfWeek; 1.316 + startTime = time; 1.317 + startTimeMode = mode; 1.318 + decodeStartRule(status); 1.319 + transitionRulesInitialized = FALSE; 1.320 +} 1.321 + 1.322 +// ------------------------------------- 1.323 + 1.324 +void 1.325 +SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, 1.326 + int32_t time, TimeMode mode, UErrorCode& status) 1.327 +{ 1.328 + setStartRule(month, dayOfMonth, 0, time, mode, status); 1.329 +} 1.330 + 1.331 +// ------------------------------------- 1.332 + 1.333 +void 1.334 +SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, 1.335 + int32_t time, TimeMode mode, UBool after, UErrorCode& status) 1.336 +{ 1.337 + setStartRule(month, after ? dayOfMonth : -dayOfMonth, 1.338 + -dayOfWeek, time, mode, status); 1.339 +} 1.340 + 1.341 +// ------------------------------------- 1.342 + 1.343 +/** 1.344 + * Sets the daylight savings ending rule. For example, in the U.S., Daylight 1.345 + * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time. 1.346 + * Therefore, you can set the end rule by calling: 1.347 + * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000); 1.348 + * Various other types of rules can be specified by manipulating the dayOfWeek 1.349 + * and dayOfWeekInMonth parameters. For complete details, see the documentation 1.350 + * for setStartRule(). 1.351 + * @param month the daylight savings ending month. Month is 0-based. 1.352 + * eg, 0 for January. 1.353 + * @param dayOfWeekInMonth the daylight savings ending 1.354 + * day-of-week-in-month. See setStartRule() for a complete explanation. 1.355 + * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule() 1.356 + * for a complete explanation. 1.357 + * @param time the daylight savings ending time. Please see the member 1.358 + * description for an example. 1.359 + */ 1.360 + 1.361 +void 1.362 +SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, 1.363 + int32_t time, TimeMode mode, UErrorCode& status) 1.364 +{ 1.365 + endMonth = (int8_t)month; 1.366 + endDay = (int8_t)dayOfWeekInMonth; 1.367 + endDayOfWeek = (int8_t)dayOfWeek; 1.368 + endTime = time; 1.369 + endTimeMode = mode; 1.370 + decodeEndRule(status); 1.371 + transitionRulesInitialized = FALSE; 1.372 +} 1.373 + 1.374 +// ------------------------------------- 1.375 + 1.376 +void 1.377 +SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, 1.378 + int32_t time, TimeMode mode, UErrorCode& status) 1.379 +{ 1.380 + setEndRule(month, dayOfMonth, 0, time, mode, status); 1.381 +} 1.382 + 1.383 +// ------------------------------------- 1.384 + 1.385 +void 1.386 +SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, 1.387 + int32_t time, TimeMode mode, UBool after, UErrorCode& status) 1.388 +{ 1.389 + setEndRule(month, after ? dayOfMonth : -dayOfMonth, 1.390 + -dayOfWeek, time, mode, status); 1.391 +} 1.392 + 1.393 +// ------------------------------------- 1.394 + 1.395 +int32_t 1.396 +SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, 1.397 + uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const 1.398 +{ 1.399 + // Check the month before calling Grego::monthLength(). This 1.400 + // duplicates the test that occurs in the 7-argument getOffset(), 1.401 + // however, this is unavoidable. We don't mind because this method, in 1.402 + // fact, should not be called; internal code should always call the 1.403 + // 7-argument getOffset(), and outside code should use Calendar.get(int 1.404 + // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of 1.405 + // this method because it's public API. - liu 8/10/98 1.406 + if(month < UCAL_JANUARY || month > UCAL_DECEMBER) { 1.407 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.408 + return 0; 1.409 + } 1.410 + 1.411 + return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status); 1.412 +} 1.413 + 1.414 +int32_t 1.415 +SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, 1.416 + uint8_t dayOfWeek, int32_t millis, 1.417 + int32_t /*monthLength*/, UErrorCode& status) const 1.418 +{ 1.419 + // Check the month before calling Grego::monthLength(). This 1.420 + // duplicates a test that occurs in the 9-argument getOffset(), 1.421 + // however, this is unavoidable. We don't mind because this method, in 1.422 + // fact, should not be called; internal code should always call the 1.423 + // 9-argument getOffset(), and outside code should use Calendar.get(int 1.424 + // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of 1.425 + // this method because it's public API. - liu 8/10/98 1.426 + if (month < UCAL_JANUARY 1.427 + || month > UCAL_DECEMBER) { 1.428 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.429 + return -1; 1.430 + } 1.431 + 1.432 + // We ignore monthLength because it can be derived from year and month. 1.433 + // This is so that February in leap years is calculated correctly. 1.434 + // We keep this argument in this function for backwards compatibility. 1.435 + return getOffset(era, year, month, day, dayOfWeek, millis, 1.436 + Grego::monthLength(year, month), 1.437 + Grego::previousMonthLength(year, month), 1.438 + status); 1.439 +} 1.440 + 1.441 +int32_t 1.442 +SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, 1.443 + uint8_t dayOfWeek, int32_t millis, 1.444 + int32_t monthLength, int32_t prevMonthLength, 1.445 + UErrorCode& status) const 1.446 +{ 1.447 + if(U_FAILURE(status)) return 0; 1.448 + 1.449 + if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) 1.450 + || month < UCAL_JANUARY 1.451 + || month > UCAL_DECEMBER 1.452 + || day < 1 1.453 + || day > monthLength 1.454 + || dayOfWeek < UCAL_SUNDAY 1.455 + || dayOfWeek > UCAL_SATURDAY 1.456 + || millis < 0 1.457 + || millis >= U_MILLIS_PER_DAY 1.458 + || monthLength < 28 1.459 + || monthLength > 31 1.460 + || prevMonthLength < 28 1.461 + || prevMonthLength > 31) { 1.462 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.463 + return -1; 1.464 + } 1.465 + 1.466 + int32_t result = rawOffset; 1.467 + 1.468 + // Bail out if we are before the onset of daylight savings time 1.469 + if(!useDaylight || year < startYear || era != GregorianCalendar::AD) 1.470 + return result; 1.471 + 1.472 + // Check for southern hemisphere. We assume that the start and end 1.473 + // month are different. 1.474 + UBool southern = (startMonth > endMonth); 1.475 + 1.476 + // Compare the date to the starting and ending rules.+1 = date>rule, -1 1.477 + // = date<rule, 0 = date==rule. 1.478 + int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, 1.479 + (int8_t)day, (int8_t)dayOfWeek, millis, 1.480 + startTimeMode == UTC_TIME ? -rawOffset : 0, 1.481 + startMode, (int8_t)startMonth, (int8_t)startDayOfWeek, 1.482 + (int8_t)startDay, startTime); 1.483 + int32_t endCompare = 0; 1.484 + 1.485 + /* We don't always have to compute endCompare. For many instances, 1.486 + * startCompare is enough to determine if we are in DST or not. In the 1.487 + * northern hemisphere, if we are before the start rule, we can't have 1.488 + * DST. In the southern hemisphere, if we are after the start rule, we 1.489 + * must have DST. This is reflected in the way the next if statement 1.490 + * (not the one immediately following) short circuits. */ 1.491 + if(southern != (startCompare >= 0)) { 1.492 + endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, 1.493 + (int8_t)day, (int8_t)dayOfWeek, millis, 1.494 + endTimeMode == WALL_TIME ? dstSavings : 1.495 + (endTimeMode == UTC_TIME ? -rawOffset : 0), 1.496 + endMode, (int8_t)endMonth, (int8_t)endDayOfWeek, 1.497 + (int8_t)endDay, endTime); 1.498 + } 1.499 + 1.500 + // Check for both the northern and southern hemisphere cases. We 1.501 + // assume that in the northern hemisphere, the start rule is before the 1.502 + // end rule within the calendar year, and vice versa for the southern 1.503 + // hemisphere. 1.504 + if ((!southern && (startCompare >= 0 && endCompare < 0)) || 1.505 + (southern && (startCompare >= 0 || endCompare < 0))) 1.506 + result += dstSavings; 1.507 + 1.508 + return result; 1.509 +} 1.510 + 1.511 +void 1.512 +SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, 1.513 + int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const { 1.514 + if (U_FAILURE(status)) { 1.515 + return; 1.516 + } 1.517 + 1.518 + rawOffsetGMT = getRawOffset(); 1.519 + int32_t year, month, dom, dow; 1.520 + double day = uprv_floor(date / U_MILLIS_PER_DAY); 1.521 + int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); 1.522 + 1.523 + Grego::dayToFields(day, year, month, dom, dow); 1.524 + 1.525 + savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, 1.526 + (uint8_t) dow, millis, 1.527 + Grego::monthLength(year, month), 1.528 + status) - rawOffsetGMT; 1.529 + if (U_FAILURE(status)) { 1.530 + return; 1.531 + } 1.532 + 1.533 + UBool recalc = FALSE; 1.534 + 1.535 + // Now we need some adjustment 1.536 + if (savingsDST > 0) { 1.537 + if ((nonExistingTimeOpt & kStdDstMask) == kStandard 1.538 + || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) { 1.539 + date -= getDSTSavings(); 1.540 + recalc = TRUE; 1.541 + } 1.542 + } else { 1.543 + if ((duplicatedTimeOpt & kStdDstMask) == kDaylight 1.544 + || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) { 1.545 + date -= getDSTSavings(); 1.546 + recalc = TRUE; 1.547 + } 1.548 + } 1.549 + if (recalc) { 1.550 + day = uprv_floor(date / U_MILLIS_PER_DAY); 1.551 + millis = (int32_t) (date - day * U_MILLIS_PER_DAY); 1.552 + Grego::dayToFields(day, year, month, dom, dow); 1.553 + savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, 1.554 + (uint8_t) dow, millis, 1.555 + Grego::monthLength(year, month), 1.556 + status) - rawOffsetGMT; 1.557 + } 1.558 +} 1.559 + 1.560 +// ------------------------------------- 1.561 + 1.562 +/** 1.563 + * Compare a given date in the year to a rule. Return 1, 0, or -1, depending 1.564 + * on whether the date is after, equal to, or before the rule date. The 1.565 + * millis are compared directly against the ruleMillis, so any 1.566 + * standard-daylight adjustments must be handled by the caller. 1.567 + * 1.568 + * @return 1 if the date is after the rule date, -1 if the date is before 1.569 + * the rule date, or 0 if the date is equal to the rule date. 1.570 + */ 1.571 +int32_t 1.572 +SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen, 1.573 + int8_t dayOfMonth, 1.574 + int8_t dayOfWeek, int32_t millis, int32_t millisDelta, 1.575 + EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek, 1.576 + int8_t ruleDay, int32_t ruleMillis) 1.577 +{ 1.578 + // Make adjustments for startTimeMode and endTimeMode 1.579 + millis += millisDelta; 1.580 + while (millis >= U_MILLIS_PER_DAY) { 1.581 + millis -= U_MILLIS_PER_DAY; 1.582 + ++dayOfMonth; 1.583 + dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based 1.584 + if (dayOfMonth > monthLen) { 1.585 + dayOfMonth = 1; 1.586 + /* When incrementing the month, it is desirible to overflow 1.587 + * from DECEMBER to DECEMBER+1, since we use the result to 1.588 + * compare against a real month. Wraparound of the value 1.589 + * leads to bug 4173604. */ 1.590 + ++month; 1.591 + } 1.592 + } 1.593 + while (millis < 0) { 1.594 + millis += U_MILLIS_PER_DAY; 1.595 + --dayOfMonth; 1.596 + dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based 1.597 + if (dayOfMonth < 1) { 1.598 + dayOfMonth = prevMonthLen; 1.599 + --month; 1.600 + } 1.601 + } 1.602 + 1.603 + // first compare months. If they're different, we don't have to worry about days 1.604 + // and times 1.605 + if (month < ruleMonth) return -1; 1.606 + else if (month > ruleMonth) return 1; 1.607 + 1.608 + // calculate the actual day of month for the rule 1.609 + int32_t ruleDayOfMonth = 0; 1.610 + 1.611 + // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days. 1.612 + if (ruleDay > monthLen) { 1.613 + ruleDay = monthLen; 1.614 + } 1.615 + 1.616 + switch (ruleMode) 1.617 + { 1.618 + // if the mode is day-of-month, the day of month is given 1.619 + case DOM_MODE: 1.620 + ruleDayOfMonth = ruleDay; 1.621 + break; 1.622 + 1.623 + // if the mode is day-of-week-in-month, calculate the day-of-month from it 1.624 + case DOW_IN_MONTH_MODE: 1.625 + // In this case ruleDay is the day-of-week-in-month (this code is using 1.626 + // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week 1.627 + // of the first day of the month, so it's trusting that they're really 1.628 + // consistent with each other) 1.629 + if (ruleDay > 0) 1.630 + ruleDayOfMonth = 1 + (ruleDay - 1) * 7 + 1.631 + (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7; 1.632 + 1.633 + // if ruleDay is negative (we assume it's not zero here), we have to do 1.634 + // the same calculation figuring backward from the last day of the month. 1.635 + else 1.636 + { 1.637 + // (again, this code is trusting that dayOfWeek and dayOfMonth are 1.638 + // consistent with each other here, since we're using them to figure 1.639 + // the day of week of the first of the month) 1.640 + ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 - 1.641 + (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7; 1.642 + } 1.643 + break; 1.644 + 1.645 + case DOW_GE_DOM_MODE: 1.646 + ruleDayOfMonth = ruleDay + 1.647 + (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7; 1.648 + break; 1.649 + 1.650 + case DOW_LE_DOM_MODE: 1.651 + ruleDayOfMonth = ruleDay - 1.652 + (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7; 1.653 + // Note at this point ruleDayOfMonth may be <1, although it will 1.654 + // be >=1 for well-formed rules. 1.655 + break; 1.656 + } 1.657 + 1.658 + // now that we have a real day-in-month for the rule, we can compare days... 1.659 + if (dayOfMonth < ruleDayOfMonth) return -1; 1.660 + else if (dayOfMonth > ruleDayOfMonth) return 1; 1.661 + 1.662 + // ...and if they're equal, we compare times 1.663 + if (millis < ruleMillis) return -1; 1.664 + else if (millis > ruleMillis) return 1; 1.665 + else return 0; 1.666 +} 1.667 + 1.668 +// ------------------------------------- 1.669 + 1.670 +int32_t 1.671 +SimpleTimeZone::getRawOffset() const 1.672 +{ 1.673 + return rawOffset; 1.674 +} 1.675 + 1.676 +// ------------------------------------- 1.677 + 1.678 +void 1.679 +SimpleTimeZone::setRawOffset(int32_t offsetMillis) 1.680 +{ 1.681 + rawOffset = offsetMillis; 1.682 + transitionRulesInitialized = FALSE; 1.683 +} 1.684 + 1.685 +// ------------------------------------- 1.686 + 1.687 +void 1.688 +SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) 1.689 +{ 1.690 + if (millisSavedDuringDST <= 0) { 1.691 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.692 + } 1.693 + else { 1.694 + dstSavings = millisSavedDuringDST; 1.695 + } 1.696 + transitionRulesInitialized = FALSE; 1.697 +} 1.698 + 1.699 +// ------------------------------------- 1.700 + 1.701 +int32_t 1.702 +SimpleTimeZone::getDSTSavings() const 1.703 +{ 1.704 + return dstSavings; 1.705 +} 1.706 + 1.707 +// ------------------------------------- 1.708 + 1.709 +UBool 1.710 +SimpleTimeZone::useDaylightTime() const 1.711 +{ 1.712 + return useDaylight; 1.713 +} 1.714 + 1.715 +// ------------------------------------- 1.716 + 1.717 +/** 1.718 + * Overrides TimeZone 1.719 + * Queries if the given date is in Daylight Savings Time. 1.720 + */ 1.721 +UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const 1.722 +{ 1.723 + // This method is wasteful since it creates a new GregorianCalendar and 1.724 + // deletes it each time it is called. However, this is a deprecated method 1.725 + // and provided only for Java compatibility as of 8/6/97 [LIU]. 1.726 + if (U_FAILURE(status)) return FALSE; 1.727 + GregorianCalendar *gc = new GregorianCalendar(*this, status); 1.728 + /* test for NULL */ 1.729 + if (gc == 0) { 1.730 + status = U_MEMORY_ALLOCATION_ERROR; 1.731 + return FALSE; 1.732 + } 1.733 + gc->setTime(date, status); 1.734 + UBool result = gc->inDaylightTime(status); 1.735 + delete gc; 1.736 + return result; 1.737 +} 1.738 + 1.739 +// ------------------------------------- 1.740 + 1.741 +/** 1.742 + * Return true if this zone has the same rules and offset as another zone. 1.743 + * @param other the TimeZone object to be compared with 1.744 + * @return true if the given zone has the same rules and offset as this one 1.745 + */ 1.746 +UBool 1.747 +SimpleTimeZone::hasSameRules(const TimeZone& other) const 1.748 +{ 1.749 + if (this == &other) return TRUE; 1.750 + if (typeid(*this) != typeid(other)) return FALSE; 1.751 + SimpleTimeZone *that = (SimpleTimeZone*)&other; 1.752 + return rawOffset == that->rawOffset && 1.753 + useDaylight == that->useDaylight && 1.754 + (!useDaylight 1.755 + // Only check rules if using DST 1.756 + || (dstSavings == that->dstSavings && 1.757 + startMode == that->startMode && 1.758 + startMonth == that->startMonth && 1.759 + startDay == that->startDay && 1.760 + startDayOfWeek == that->startDayOfWeek && 1.761 + startTime == that->startTime && 1.762 + startTimeMode == that->startTimeMode && 1.763 + endMode == that->endMode && 1.764 + endMonth == that->endMonth && 1.765 + endDay == that->endDay && 1.766 + endDayOfWeek == that->endDayOfWeek && 1.767 + endTime == that->endTime && 1.768 + endTimeMode == that->endTimeMode && 1.769 + startYear == that->startYear)); 1.770 +} 1.771 + 1.772 +// ------------------------------------- 1.773 + 1.774 +//---------------------------------------------------------------------- 1.775 +// Rule representation 1.776 +// 1.777 +// We represent the following flavors of rules: 1.778 +// 5 the fifth of the month 1.779 +// lastSun the last Sunday in the month 1.780 +// lastMon the last Monday in the month 1.781 +// Sun>=8 first Sunday on or after the eighth 1.782 +// Sun<=25 last Sunday on or before the 25th 1.783 +// This is further complicated by the fact that we need to remain 1.784 +// backward compatible with the 1.1 FCS. Finally, we need to minimize 1.785 +// API changes. In order to satisfy these requirements, we support 1.786 +// three representation systems, and we translate between them. 1.787 +// 1.788 +// INTERNAL REPRESENTATION 1.789 +// This is the format SimpleTimeZone objects take after construction or 1.790 +// streaming in is complete. Rules are represented directly, using an 1.791 +// unencoded format. We will discuss the start rule only below; the end 1.792 +// rule is analogous. 1.793 +// startMode Takes on enumerated values DAY_OF_MONTH, 1.794 +// DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM. 1.795 +// startDay The day of the month, or for DOW_IN_MONTH mode, a 1.796 +// value indicating which DOW, such as +1 for first, 1.797 +// +2 for second, -1 for last, etc. 1.798 +// startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH. 1.799 +// 1.800 +// ENCODED REPRESENTATION 1.801 +// This is the format accepted by the constructor and by setStartRule() 1.802 +// and setEndRule(). It uses various combinations of positive, negative, 1.803 +// and zero values to encode the different rules. This representation 1.804 +// allows us to specify all the different rule flavors without altering 1.805 +// the API. 1.806 +// MODE startMonth startDay startDayOfWeek 1.807 +// DOW_IN_MONTH_MODE >=0 !=0 >0 1.808 +// DOM_MODE >=0 >0 ==0 1.809 +// DOW_GE_DOM_MODE >=0 >0 <0 1.810 +// DOW_LE_DOM_MODE >=0 <0 <0 1.811 +// (no DST) don't care ==0 don't care 1.812 +// 1.813 +// STREAMED REPRESENTATION 1.814 +// We must retain binary compatibility with the 1.1 FCS. The 1.1 code only 1.815 +// handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the 1.816 +// flag useDaylight. When we stream an object out, we translate into an 1.817 +// approximate DOW_IN_MONTH_MODE representation so the object can be parsed 1.818 +// and used by 1.1 code. Following that, we write out the full 1.819 +// representation separately so that contemporary code can recognize and 1.820 +// parse it. The full representation is written in a "packed" format, 1.821 +// consisting of a version number, a length, and an array of bytes. Future 1.822 +// versions of this class may specify different versions. If they wish to 1.823 +// include additional data, they should do so by storing them after the 1.824 +// packed representation below. 1.825 +//---------------------------------------------------------------------- 1.826 + 1.827 +/** 1.828 + * Given a set of encoded rules in startDay and startDayOfMonth, decode 1.829 + * them and set the startMode appropriately. Do the same for endDay and 1.830 + * endDayOfMonth. Upon entry, the day of week variables may be zero or 1.831 + * negative, in order to indicate special modes. The day of month 1.832 + * variables may also be negative. Upon exit, the mode variables will be 1.833 + * set, and the day of week and day of month variables will be positive. 1.834 + * This method also recognizes a startDay or endDay of zero as indicating 1.835 + * no DST. 1.836 + */ 1.837 +void 1.838 +SimpleTimeZone::decodeRules(UErrorCode& status) 1.839 +{ 1.840 + decodeStartRule(status); 1.841 + decodeEndRule(status); 1.842 +} 1.843 + 1.844 +/** 1.845 + * Decode the start rule and validate the parameters. The parameters are 1.846 + * expected to be in encoded form, which represents the various rule modes 1.847 + * by negating or zeroing certain values. Representation formats are: 1.848 + * <p> 1.849 + * <pre> 1.850 + * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST 1.851 + * ------------ ----- -------- -------- ---------- 1.852 + * month 0..11 same same same don't care 1.853 + * day -5..5 1..31 1..31 -1..-31 0 1.854 + * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care 1.855 + * time 0..ONEDAY same same same don't care 1.856 + * </pre> 1.857 + * The range for month does not include UNDECIMBER since this class is 1.858 + * really specific to GregorianCalendar, which does not use that month. 1.859 + * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the 1.860 + * end rule is an exclusive limit point. That is, the range of times that 1.861 + * are in DST include those >= the start and < the end. For this reason, 1.862 + * it should be possible to specify an end of ONEDAY in order to include the 1.863 + * entire day. Although this is equivalent to time 0 of the following day, 1.864 + * it's not always possible to specify that, for example, on December 31. 1.865 + * While arguably the start range should still be 0..ONEDAY-1, we keep 1.866 + * the start and end ranges the same for consistency. 1.867 + */ 1.868 +void 1.869 +SimpleTimeZone::decodeStartRule(UErrorCode& status) 1.870 +{ 1.871 + if(U_FAILURE(status)) return; 1.872 + 1.873 + useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); 1.874 + if (useDaylight && dstSavings == 0) { 1.875 + dstSavings = U_MILLIS_PER_HOUR; 1.876 + } 1.877 + if (startDay != 0) { 1.878 + if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) { 1.879 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.880 + return; 1.881 + } 1.882 + if (startTime < 0 || startTime > U_MILLIS_PER_DAY || 1.883 + startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) { 1.884 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.885 + return; 1.886 + } 1.887 + if (startDayOfWeek == 0) { 1.888 + startMode = DOM_MODE; 1.889 + } else { 1.890 + if (startDayOfWeek > 0) { 1.891 + startMode = DOW_IN_MONTH_MODE; 1.892 + } else { 1.893 + startDayOfWeek = (int8_t)-startDayOfWeek; 1.894 + if (startDay > 0) { 1.895 + startMode = DOW_GE_DOM_MODE; 1.896 + } else { 1.897 + startDay = (int8_t)-startDay; 1.898 + startMode = DOW_LE_DOM_MODE; 1.899 + } 1.900 + } 1.901 + if (startDayOfWeek > UCAL_SATURDAY) { 1.902 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.903 + return; 1.904 + } 1.905 + } 1.906 + if (startMode == DOW_IN_MONTH_MODE) { 1.907 + if (startDay < -5 || startDay > 5) { 1.908 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.909 + return; 1.910 + } 1.911 + } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) { 1.912 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.913 + return; 1.914 + } 1.915 + } 1.916 +} 1.917 + 1.918 +/** 1.919 + * Decode the end rule and validate the parameters. This method is exactly 1.920 + * analogous to decodeStartRule(). 1.921 + * @see decodeStartRule 1.922 + */ 1.923 +void 1.924 +SimpleTimeZone::decodeEndRule(UErrorCode& status) 1.925 +{ 1.926 + if(U_FAILURE(status)) return; 1.927 + 1.928 + useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); 1.929 + if (useDaylight && dstSavings == 0) { 1.930 + dstSavings = U_MILLIS_PER_HOUR; 1.931 + } 1.932 + if (endDay != 0) { 1.933 + if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) { 1.934 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.935 + return; 1.936 + } 1.937 + if (endTime < 0 || endTime > U_MILLIS_PER_DAY || 1.938 + endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) { 1.939 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.940 + return; 1.941 + } 1.942 + if (endDayOfWeek == 0) { 1.943 + endMode = DOM_MODE; 1.944 + } else { 1.945 + if (endDayOfWeek > 0) { 1.946 + endMode = DOW_IN_MONTH_MODE; 1.947 + } else { 1.948 + endDayOfWeek = (int8_t)-endDayOfWeek; 1.949 + if (endDay > 0) { 1.950 + endMode = DOW_GE_DOM_MODE; 1.951 + } else { 1.952 + endDay = (int8_t)-endDay; 1.953 + endMode = DOW_LE_DOM_MODE; 1.954 + } 1.955 + } 1.956 + if (endDayOfWeek > UCAL_SATURDAY) { 1.957 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.958 + return; 1.959 + } 1.960 + } 1.961 + if (endMode == DOW_IN_MONTH_MODE) { 1.962 + if (endDay < -5 || endDay > 5) { 1.963 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.964 + return; 1.965 + } 1.966 + } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) { 1.967 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.968 + return; 1.969 + } 1.970 + } 1.971 +} 1.972 + 1.973 +UBool 1.974 +SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { 1.975 + if (!useDaylight) { 1.976 + return FALSE; 1.977 + } 1.978 + 1.979 + UErrorCode status = U_ZERO_ERROR; 1.980 + checkTransitionRules(status); 1.981 + if (U_FAILURE(status)) { 1.982 + return FALSE; 1.983 + } 1.984 + 1.985 + UDate firstTransitionTime = firstTransition->getTime(); 1.986 + if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) { 1.987 + result = *firstTransition; 1.988 + } 1.989 + UDate stdDate, dstDate; 1.990 + UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); 1.991 + UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); 1.992 + if (stdAvail && (!dstAvail || stdDate < dstDate)) { 1.993 + result.setTime(stdDate); 1.994 + result.setFrom((const TimeZoneRule&)*dstRule); 1.995 + result.setTo((const TimeZoneRule&)*stdRule); 1.996 + return TRUE; 1.997 + } 1.998 + if (dstAvail && (!stdAvail || dstDate < stdDate)) { 1.999 + result.setTime(dstDate); 1.1000 + result.setFrom((const TimeZoneRule&)*stdRule); 1.1001 + result.setTo((const TimeZoneRule&)*dstRule); 1.1002 + return TRUE; 1.1003 + } 1.1004 + return FALSE; 1.1005 +} 1.1006 + 1.1007 +UBool 1.1008 +SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { 1.1009 + if (!useDaylight) { 1.1010 + return FALSE; 1.1011 + } 1.1012 + 1.1013 + UErrorCode status = U_ZERO_ERROR; 1.1014 + checkTransitionRules(status); 1.1015 + if (U_FAILURE(status)) { 1.1016 + return FALSE; 1.1017 + } 1.1018 + 1.1019 + UDate firstTransitionTime = firstTransition->getTime(); 1.1020 + if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) { 1.1021 + return FALSE; 1.1022 + } 1.1023 + UDate stdDate, dstDate; 1.1024 + UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); 1.1025 + UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); 1.1026 + if (stdAvail && (!dstAvail || stdDate > dstDate)) { 1.1027 + result.setTime(stdDate); 1.1028 + result.setFrom((const TimeZoneRule&)*dstRule); 1.1029 + result.setTo((const TimeZoneRule&)*stdRule); 1.1030 + return TRUE; 1.1031 + } 1.1032 + if (dstAvail && (!stdAvail || dstDate > stdDate)) { 1.1033 + result.setTime(dstDate); 1.1034 + result.setFrom((const TimeZoneRule&)*stdRule); 1.1035 + result.setTo((const TimeZoneRule&)*dstRule); 1.1036 + return TRUE; 1.1037 + } 1.1038 + return FALSE; 1.1039 +} 1.1040 + 1.1041 +void 1.1042 +SimpleTimeZone::clearTransitionRules(void) { 1.1043 + initialRule = NULL; 1.1044 + firstTransition = NULL; 1.1045 + stdRule = NULL; 1.1046 + dstRule = NULL; 1.1047 + transitionRulesInitialized = FALSE; 1.1048 +} 1.1049 + 1.1050 +void 1.1051 +SimpleTimeZone::deleteTransitionRules(void) { 1.1052 + if (initialRule != NULL) { 1.1053 + delete initialRule; 1.1054 + } 1.1055 + if (firstTransition != NULL) { 1.1056 + delete firstTransition; 1.1057 + } 1.1058 + if (stdRule != NULL) { 1.1059 + delete stdRule; 1.1060 + } 1.1061 + if (dstRule != NULL) { 1.1062 + delete dstRule; 1.1063 + } 1.1064 + clearTransitionRules(); 1.1065 + } 1.1066 + 1.1067 +/* 1.1068 + * Lazy transition rules initializer 1.1069 + * 1.1070 + * Note On the removal of UMTX_CHECK from checkTransitionRules(): 1.1071 + * 1.1072 + * It would be faster to have a UInitOnce as part of a SimpleTimeZone object, 1.1073 + * which would avoid needing to lock a mutex to check the initialization state. 1.1074 + * But we can't easily because simpletz.h is a public header, and including 1.1075 + * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers. 1.1076 + * 1.1077 + * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object, 1.1078 + * allocate it in the constructors. This would be a more intrusive change, but doable 1.1079 + * if performance turns out to be an issue. 1.1080 + */ 1.1081 +static UMutex gLock = U_MUTEX_INITIALIZER; 1.1082 + 1.1083 +void 1.1084 +SimpleTimeZone::checkTransitionRules(UErrorCode& status) const { 1.1085 + if (U_FAILURE(status)) { 1.1086 + return; 1.1087 + } 1.1088 + umtx_lock(&gLock); 1.1089 + if (!transitionRulesInitialized) { 1.1090 + SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this); 1.1091 + ncThis->initTransitionRules(status); 1.1092 + } 1.1093 + umtx_unlock(&gLock); 1.1094 +} 1.1095 + 1.1096 +void 1.1097 +SimpleTimeZone::initTransitionRules(UErrorCode& status) { 1.1098 + if (U_FAILURE(status)) { 1.1099 + return; 1.1100 + } 1.1101 + if (transitionRulesInitialized) { 1.1102 + return; 1.1103 + } 1.1104 + deleteTransitionRules(); 1.1105 + UnicodeString tzid; 1.1106 + getID(tzid); 1.1107 + 1.1108 + if (useDaylight) { 1.1109 + DateTimeRule* dtRule; 1.1110 + DateTimeRule::TimeRuleType timeRuleType; 1.1111 + UDate firstStdStart, firstDstStart; 1.1112 + 1.1113 + // Create a TimeZoneRule for daylight saving time 1.1114 + timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : 1.1115 + ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); 1.1116 + switch (startMode) { 1.1117 + case DOM_MODE: 1.1118 + dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); 1.1119 + break; 1.1120 + case DOW_IN_MONTH_MODE: 1.1121 + dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType); 1.1122 + break; 1.1123 + case DOW_GE_DOM_MODE: 1.1124 + dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType); 1.1125 + break; 1.1126 + case DOW_LE_DOM_MODE: 1.1127 + dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType); 1.1128 + break; 1.1129 + default: 1.1130 + status = U_INVALID_STATE_ERROR; 1.1131 + return; 1.1132 + } 1.1133 + // Check for Null pointer 1.1134 + if (dtRule == NULL) { 1.1135 + status = U_MEMORY_ALLOCATION_ERROR; 1.1136 + return; 1.1137 + } 1.1138 + // For now, use ID + "(DST)" as the name 1.1139 + dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(), 1.1140 + dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); 1.1141 + 1.1142 + // Check for Null pointer 1.1143 + if (dstRule == NULL) { 1.1144 + status = U_MEMORY_ALLOCATION_ERROR; 1.1145 + deleteTransitionRules(); 1.1146 + return; 1.1147 + } 1.1148 + 1.1149 + // Calculate the first DST start time 1.1150 + dstRule->getFirstStart(getRawOffset(), 0, firstDstStart); 1.1151 + 1.1152 + // Create a TimeZoneRule for standard time 1.1153 + timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : 1.1154 + ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); 1.1155 + switch (endMode) { 1.1156 + case DOM_MODE: 1.1157 + dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); 1.1158 + break; 1.1159 + case DOW_IN_MONTH_MODE: 1.1160 + dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); 1.1161 + break; 1.1162 + case DOW_GE_DOM_MODE: 1.1163 + dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType); 1.1164 + break; 1.1165 + case DOW_LE_DOM_MODE: 1.1166 + dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType); 1.1167 + break; 1.1168 + } 1.1169 + 1.1170 + // Check for Null pointer 1.1171 + if (dtRule == NULL) { 1.1172 + status = U_MEMORY_ALLOCATION_ERROR; 1.1173 + deleteTransitionRules(); 1.1174 + return; 1.1175 + } 1.1176 + // For now, use ID + "(STD)" as the name 1.1177 + stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0, 1.1178 + dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); 1.1179 + 1.1180 + //Check for Null pointer 1.1181 + if (stdRule == NULL) { 1.1182 + status = U_MEMORY_ALLOCATION_ERROR; 1.1183 + deleteTransitionRules(); 1.1184 + return; 1.1185 + } 1.1186 + 1.1187 + // Calculate the first STD start time 1.1188 + stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart); 1.1189 + 1.1190 + // Create a TimeZoneRule for initial time 1.1191 + if (firstStdStart < firstDstStart) { 1.1192 + initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings()); 1.1193 + firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule); 1.1194 + } else { 1.1195 + initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0); 1.1196 + firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule); 1.1197 + } 1.1198 + // Check for null pointers. 1.1199 + if (initialRule == NULL || firstTransition == NULL) { 1.1200 + status = U_MEMORY_ALLOCATION_ERROR; 1.1201 + deleteTransitionRules(); 1.1202 + return; 1.1203 + } 1.1204 + 1.1205 + } else { 1.1206 + // Create a TimeZoneRule for initial time 1.1207 + initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0); 1.1208 + // Check for null pointer. 1.1209 + if (initialRule == NULL) { 1.1210 + status = U_MEMORY_ALLOCATION_ERROR; 1.1211 + deleteTransitionRules(); 1.1212 + return; 1.1213 + } 1.1214 + } 1.1215 + 1.1216 + transitionRulesInitialized = TRUE; 1.1217 +} 1.1218 + 1.1219 +int32_t 1.1220 +SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const { 1.1221 + return (useDaylight) ? 2 : 0; 1.1222 +} 1.1223 + 1.1224 +void 1.1225 +SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, 1.1226 + const TimeZoneRule* trsrules[], 1.1227 + int32_t& trscount, 1.1228 + UErrorCode& status) const { 1.1229 + if (U_FAILURE(status)) { 1.1230 + return; 1.1231 + } 1.1232 + checkTransitionRules(status); 1.1233 + if (U_FAILURE(status)) { 1.1234 + return; 1.1235 + } 1.1236 + initial = initialRule; 1.1237 + int32_t cnt = 0; 1.1238 + if (stdRule != NULL) { 1.1239 + if (cnt < trscount) { 1.1240 + trsrules[cnt++] = stdRule; 1.1241 + } 1.1242 + if (cnt < trscount) { 1.1243 + trsrules[cnt++] = dstRule; 1.1244 + } 1.1245 + } 1.1246 + trscount = cnt; 1.1247 +} 1.1248 + 1.1249 + 1.1250 +U_NAMESPACE_END 1.1251 + 1.1252 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.1253 + 1.1254 +//eof