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