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