intl/icu/source/i18n/simpletz.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 *******************************************************************************
michael@0 3 * Copyright (C) 1997-2013, International Business Machines Corporation and
michael@0 4 * others. All Rights Reserved.
michael@0 5 *******************************************************************************
michael@0 6 *
michael@0 7 * File SIMPLETZ.H
michael@0 8 *
michael@0 9 * Modification History:
michael@0 10 *
michael@0 11 * Date Name Description
michael@0 12 * 12/05/96 clhuang Creation.
michael@0 13 * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and
michael@0 14 * testing.
michael@0 15 * 07/29/97 aliu Ported source bodies back from Java version with
michael@0 16 * numerous feature enhancements and bug fixes.
michael@0 17 * 08/10/98 stephen JDK 1.2 sync.
michael@0 18 * 09/17/98 stephen Fixed getOffset() for last hour of year and DST
michael@0 19 * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule
michael@0 20 * methods that take TimeMode. Whitespace cleanup.
michael@0 21 ********************************************************************************
michael@0 22 */
michael@0 23
michael@0 24 #include "utypeinfo.h" // for 'typeid' to work
michael@0 25
michael@0 26 #include "unicode/utypes.h"
michael@0 27
michael@0 28 #if !UCONFIG_NO_FORMATTING
michael@0 29
michael@0 30 #include "unicode/simpletz.h"
michael@0 31 #include "unicode/gregocal.h"
michael@0 32 #include "unicode/smpdtfmt.h"
michael@0 33
michael@0 34 #include "gregoimp.h"
michael@0 35 #include "umutex.h"
michael@0 36
michael@0 37 U_NAMESPACE_BEGIN
michael@0 38
michael@0 39 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
michael@0 40
michael@0 41 // Use only for decodeStartRule() and decodeEndRule() where the year is not
michael@0 42 // available. Set February to 29 days to accomodate rules with that date
michael@0 43 // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
michael@0 44 // The compareToRule() method adjusts to February 28 in non-leap years.
michael@0 45 //
michael@0 46 // For actual getOffset() calculations, use Grego::monthLength() and
michael@0 47 // Grego::previousMonthLength() which take leap years into account.
michael@0 48 // We handle leap years assuming always
michael@0 49 // Gregorian, since we know they didn't have daylight time when
michael@0 50 // Gregorian calendar started.
michael@0 51 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
michael@0 52
michael@0 53 static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)"
michael@0 54 static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)"
michael@0 55
michael@0 56
michael@0 57 // *****************************************************************************
michael@0 58 // class SimpleTimeZone
michael@0 59 // *****************************************************************************
michael@0 60
michael@0 61
michael@0 62 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
michael@0 63 : BasicTimeZone(ID),
michael@0 64 startMonth(0),
michael@0 65 startDay(0),
michael@0 66 startDayOfWeek(0),
michael@0 67 startTime(0),
michael@0 68 startTimeMode(WALL_TIME),
michael@0 69 endTimeMode(WALL_TIME),
michael@0 70 endMonth(0),
michael@0 71 endDay(0),
michael@0 72 endDayOfWeek(0),
michael@0 73 endTime(0),
michael@0 74 startYear(0),
michael@0 75 rawOffset(rawOffsetGMT),
michael@0 76 useDaylight(FALSE),
michael@0 77 startMode(DOM_MODE),
michael@0 78 endMode(DOM_MODE),
michael@0 79 dstSavings(U_MILLIS_PER_HOUR)
michael@0 80 {
michael@0 81 clearTransitionRules();
michael@0 82 }
michael@0 83
michael@0 84 // -------------------------------------
michael@0 85
michael@0 86 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
michael@0 87 int8_t savingsStartMonth, int8_t savingsStartDay,
michael@0 88 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
michael@0 89 int8_t savingsEndMonth, int8_t savingsEndDay,
michael@0 90 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
michael@0 91 UErrorCode& status)
michael@0 92 : BasicTimeZone(ID)
michael@0 93 {
michael@0 94 clearTransitionRules();
michael@0 95 construct(rawOffsetGMT,
michael@0 96 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
michael@0 97 savingsStartTime, WALL_TIME,
michael@0 98 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
michael@0 99 savingsEndTime, WALL_TIME,
michael@0 100 U_MILLIS_PER_HOUR, status);
michael@0 101 }
michael@0 102
michael@0 103 // -------------------------------------
michael@0 104
michael@0 105 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
michael@0 106 int8_t savingsStartMonth, int8_t savingsStartDay,
michael@0 107 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
michael@0 108 int8_t savingsEndMonth, int8_t savingsEndDay,
michael@0 109 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
michael@0 110 int32_t savingsDST, UErrorCode& status)
michael@0 111 : BasicTimeZone(ID)
michael@0 112 {
michael@0 113 clearTransitionRules();
michael@0 114 construct(rawOffsetGMT,
michael@0 115 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
michael@0 116 savingsStartTime, WALL_TIME,
michael@0 117 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
michael@0 118 savingsEndTime, WALL_TIME,
michael@0 119 savingsDST, status);
michael@0 120 }
michael@0 121
michael@0 122 // -------------------------------------
michael@0 123
michael@0 124 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
michael@0 125 int8_t savingsStartMonth, int8_t savingsStartDay,
michael@0 126 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
michael@0 127 TimeMode savingsStartTimeMode,
michael@0 128 int8_t savingsEndMonth, int8_t savingsEndDay,
michael@0 129 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
michael@0 130 TimeMode savingsEndTimeMode,
michael@0 131 int32_t savingsDST, UErrorCode& status)
michael@0 132 : BasicTimeZone(ID)
michael@0 133 {
michael@0 134 clearTransitionRules();
michael@0 135 construct(rawOffsetGMT,
michael@0 136 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
michael@0 137 savingsStartTime, savingsStartTimeMode,
michael@0 138 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
michael@0 139 savingsEndTime, savingsEndTimeMode,
michael@0 140 savingsDST, status);
michael@0 141 }
michael@0 142
michael@0 143 /**
michael@0 144 * Internal construction method.
michael@0 145 */
michael@0 146 void SimpleTimeZone::construct(int32_t rawOffsetGMT,
michael@0 147 int8_t savingsStartMonth,
michael@0 148 int8_t savingsStartDay,
michael@0 149 int8_t savingsStartDayOfWeek,
michael@0 150 int32_t savingsStartTime,
michael@0 151 TimeMode savingsStartTimeMode,
michael@0 152 int8_t savingsEndMonth,
michael@0 153 int8_t savingsEndDay,
michael@0 154 int8_t savingsEndDayOfWeek,
michael@0 155 int32_t savingsEndTime,
michael@0 156 TimeMode savingsEndTimeMode,
michael@0 157 int32_t savingsDST,
michael@0 158 UErrorCode& status)
michael@0 159 {
michael@0 160 this->rawOffset = rawOffsetGMT;
michael@0 161 this->startMonth = savingsStartMonth;
michael@0 162 this->startDay = savingsStartDay;
michael@0 163 this->startDayOfWeek = savingsStartDayOfWeek;
michael@0 164 this->startTime = savingsStartTime;
michael@0 165 this->startTimeMode = savingsStartTimeMode;
michael@0 166 this->endMonth = savingsEndMonth;
michael@0 167 this->endDay = savingsEndDay;
michael@0 168 this->endDayOfWeek = savingsEndDayOfWeek;
michael@0 169 this->endTime = savingsEndTime;
michael@0 170 this->endTimeMode = savingsEndTimeMode;
michael@0 171 this->dstSavings = savingsDST;
michael@0 172 this->startYear = 0;
michael@0 173 this->startMode = DOM_MODE;
michael@0 174 this->endMode = DOM_MODE;
michael@0 175
michael@0 176 decodeRules(status);
michael@0 177
michael@0 178 if (savingsDST <= 0) {
michael@0 179 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 180 }
michael@0 181 }
michael@0 182
michael@0 183 // -------------------------------------
michael@0 184
michael@0 185 SimpleTimeZone::~SimpleTimeZone()
michael@0 186 {
michael@0 187 deleteTransitionRules();
michael@0 188 }
michael@0 189
michael@0 190 // -------------------------------------
michael@0 191
michael@0 192 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
michael@0 193 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
michael@0 194 : BasicTimeZone(source)
michael@0 195 {
michael@0 196 *this = source;
michael@0 197 }
michael@0 198
michael@0 199 // -------------------------------------
michael@0 200
michael@0 201 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
michael@0 202 SimpleTimeZone &
michael@0 203 SimpleTimeZone::operator=(const SimpleTimeZone &right)
michael@0 204 {
michael@0 205 if (this != &right)
michael@0 206 {
michael@0 207 TimeZone::operator=(right);
michael@0 208 rawOffset = right.rawOffset;
michael@0 209 startMonth = right.startMonth;
michael@0 210 startDay = right.startDay;
michael@0 211 startDayOfWeek = right.startDayOfWeek;
michael@0 212 startTime = right.startTime;
michael@0 213 startTimeMode = right.startTimeMode;
michael@0 214 startMode = right.startMode;
michael@0 215 endMonth = right.endMonth;
michael@0 216 endDay = right.endDay;
michael@0 217 endDayOfWeek = right.endDayOfWeek;
michael@0 218 endTime = right.endTime;
michael@0 219 endTimeMode = right.endTimeMode;
michael@0 220 endMode = right.endMode;
michael@0 221 startYear = right.startYear;
michael@0 222 dstSavings = right.dstSavings;
michael@0 223 useDaylight = right.useDaylight;
michael@0 224 clearTransitionRules();
michael@0 225 }
michael@0 226 return *this;
michael@0 227 }
michael@0 228
michael@0 229 // -------------------------------------
michael@0 230
michael@0 231 UBool
michael@0 232 SimpleTimeZone::operator==(const TimeZone& that) const
michael@0 233 {
michael@0 234 return ((this == &that) ||
michael@0 235 (typeid(*this) == typeid(that) &&
michael@0 236 TimeZone::operator==(that) &&
michael@0 237 hasSameRules(that)));
michael@0 238 }
michael@0 239
michael@0 240 // -------------------------------------
michael@0 241
michael@0 242 // Called by TimeZone::createDefault() inside a Mutex - be careful.
michael@0 243 TimeZone*
michael@0 244 SimpleTimeZone::clone() const
michael@0 245 {
michael@0 246 return new SimpleTimeZone(*this);
michael@0 247 }
michael@0 248
michael@0 249 // -------------------------------------
michael@0 250
michael@0 251 /**
michael@0 252 * Sets the daylight savings starting year, that is, the year this time zone began
michael@0 253 * observing its specified daylight savings time rules. The time zone is considered
michael@0 254 * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
michael@0 255 * support historical daylight-savings-time rules.
michael@0 256 * @param year the daylight savings starting year.
michael@0 257 */
michael@0 258 void
michael@0 259 SimpleTimeZone::setStartYear(int32_t year)
michael@0 260 {
michael@0 261 startYear = year;
michael@0 262 transitionRulesInitialized = FALSE;
michael@0 263 }
michael@0 264
michael@0 265 // -------------------------------------
michael@0 266
michael@0 267 /**
michael@0 268 * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
michael@0 269 * Time starts at the first Sunday in April, at 2 AM in standard time.
michael@0 270 * Therefore, you can set the start rule by calling:
michael@0 271 * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
michael@0 272 * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
michael@0 273 * the exact starting date. Their exact meaning depend on their respective signs,
michael@0 274 * allowing various types of rules to be constructed, as follows:<ul>
michael@0 275 * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
michael@0 276 * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
michael@0 277 * of the month).
michael@0 278 * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
michael@0 279 * the day of week in the month counting backward from the end of the month.
michael@0 280 * (e.g., (-1, MONDAY) is the last Monday in the month)
michael@0 281 * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
michael@0 282 * specifies the day of the month, regardless of what day of the week it is.
michael@0 283 * (e.g., (10, 0) is the tenth day of the month)
michael@0 284 * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
michael@0 285 * specifies the day of the month counting backward from the end of the
michael@0 286 * month, regardless of what day of the week it is (e.g., (-2, 0) is the
michael@0 287 * next-to-last day of the month).
michael@0 288 * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
michael@0 289 * first specified day of the week on or after the specfied day of the month.
michael@0 290 * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
michael@0 291 * [or the 15th itself if the 15th is a Sunday].)
michael@0 292 * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
michael@0 293 * last specified day of the week on or before the specified day of the month.
michael@0 294 * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
michael@0 295 * [or the 20th itself if the 20th is a Tuesday].)</ul>
michael@0 296 * @param month the daylight savings starting month. Month is 0-based.
michael@0 297 * eg, 0 for January.
michael@0 298 * @param dayOfWeekInMonth the daylight savings starting
michael@0 299 * day-of-week-in-month. Please see the member description for an example.
michael@0 300 * @param dayOfWeek the daylight savings starting day-of-week. Please see
michael@0 301 * the member description for an example.
michael@0 302 * @param time the daylight savings starting time. Please see the member
michael@0 303 * description for an example.
michael@0 304 */
michael@0 305
michael@0 306 void
michael@0 307 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
michael@0 308 int32_t time, TimeMode mode, UErrorCode& status)
michael@0 309 {
michael@0 310 startMonth = (int8_t)month;
michael@0 311 startDay = (int8_t)dayOfWeekInMonth;
michael@0 312 startDayOfWeek = (int8_t)dayOfWeek;
michael@0 313 startTime = time;
michael@0 314 startTimeMode = mode;
michael@0 315 decodeStartRule(status);
michael@0 316 transitionRulesInitialized = FALSE;
michael@0 317 }
michael@0 318
michael@0 319 // -------------------------------------
michael@0 320
michael@0 321 void
michael@0 322 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
michael@0 323 int32_t time, TimeMode mode, UErrorCode& status)
michael@0 324 {
michael@0 325 setStartRule(month, dayOfMonth, 0, time, mode, status);
michael@0 326 }
michael@0 327
michael@0 328 // -------------------------------------
michael@0 329
michael@0 330 void
michael@0 331 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
michael@0 332 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
michael@0 333 {
michael@0 334 setStartRule(month, after ? dayOfMonth : -dayOfMonth,
michael@0 335 -dayOfWeek, time, mode, status);
michael@0 336 }
michael@0 337
michael@0 338 // -------------------------------------
michael@0 339
michael@0 340 /**
michael@0 341 * Sets the daylight savings ending rule. For example, in the U.S., Daylight
michael@0 342 * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
michael@0 343 * Therefore, you can set the end rule by calling:
michael@0 344 * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
michael@0 345 * Various other types of rules can be specified by manipulating the dayOfWeek
michael@0 346 * and dayOfWeekInMonth parameters. For complete details, see the documentation
michael@0 347 * for setStartRule().
michael@0 348 * @param month the daylight savings ending month. Month is 0-based.
michael@0 349 * eg, 0 for January.
michael@0 350 * @param dayOfWeekInMonth the daylight savings ending
michael@0 351 * day-of-week-in-month. See setStartRule() for a complete explanation.
michael@0 352 * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
michael@0 353 * for a complete explanation.
michael@0 354 * @param time the daylight savings ending time. Please see the member
michael@0 355 * description for an example.
michael@0 356 */
michael@0 357
michael@0 358 void
michael@0 359 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
michael@0 360 int32_t time, TimeMode mode, UErrorCode& status)
michael@0 361 {
michael@0 362 endMonth = (int8_t)month;
michael@0 363 endDay = (int8_t)dayOfWeekInMonth;
michael@0 364 endDayOfWeek = (int8_t)dayOfWeek;
michael@0 365 endTime = time;
michael@0 366 endTimeMode = mode;
michael@0 367 decodeEndRule(status);
michael@0 368 transitionRulesInitialized = FALSE;
michael@0 369 }
michael@0 370
michael@0 371 // -------------------------------------
michael@0 372
michael@0 373 void
michael@0 374 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
michael@0 375 int32_t time, TimeMode mode, UErrorCode& status)
michael@0 376 {
michael@0 377 setEndRule(month, dayOfMonth, 0, time, mode, status);
michael@0 378 }
michael@0 379
michael@0 380 // -------------------------------------
michael@0 381
michael@0 382 void
michael@0 383 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
michael@0 384 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
michael@0 385 {
michael@0 386 setEndRule(month, after ? dayOfMonth : -dayOfMonth,
michael@0 387 -dayOfWeek, time, mode, status);
michael@0 388 }
michael@0 389
michael@0 390 // -------------------------------------
michael@0 391
michael@0 392 int32_t
michael@0 393 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
michael@0 394 uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
michael@0 395 {
michael@0 396 // Check the month before calling Grego::monthLength(). This
michael@0 397 // duplicates the test that occurs in the 7-argument getOffset(),
michael@0 398 // however, this is unavoidable. We don't mind because this method, in
michael@0 399 // fact, should not be called; internal code should always call the
michael@0 400 // 7-argument getOffset(), and outside code should use Calendar.get(int
michael@0 401 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
michael@0 402 // this method because it's public API. - liu 8/10/98
michael@0 403 if(month < UCAL_JANUARY || month > UCAL_DECEMBER) {
michael@0 404 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 405 return 0;
michael@0 406 }
michael@0 407
michael@0 408 return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status);
michael@0 409 }
michael@0 410
michael@0 411 int32_t
michael@0 412 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
michael@0 413 uint8_t dayOfWeek, int32_t millis,
michael@0 414 int32_t /*monthLength*/, UErrorCode& status) const
michael@0 415 {
michael@0 416 // Check the month before calling Grego::monthLength(). This
michael@0 417 // duplicates a test that occurs in the 9-argument getOffset(),
michael@0 418 // however, this is unavoidable. We don't mind because this method, in
michael@0 419 // fact, should not be called; internal code should always call the
michael@0 420 // 9-argument getOffset(), and outside code should use Calendar.get(int
michael@0 421 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
michael@0 422 // this method because it's public API. - liu 8/10/98
michael@0 423 if (month < UCAL_JANUARY
michael@0 424 || month > UCAL_DECEMBER) {
michael@0 425 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 426 return -1;
michael@0 427 }
michael@0 428
michael@0 429 // We ignore monthLength because it can be derived from year and month.
michael@0 430 // This is so that February in leap years is calculated correctly.
michael@0 431 // We keep this argument in this function for backwards compatibility.
michael@0 432 return getOffset(era, year, month, day, dayOfWeek, millis,
michael@0 433 Grego::monthLength(year, month),
michael@0 434 Grego::previousMonthLength(year, month),
michael@0 435 status);
michael@0 436 }
michael@0 437
michael@0 438 int32_t
michael@0 439 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
michael@0 440 uint8_t dayOfWeek, int32_t millis,
michael@0 441 int32_t monthLength, int32_t prevMonthLength,
michael@0 442 UErrorCode& status) const
michael@0 443 {
michael@0 444 if(U_FAILURE(status)) return 0;
michael@0 445
michael@0 446 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
michael@0 447 || month < UCAL_JANUARY
michael@0 448 || month > UCAL_DECEMBER
michael@0 449 || day < 1
michael@0 450 || day > monthLength
michael@0 451 || dayOfWeek < UCAL_SUNDAY
michael@0 452 || dayOfWeek > UCAL_SATURDAY
michael@0 453 || millis < 0
michael@0 454 || millis >= U_MILLIS_PER_DAY
michael@0 455 || monthLength < 28
michael@0 456 || monthLength > 31
michael@0 457 || prevMonthLength < 28
michael@0 458 || prevMonthLength > 31) {
michael@0 459 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 460 return -1;
michael@0 461 }
michael@0 462
michael@0 463 int32_t result = rawOffset;
michael@0 464
michael@0 465 // Bail out if we are before the onset of daylight savings time
michael@0 466 if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
michael@0 467 return result;
michael@0 468
michael@0 469 // Check for southern hemisphere. We assume that the start and end
michael@0 470 // month are different.
michael@0 471 UBool southern = (startMonth > endMonth);
michael@0 472
michael@0 473 // Compare the date to the starting and ending rules.+1 = date>rule, -1
michael@0 474 // = date<rule, 0 = date==rule.
michael@0 475 int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
michael@0 476 (int8_t)day, (int8_t)dayOfWeek, millis,
michael@0 477 startTimeMode == UTC_TIME ? -rawOffset : 0,
michael@0 478 startMode, (int8_t)startMonth, (int8_t)startDayOfWeek,
michael@0 479 (int8_t)startDay, startTime);
michael@0 480 int32_t endCompare = 0;
michael@0 481
michael@0 482 /* We don't always have to compute endCompare. For many instances,
michael@0 483 * startCompare is enough to determine if we are in DST or not. In the
michael@0 484 * northern hemisphere, if we are before the start rule, we can't have
michael@0 485 * DST. In the southern hemisphere, if we are after the start rule, we
michael@0 486 * must have DST. This is reflected in the way the next if statement
michael@0 487 * (not the one immediately following) short circuits. */
michael@0 488 if(southern != (startCompare >= 0)) {
michael@0 489 endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
michael@0 490 (int8_t)day, (int8_t)dayOfWeek, millis,
michael@0 491 endTimeMode == WALL_TIME ? dstSavings :
michael@0 492 (endTimeMode == UTC_TIME ? -rawOffset : 0),
michael@0 493 endMode, (int8_t)endMonth, (int8_t)endDayOfWeek,
michael@0 494 (int8_t)endDay, endTime);
michael@0 495 }
michael@0 496
michael@0 497 // Check for both the northern and southern hemisphere cases. We
michael@0 498 // assume that in the northern hemisphere, the start rule is before the
michael@0 499 // end rule within the calendar year, and vice versa for the southern
michael@0 500 // hemisphere.
michael@0 501 if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
michael@0 502 (southern && (startCompare >= 0 || endCompare < 0)))
michael@0 503 result += dstSavings;
michael@0 504
michael@0 505 return result;
michael@0 506 }
michael@0 507
michael@0 508 void
michael@0 509 SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
michael@0 510 int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const {
michael@0 511 if (U_FAILURE(status)) {
michael@0 512 return;
michael@0 513 }
michael@0 514
michael@0 515 rawOffsetGMT = getRawOffset();
michael@0 516 int32_t year, month, dom, dow;
michael@0 517 double day = uprv_floor(date / U_MILLIS_PER_DAY);
michael@0 518 int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
michael@0 519
michael@0 520 Grego::dayToFields(day, year, month, dom, dow);
michael@0 521
michael@0 522 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
michael@0 523 (uint8_t) dow, millis,
michael@0 524 Grego::monthLength(year, month),
michael@0 525 status) - rawOffsetGMT;
michael@0 526 if (U_FAILURE(status)) {
michael@0 527 return;
michael@0 528 }
michael@0 529
michael@0 530 UBool recalc = FALSE;
michael@0 531
michael@0 532 // Now we need some adjustment
michael@0 533 if (savingsDST > 0) {
michael@0 534 if ((nonExistingTimeOpt & kStdDstMask) == kStandard
michael@0 535 || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
michael@0 536 date -= getDSTSavings();
michael@0 537 recalc = TRUE;
michael@0 538 }
michael@0 539 } else {
michael@0 540 if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
michael@0 541 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
michael@0 542 date -= getDSTSavings();
michael@0 543 recalc = TRUE;
michael@0 544 }
michael@0 545 }
michael@0 546 if (recalc) {
michael@0 547 day = uprv_floor(date / U_MILLIS_PER_DAY);
michael@0 548 millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
michael@0 549 Grego::dayToFields(day, year, month, dom, dow);
michael@0 550 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
michael@0 551 (uint8_t) dow, millis,
michael@0 552 Grego::monthLength(year, month),
michael@0 553 status) - rawOffsetGMT;
michael@0 554 }
michael@0 555 }
michael@0 556
michael@0 557 // -------------------------------------
michael@0 558
michael@0 559 /**
michael@0 560 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
michael@0 561 * on whether the date is after, equal to, or before the rule date. The
michael@0 562 * millis are compared directly against the ruleMillis, so any
michael@0 563 * standard-daylight adjustments must be handled by the caller.
michael@0 564 *
michael@0 565 * @return 1 if the date is after the rule date, -1 if the date is before
michael@0 566 * the rule date, or 0 if the date is equal to the rule date.
michael@0 567 */
michael@0 568 int32_t
michael@0 569 SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
michael@0 570 int8_t dayOfMonth,
michael@0 571 int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
michael@0 572 EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
michael@0 573 int8_t ruleDay, int32_t ruleMillis)
michael@0 574 {
michael@0 575 // Make adjustments for startTimeMode and endTimeMode
michael@0 576 millis += millisDelta;
michael@0 577 while (millis >= U_MILLIS_PER_DAY) {
michael@0 578 millis -= U_MILLIS_PER_DAY;
michael@0 579 ++dayOfMonth;
michael@0 580 dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
michael@0 581 if (dayOfMonth > monthLen) {
michael@0 582 dayOfMonth = 1;
michael@0 583 /* When incrementing the month, it is desirible to overflow
michael@0 584 * from DECEMBER to DECEMBER+1, since we use the result to
michael@0 585 * compare against a real month. Wraparound of the value
michael@0 586 * leads to bug 4173604. */
michael@0 587 ++month;
michael@0 588 }
michael@0 589 }
michael@0 590 while (millis < 0) {
michael@0 591 millis += U_MILLIS_PER_DAY;
michael@0 592 --dayOfMonth;
michael@0 593 dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
michael@0 594 if (dayOfMonth < 1) {
michael@0 595 dayOfMonth = prevMonthLen;
michael@0 596 --month;
michael@0 597 }
michael@0 598 }
michael@0 599
michael@0 600 // first compare months. If they're different, we don't have to worry about days
michael@0 601 // and times
michael@0 602 if (month < ruleMonth) return -1;
michael@0 603 else if (month > ruleMonth) return 1;
michael@0 604
michael@0 605 // calculate the actual day of month for the rule
michael@0 606 int32_t ruleDayOfMonth = 0;
michael@0 607
michael@0 608 // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
michael@0 609 if (ruleDay > monthLen) {
michael@0 610 ruleDay = monthLen;
michael@0 611 }
michael@0 612
michael@0 613 switch (ruleMode)
michael@0 614 {
michael@0 615 // if the mode is day-of-month, the day of month is given
michael@0 616 case DOM_MODE:
michael@0 617 ruleDayOfMonth = ruleDay;
michael@0 618 break;
michael@0 619
michael@0 620 // if the mode is day-of-week-in-month, calculate the day-of-month from it
michael@0 621 case DOW_IN_MONTH_MODE:
michael@0 622 // In this case ruleDay is the day-of-week-in-month (this code is using
michael@0 623 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
michael@0 624 // of the first day of the month, so it's trusting that they're really
michael@0 625 // consistent with each other)
michael@0 626 if (ruleDay > 0)
michael@0 627 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
michael@0 628 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
michael@0 629
michael@0 630 // if ruleDay is negative (we assume it's not zero here), we have to do
michael@0 631 // the same calculation figuring backward from the last day of the month.
michael@0 632 else
michael@0 633 {
michael@0 634 // (again, this code is trusting that dayOfWeek and dayOfMonth are
michael@0 635 // consistent with each other here, since we're using them to figure
michael@0 636 // the day of week of the first of the month)
michael@0 637 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
michael@0 638 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
michael@0 639 }
michael@0 640 break;
michael@0 641
michael@0 642 case DOW_GE_DOM_MODE:
michael@0 643 ruleDayOfMonth = ruleDay +
michael@0 644 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
michael@0 645 break;
michael@0 646
michael@0 647 case DOW_LE_DOM_MODE:
michael@0 648 ruleDayOfMonth = ruleDay -
michael@0 649 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
michael@0 650 // Note at this point ruleDayOfMonth may be <1, although it will
michael@0 651 // be >=1 for well-formed rules.
michael@0 652 break;
michael@0 653 }
michael@0 654
michael@0 655 // now that we have a real day-in-month for the rule, we can compare days...
michael@0 656 if (dayOfMonth < ruleDayOfMonth) return -1;
michael@0 657 else if (dayOfMonth > ruleDayOfMonth) return 1;
michael@0 658
michael@0 659 // ...and if they're equal, we compare times
michael@0 660 if (millis < ruleMillis) return -1;
michael@0 661 else if (millis > ruleMillis) return 1;
michael@0 662 else return 0;
michael@0 663 }
michael@0 664
michael@0 665 // -------------------------------------
michael@0 666
michael@0 667 int32_t
michael@0 668 SimpleTimeZone::getRawOffset() const
michael@0 669 {
michael@0 670 return rawOffset;
michael@0 671 }
michael@0 672
michael@0 673 // -------------------------------------
michael@0 674
michael@0 675 void
michael@0 676 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
michael@0 677 {
michael@0 678 rawOffset = offsetMillis;
michael@0 679 transitionRulesInitialized = FALSE;
michael@0 680 }
michael@0 681
michael@0 682 // -------------------------------------
michael@0 683
michael@0 684 void
michael@0 685 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
michael@0 686 {
michael@0 687 if (millisSavedDuringDST <= 0) {
michael@0 688 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 689 }
michael@0 690 else {
michael@0 691 dstSavings = millisSavedDuringDST;
michael@0 692 }
michael@0 693 transitionRulesInitialized = FALSE;
michael@0 694 }
michael@0 695
michael@0 696 // -------------------------------------
michael@0 697
michael@0 698 int32_t
michael@0 699 SimpleTimeZone::getDSTSavings() const
michael@0 700 {
michael@0 701 return dstSavings;
michael@0 702 }
michael@0 703
michael@0 704 // -------------------------------------
michael@0 705
michael@0 706 UBool
michael@0 707 SimpleTimeZone::useDaylightTime() const
michael@0 708 {
michael@0 709 return useDaylight;
michael@0 710 }
michael@0 711
michael@0 712 // -------------------------------------
michael@0 713
michael@0 714 /**
michael@0 715 * Overrides TimeZone
michael@0 716 * Queries if the given date is in Daylight Savings Time.
michael@0 717 */
michael@0 718 UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
michael@0 719 {
michael@0 720 // This method is wasteful since it creates a new GregorianCalendar and
michael@0 721 // deletes it each time it is called. However, this is a deprecated method
michael@0 722 // and provided only for Java compatibility as of 8/6/97 [LIU].
michael@0 723 if (U_FAILURE(status)) return FALSE;
michael@0 724 GregorianCalendar *gc = new GregorianCalendar(*this, status);
michael@0 725 /* test for NULL */
michael@0 726 if (gc == 0) {
michael@0 727 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 728 return FALSE;
michael@0 729 }
michael@0 730 gc->setTime(date, status);
michael@0 731 UBool result = gc->inDaylightTime(status);
michael@0 732 delete gc;
michael@0 733 return result;
michael@0 734 }
michael@0 735
michael@0 736 // -------------------------------------
michael@0 737
michael@0 738 /**
michael@0 739 * Return true if this zone has the same rules and offset as another zone.
michael@0 740 * @param other the TimeZone object to be compared with
michael@0 741 * @return true if the given zone has the same rules and offset as this one
michael@0 742 */
michael@0 743 UBool
michael@0 744 SimpleTimeZone::hasSameRules(const TimeZone& other) const
michael@0 745 {
michael@0 746 if (this == &other) return TRUE;
michael@0 747 if (typeid(*this) != typeid(other)) return FALSE;
michael@0 748 SimpleTimeZone *that = (SimpleTimeZone*)&other;
michael@0 749 return rawOffset == that->rawOffset &&
michael@0 750 useDaylight == that->useDaylight &&
michael@0 751 (!useDaylight
michael@0 752 // Only check rules if using DST
michael@0 753 || (dstSavings == that->dstSavings &&
michael@0 754 startMode == that->startMode &&
michael@0 755 startMonth == that->startMonth &&
michael@0 756 startDay == that->startDay &&
michael@0 757 startDayOfWeek == that->startDayOfWeek &&
michael@0 758 startTime == that->startTime &&
michael@0 759 startTimeMode == that->startTimeMode &&
michael@0 760 endMode == that->endMode &&
michael@0 761 endMonth == that->endMonth &&
michael@0 762 endDay == that->endDay &&
michael@0 763 endDayOfWeek == that->endDayOfWeek &&
michael@0 764 endTime == that->endTime &&
michael@0 765 endTimeMode == that->endTimeMode &&
michael@0 766 startYear == that->startYear));
michael@0 767 }
michael@0 768
michael@0 769 // -------------------------------------
michael@0 770
michael@0 771 //----------------------------------------------------------------------
michael@0 772 // Rule representation
michael@0 773 //
michael@0 774 // We represent the following flavors of rules:
michael@0 775 // 5 the fifth of the month
michael@0 776 // lastSun the last Sunday in the month
michael@0 777 // lastMon the last Monday in the month
michael@0 778 // Sun>=8 first Sunday on or after the eighth
michael@0 779 // Sun<=25 last Sunday on or before the 25th
michael@0 780 // This is further complicated by the fact that we need to remain
michael@0 781 // backward compatible with the 1.1 FCS. Finally, we need to minimize
michael@0 782 // API changes. In order to satisfy these requirements, we support
michael@0 783 // three representation systems, and we translate between them.
michael@0 784 //
michael@0 785 // INTERNAL REPRESENTATION
michael@0 786 // This is the format SimpleTimeZone objects take after construction or
michael@0 787 // streaming in is complete. Rules are represented directly, using an
michael@0 788 // unencoded format. We will discuss the start rule only below; the end
michael@0 789 // rule is analogous.
michael@0 790 // startMode Takes on enumerated values DAY_OF_MONTH,
michael@0 791 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
michael@0 792 // startDay The day of the month, or for DOW_IN_MONTH mode, a
michael@0 793 // value indicating which DOW, such as +1 for first,
michael@0 794 // +2 for second, -1 for last, etc.
michael@0 795 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
michael@0 796 //
michael@0 797 // ENCODED REPRESENTATION
michael@0 798 // This is the format accepted by the constructor and by setStartRule()
michael@0 799 // and setEndRule(). It uses various combinations of positive, negative,
michael@0 800 // and zero values to encode the different rules. This representation
michael@0 801 // allows us to specify all the different rule flavors without altering
michael@0 802 // the API.
michael@0 803 // MODE startMonth startDay startDayOfWeek
michael@0 804 // DOW_IN_MONTH_MODE >=0 !=0 >0
michael@0 805 // DOM_MODE >=0 >0 ==0
michael@0 806 // DOW_GE_DOM_MODE >=0 >0 <0
michael@0 807 // DOW_LE_DOM_MODE >=0 <0 <0
michael@0 808 // (no DST) don't care ==0 don't care
michael@0 809 //
michael@0 810 // STREAMED REPRESENTATION
michael@0 811 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
michael@0 812 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
michael@0 813 // flag useDaylight. When we stream an object out, we translate into an
michael@0 814 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
michael@0 815 // and used by 1.1 code. Following that, we write out the full
michael@0 816 // representation separately so that contemporary code can recognize and
michael@0 817 // parse it. The full representation is written in a "packed" format,
michael@0 818 // consisting of a version number, a length, and an array of bytes. Future
michael@0 819 // versions of this class may specify different versions. If they wish to
michael@0 820 // include additional data, they should do so by storing them after the
michael@0 821 // packed representation below.
michael@0 822 //----------------------------------------------------------------------
michael@0 823
michael@0 824 /**
michael@0 825 * Given a set of encoded rules in startDay and startDayOfMonth, decode
michael@0 826 * them and set the startMode appropriately. Do the same for endDay and
michael@0 827 * endDayOfMonth. Upon entry, the day of week variables may be zero or
michael@0 828 * negative, in order to indicate special modes. The day of month
michael@0 829 * variables may also be negative. Upon exit, the mode variables will be
michael@0 830 * set, and the day of week and day of month variables will be positive.
michael@0 831 * This method also recognizes a startDay or endDay of zero as indicating
michael@0 832 * no DST.
michael@0 833 */
michael@0 834 void
michael@0 835 SimpleTimeZone::decodeRules(UErrorCode& status)
michael@0 836 {
michael@0 837 decodeStartRule(status);
michael@0 838 decodeEndRule(status);
michael@0 839 }
michael@0 840
michael@0 841 /**
michael@0 842 * Decode the start rule and validate the parameters. The parameters are
michael@0 843 * expected to be in encoded form, which represents the various rule modes
michael@0 844 * by negating or zeroing certain values. Representation formats are:
michael@0 845 * <p>
michael@0 846 * <pre>
michael@0 847 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
michael@0 848 * ------------ ----- -------- -------- ----------
michael@0 849 * month 0..11 same same same don't care
michael@0 850 * day -5..5 1..31 1..31 -1..-31 0
michael@0 851 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
michael@0 852 * time 0..ONEDAY same same same don't care
michael@0 853 * </pre>
michael@0 854 * The range for month does not include UNDECIMBER since this class is
michael@0 855 * really specific to GregorianCalendar, which does not use that month.
michael@0 856 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
michael@0 857 * end rule is an exclusive limit point. That is, the range of times that
michael@0 858 * are in DST include those >= the start and < the end. For this reason,
michael@0 859 * it should be possible to specify an end of ONEDAY in order to include the
michael@0 860 * entire day. Although this is equivalent to time 0 of the following day,
michael@0 861 * it's not always possible to specify that, for example, on December 31.
michael@0 862 * While arguably the start range should still be 0..ONEDAY-1, we keep
michael@0 863 * the start and end ranges the same for consistency.
michael@0 864 */
michael@0 865 void
michael@0 866 SimpleTimeZone::decodeStartRule(UErrorCode& status)
michael@0 867 {
michael@0 868 if(U_FAILURE(status)) return;
michael@0 869
michael@0 870 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
michael@0 871 if (useDaylight && dstSavings == 0) {
michael@0 872 dstSavings = U_MILLIS_PER_HOUR;
michael@0 873 }
michael@0 874 if (startDay != 0) {
michael@0 875 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
michael@0 876 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 877 return;
michael@0 878 }
michael@0 879 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
michael@0 880 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
michael@0 881 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 882 return;
michael@0 883 }
michael@0 884 if (startDayOfWeek == 0) {
michael@0 885 startMode = DOM_MODE;
michael@0 886 } else {
michael@0 887 if (startDayOfWeek > 0) {
michael@0 888 startMode = DOW_IN_MONTH_MODE;
michael@0 889 } else {
michael@0 890 startDayOfWeek = (int8_t)-startDayOfWeek;
michael@0 891 if (startDay > 0) {
michael@0 892 startMode = DOW_GE_DOM_MODE;
michael@0 893 } else {
michael@0 894 startDay = (int8_t)-startDay;
michael@0 895 startMode = DOW_LE_DOM_MODE;
michael@0 896 }
michael@0 897 }
michael@0 898 if (startDayOfWeek > UCAL_SATURDAY) {
michael@0 899 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 900 return;
michael@0 901 }
michael@0 902 }
michael@0 903 if (startMode == DOW_IN_MONTH_MODE) {
michael@0 904 if (startDay < -5 || startDay > 5) {
michael@0 905 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 906 return;
michael@0 907 }
michael@0 908 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
michael@0 909 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 910 return;
michael@0 911 }
michael@0 912 }
michael@0 913 }
michael@0 914
michael@0 915 /**
michael@0 916 * Decode the end rule and validate the parameters. This method is exactly
michael@0 917 * analogous to decodeStartRule().
michael@0 918 * @see decodeStartRule
michael@0 919 */
michael@0 920 void
michael@0 921 SimpleTimeZone::decodeEndRule(UErrorCode& status)
michael@0 922 {
michael@0 923 if(U_FAILURE(status)) return;
michael@0 924
michael@0 925 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
michael@0 926 if (useDaylight && dstSavings == 0) {
michael@0 927 dstSavings = U_MILLIS_PER_HOUR;
michael@0 928 }
michael@0 929 if (endDay != 0) {
michael@0 930 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
michael@0 931 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 932 return;
michael@0 933 }
michael@0 934 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
michael@0 935 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
michael@0 936 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 937 return;
michael@0 938 }
michael@0 939 if (endDayOfWeek == 0) {
michael@0 940 endMode = DOM_MODE;
michael@0 941 } else {
michael@0 942 if (endDayOfWeek > 0) {
michael@0 943 endMode = DOW_IN_MONTH_MODE;
michael@0 944 } else {
michael@0 945 endDayOfWeek = (int8_t)-endDayOfWeek;
michael@0 946 if (endDay > 0) {
michael@0 947 endMode = DOW_GE_DOM_MODE;
michael@0 948 } else {
michael@0 949 endDay = (int8_t)-endDay;
michael@0 950 endMode = DOW_LE_DOM_MODE;
michael@0 951 }
michael@0 952 }
michael@0 953 if (endDayOfWeek > UCAL_SATURDAY) {
michael@0 954 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 955 return;
michael@0 956 }
michael@0 957 }
michael@0 958 if (endMode == DOW_IN_MONTH_MODE) {
michael@0 959 if (endDay < -5 || endDay > 5) {
michael@0 960 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 961 return;
michael@0 962 }
michael@0 963 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
michael@0 964 status = U_ILLEGAL_ARGUMENT_ERROR;
michael@0 965 return;
michael@0 966 }
michael@0 967 }
michael@0 968 }
michael@0 969
michael@0 970 UBool
michael@0 971 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
michael@0 972 if (!useDaylight) {
michael@0 973 return FALSE;
michael@0 974 }
michael@0 975
michael@0 976 UErrorCode status = U_ZERO_ERROR;
michael@0 977 checkTransitionRules(status);
michael@0 978 if (U_FAILURE(status)) {
michael@0 979 return FALSE;
michael@0 980 }
michael@0 981
michael@0 982 UDate firstTransitionTime = firstTransition->getTime();
michael@0 983 if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
michael@0 984 result = *firstTransition;
michael@0 985 }
michael@0 986 UDate stdDate, dstDate;
michael@0 987 UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
michael@0 988 UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
michael@0 989 if (stdAvail && (!dstAvail || stdDate < dstDate)) {
michael@0 990 result.setTime(stdDate);
michael@0 991 result.setFrom((const TimeZoneRule&)*dstRule);
michael@0 992 result.setTo((const TimeZoneRule&)*stdRule);
michael@0 993 return TRUE;
michael@0 994 }
michael@0 995 if (dstAvail && (!stdAvail || dstDate < stdDate)) {
michael@0 996 result.setTime(dstDate);
michael@0 997 result.setFrom((const TimeZoneRule&)*stdRule);
michael@0 998 result.setTo((const TimeZoneRule&)*dstRule);
michael@0 999 return TRUE;
michael@0 1000 }
michael@0 1001 return FALSE;
michael@0 1002 }
michael@0 1003
michael@0 1004 UBool
michael@0 1005 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
michael@0 1006 if (!useDaylight) {
michael@0 1007 return FALSE;
michael@0 1008 }
michael@0 1009
michael@0 1010 UErrorCode status = U_ZERO_ERROR;
michael@0 1011 checkTransitionRules(status);
michael@0 1012 if (U_FAILURE(status)) {
michael@0 1013 return FALSE;
michael@0 1014 }
michael@0 1015
michael@0 1016 UDate firstTransitionTime = firstTransition->getTime();
michael@0 1017 if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
michael@0 1018 return FALSE;
michael@0 1019 }
michael@0 1020 UDate stdDate, dstDate;
michael@0 1021 UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
michael@0 1022 UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
michael@0 1023 if (stdAvail && (!dstAvail || stdDate > dstDate)) {
michael@0 1024 result.setTime(stdDate);
michael@0 1025 result.setFrom((const TimeZoneRule&)*dstRule);
michael@0 1026 result.setTo((const TimeZoneRule&)*stdRule);
michael@0 1027 return TRUE;
michael@0 1028 }
michael@0 1029 if (dstAvail && (!stdAvail || dstDate > stdDate)) {
michael@0 1030 result.setTime(dstDate);
michael@0 1031 result.setFrom((const TimeZoneRule&)*stdRule);
michael@0 1032 result.setTo((const TimeZoneRule&)*dstRule);
michael@0 1033 return TRUE;
michael@0 1034 }
michael@0 1035 return FALSE;
michael@0 1036 }
michael@0 1037
michael@0 1038 void
michael@0 1039 SimpleTimeZone::clearTransitionRules(void) {
michael@0 1040 initialRule = NULL;
michael@0 1041 firstTransition = NULL;
michael@0 1042 stdRule = NULL;
michael@0 1043 dstRule = NULL;
michael@0 1044 transitionRulesInitialized = FALSE;
michael@0 1045 }
michael@0 1046
michael@0 1047 void
michael@0 1048 SimpleTimeZone::deleteTransitionRules(void) {
michael@0 1049 if (initialRule != NULL) {
michael@0 1050 delete initialRule;
michael@0 1051 }
michael@0 1052 if (firstTransition != NULL) {
michael@0 1053 delete firstTransition;
michael@0 1054 }
michael@0 1055 if (stdRule != NULL) {
michael@0 1056 delete stdRule;
michael@0 1057 }
michael@0 1058 if (dstRule != NULL) {
michael@0 1059 delete dstRule;
michael@0 1060 }
michael@0 1061 clearTransitionRules();
michael@0 1062 }
michael@0 1063
michael@0 1064 /*
michael@0 1065 * Lazy transition rules initializer
michael@0 1066 *
michael@0 1067 * Note On the removal of UMTX_CHECK from checkTransitionRules():
michael@0 1068 *
michael@0 1069 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
michael@0 1070 * which would avoid needing to lock a mutex to check the initialization state.
michael@0 1071 * But we can't easily because simpletz.h is a public header, and including
michael@0 1072 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
michael@0 1073 *
michael@0 1074 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
michael@0 1075 * allocate it in the constructors. This would be a more intrusive change, but doable
michael@0 1076 * if performance turns out to be an issue.
michael@0 1077 */
michael@0 1078 static UMutex gLock = U_MUTEX_INITIALIZER;
michael@0 1079
michael@0 1080 void
michael@0 1081 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
michael@0 1082 if (U_FAILURE(status)) {
michael@0 1083 return;
michael@0 1084 }
michael@0 1085 umtx_lock(&gLock);
michael@0 1086 if (!transitionRulesInitialized) {
michael@0 1087 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
michael@0 1088 ncThis->initTransitionRules(status);
michael@0 1089 }
michael@0 1090 umtx_unlock(&gLock);
michael@0 1091 }
michael@0 1092
michael@0 1093 void
michael@0 1094 SimpleTimeZone::initTransitionRules(UErrorCode& status) {
michael@0 1095 if (U_FAILURE(status)) {
michael@0 1096 return;
michael@0 1097 }
michael@0 1098 if (transitionRulesInitialized) {
michael@0 1099 return;
michael@0 1100 }
michael@0 1101 deleteTransitionRules();
michael@0 1102 UnicodeString tzid;
michael@0 1103 getID(tzid);
michael@0 1104
michael@0 1105 if (useDaylight) {
michael@0 1106 DateTimeRule* dtRule;
michael@0 1107 DateTimeRule::TimeRuleType timeRuleType;
michael@0 1108 UDate firstStdStart, firstDstStart;
michael@0 1109
michael@0 1110 // Create a TimeZoneRule for daylight saving time
michael@0 1111 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
michael@0 1112 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
michael@0 1113 switch (startMode) {
michael@0 1114 case DOM_MODE:
michael@0 1115 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
michael@0 1116 break;
michael@0 1117 case DOW_IN_MONTH_MODE:
michael@0 1118 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
michael@0 1119 break;
michael@0 1120 case DOW_GE_DOM_MODE:
michael@0 1121 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
michael@0 1122 break;
michael@0 1123 case DOW_LE_DOM_MODE:
michael@0 1124 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
michael@0 1125 break;
michael@0 1126 default:
michael@0 1127 status = U_INVALID_STATE_ERROR;
michael@0 1128 return;
michael@0 1129 }
michael@0 1130 // Check for Null pointer
michael@0 1131 if (dtRule == NULL) {
michael@0 1132 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1133 return;
michael@0 1134 }
michael@0 1135 // For now, use ID + "(DST)" as the name
michael@0 1136 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
michael@0 1137 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
michael@0 1138
michael@0 1139 // Check for Null pointer
michael@0 1140 if (dstRule == NULL) {
michael@0 1141 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1142 deleteTransitionRules();
michael@0 1143 return;
michael@0 1144 }
michael@0 1145
michael@0 1146 // Calculate the first DST start time
michael@0 1147 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
michael@0 1148
michael@0 1149 // Create a TimeZoneRule for standard time
michael@0 1150 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
michael@0 1151 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
michael@0 1152 switch (endMode) {
michael@0 1153 case DOM_MODE:
michael@0 1154 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
michael@0 1155 break;
michael@0 1156 case DOW_IN_MONTH_MODE:
michael@0 1157 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
michael@0 1158 break;
michael@0 1159 case DOW_GE_DOM_MODE:
michael@0 1160 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
michael@0 1161 break;
michael@0 1162 case DOW_LE_DOM_MODE:
michael@0 1163 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
michael@0 1164 break;
michael@0 1165 }
michael@0 1166
michael@0 1167 // Check for Null pointer
michael@0 1168 if (dtRule == NULL) {
michael@0 1169 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1170 deleteTransitionRules();
michael@0 1171 return;
michael@0 1172 }
michael@0 1173 // For now, use ID + "(STD)" as the name
michael@0 1174 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
michael@0 1175 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
michael@0 1176
michael@0 1177 //Check for Null pointer
michael@0 1178 if (stdRule == NULL) {
michael@0 1179 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1180 deleteTransitionRules();
michael@0 1181 return;
michael@0 1182 }
michael@0 1183
michael@0 1184 // Calculate the first STD start time
michael@0 1185 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
michael@0 1186
michael@0 1187 // Create a TimeZoneRule for initial time
michael@0 1188 if (firstStdStart < firstDstStart) {
michael@0 1189 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
michael@0 1190 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
michael@0 1191 } else {
michael@0 1192 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
michael@0 1193 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
michael@0 1194 }
michael@0 1195 // Check for null pointers.
michael@0 1196 if (initialRule == NULL || firstTransition == NULL) {
michael@0 1197 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1198 deleteTransitionRules();
michael@0 1199 return;
michael@0 1200 }
michael@0 1201
michael@0 1202 } else {
michael@0 1203 // Create a TimeZoneRule for initial time
michael@0 1204 initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
michael@0 1205 // Check for null pointer.
michael@0 1206 if (initialRule == NULL) {
michael@0 1207 status = U_MEMORY_ALLOCATION_ERROR;
michael@0 1208 deleteTransitionRules();
michael@0 1209 return;
michael@0 1210 }
michael@0 1211 }
michael@0 1212
michael@0 1213 transitionRulesInitialized = TRUE;
michael@0 1214 }
michael@0 1215
michael@0 1216 int32_t
michael@0 1217 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
michael@0 1218 return (useDaylight) ? 2 : 0;
michael@0 1219 }
michael@0 1220
michael@0 1221 void
michael@0 1222 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
michael@0 1223 const TimeZoneRule* trsrules[],
michael@0 1224 int32_t& trscount,
michael@0 1225 UErrorCode& status) const {
michael@0 1226 if (U_FAILURE(status)) {
michael@0 1227 return;
michael@0 1228 }
michael@0 1229 checkTransitionRules(status);
michael@0 1230 if (U_FAILURE(status)) {
michael@0 1231 return;
michael@0 1232 }
michael@0 1233 initial = initialRule;
michael@0 1234 int32_t cnt = 0;
michael@0 1235 if (stdRule != NULL) {
michael@0 1236 if (cnt < trscount) {
michael@0 1237 trsrules[cnt++] = stdRule;
michael@0 1238 }
michael@0 1239 if (cnt < trscount) {
michael@0 1240 trsrules[cnt++] = dstRule;
michael@0 1241 }
michael@0 1242 }
michael@0 1243 trscount = cnt;
michael@0 1244 }
michael@0 1245
michael@0 1246
michael@0 1247 U_NAMESPACE_END
michael@0 1248
michael@0 1249 #endif /* #if !UCONFIG_NO_FORMATTING */
michael@0 1250
michael@0 1251 //eof

mercurial