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 37U_NAMESPACE_BEGIN 38 39UOBJECT_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. 51const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 52 53static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)" 54static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)" 55 56 57// ***************************************************************************** 58// class SimpleTimeZone 59// ***************************************************************************** 60 61 62SimpleTimeZone::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 86SimpleTimeZone::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 105SimpleTimeZone::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 124SimpleTimeZone::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 */ 146void 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 185SimpleTimeZone::~SimpleTimeZone() 186{ 187 deleteTransitionRules(); 188} 189 190// ------------------------------------- 191 192// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. 193SimpleTimeZone::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. 202SimpleTimeZone & 203SimpleTimeZone::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 231UBool 232SimpleTimeZone::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. 243TimeZone* 244SimpleTimeZone::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 */ 258void 259SimpleTimeZone::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 306void 307SimpleTimeZone::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 321void 322SimpleTimeZone::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 330void 331SimpleTimeZone::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 358void 359SimpleTimeZone::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 373void 374SimpleTimeZone::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 382void 383SimpleTimeZone::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 392int32_t 393SimpleTimeZone::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 411int32_t 412SimpleTimeZone::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 438int32_t 439SimpleTimeZone::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 508void 509SimpleTimeZone::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 */ 568int32_t 569SimpleTimeZone::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 667int32_t 668SimpleTimeZone::getRawOffset() const 669{ 670 return rawOffset; 671} 672 673// ------------------------------------- 674 675void 676SimpleTimeZone::setRawOffset(int32_t offsetMillis) 677{ 678 rawOffset = offsetMillis; 679 transitionRulesInitialized = FALSE; 680} 681 682// ------------------------------------- 683 684void 685SimpleTimeZone::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 698int32_t 699SimpleTimeZone::getDSTSavings() const 700{ 701 return dstSavings; 702} 703 704// ------------------------------------- 705 706UBool 707SimpleTimeZone::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 */ 718UBool 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 */ 743UBool 744SimpleTimeZone::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 */ 834void 835SimpleTimeZone::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 */ 865void 866SimpleTimeZone::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 */ 920void 921SimpleTimeZone::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 970UBool 971SimpleTimeZone::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 1004UBool 1005SimpleTimeZone::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 1038void 1039SimpleTimeZone::clearTransitionRules(void) { 1040 initialRule = NULL; 1041 firstTransition = NULL; 1042 stdRule = NULL; 1043 dstRule = NULL; 1044 transitionRulesInitialized = FALSE; 1045} 1046 1047void 1048SimpleTimeZone::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 * Note On the removal of UMTX_CHECK from checkTransitionRules(): 1068 * 1069 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object, 1070 * which would avoid needing to lock a mutex to check the initialization state. 1071 * But we can't easily because simpletz.h is a public header, and including 1072 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers. 1073 * 1074 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object, 1075 * allocate it in the constructors. This would be a more intrusive change, but doable 1076 * if performance turns out to be an issue. 1077 */ 1078static UMutex gLock = U_MUTEX_INITIALIZER; 1079 1080void 1081SimpleTimeZone::checkTransitionRules(UErrorCode& status) const { 1082 if (U_FAILURE(status)) { 1083 return; 1084 } 1085 umtx_lock(&gLock); 1086 if (!transitionRulesInitialized) { 1087 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this); 1088 ncThis->initTransitionRules(status); 1089 } 1090 umtx_unlock(&gLock); 1091} 1092 1093void 1094SimpleTimeZone::initTransitionRules(UErrorCode& status) { 1095 if (U_FAILURE(status)) { 1096 return; 1097 } 1098 if (transitionRulesInitialized) { 1099 return; 1100 } 1101 deleteTransitionRules(); 1102 UnicodeString tzid; 1103 getID(tzid); 1104 1105 if (useDaylight) { 1106 DateTimeRule* dtRule; 1107 DateTimeRule::TimeRuleType timeRuleType; 1108 UDate firstStdStart, firstDstStart; 1109 1110 // Create a TimeZoneRule for daylight saving time 1111 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : 1112 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); 1113 switch (startMode) { 1114 case DOM_MODE: 1115 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); 1116 break; 1117 case DOW_IN_MONTH_MODE: 1118 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType); 1119 break; 1120 case DOW_GE_DOM_MODE: 1121 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType); 1122 break; 1123 case DOW_LE_DOM_MODE: 1124 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType); 1125 break; 1126 default: 1127 status = U_INVALID_STATE_ERROR; 1128 return; 1129 } 1130 // Check for Null pointer 1131 if (dtRule == NULL) { 1132 status = U_MEMORY_ALLOCATION_ERROR; 1133 return; 1134 } 1135 // For now, use ID + "(DST)" as the name 1136 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(), 1137 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); 1138 1139 // Check for Null pointer 1140 if (dstRule == NULL) { 1141 status = U_MEMORY_ALLOCATION_ERROR; 1142 deleteTransitionRules(); 1143 return; 1144 } 1145 1146 // Calculate the first DST start time 1147 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart); 1148 1149 // Create a TimeZoneRule for standard time 1150 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : 1151 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); 1152 switch (endMode) { 1153 case DOM_MODE: 1154 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); 1155 break; 1156 case DOW_IN_MONTH_MODE: 1157 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); 1158 break; 1159 case DOW_GE_DOM_MODE: 1160 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType); 1161 break; 1162 case DOW_LE_DOM_MODE: 1163 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType); 1164 break; 1165 } 1166 1167 // Check for Null pointer 1168 if (dtRule == NULL) { 1169 status = U_MEMORY_ALLOCATION_ERROR; 1170 deleteTransitionRules(); 1171 return; 1172 } 1173 // For now, use ID + "(STD)" as the name 1174 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0, 1175 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); 1176 1177 //Check for Null pointer 1178 if (stdRule == NULL) { 1179 status = U_MEMORY_ALLOCATION_ERROR; 1180 deleteTransitionRules(); 1181 return; 1182 } 1183 1184 // Calculate the first STD start time 1185 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart); 1186 1187 // Create a TimeZoneRule for initial time 1188 if (firstStdStart < firstDstStart) { 1189 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings()); 1190 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule); 1191 } else { 1192 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0); 1193 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule); 1194 } 1195 // Check for null pointers. 1196 if (initialRule == NULL || firstTransition == NULL) { 1197 status = U_MEMORY_ALLOCATION_ERROR; 1198 deleteTransitionRules(); 1199 return; 1200 } 1201 1202 } else { 1203 // Create a TimeZoneRule for initial time 1204 initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0); 1205 // Check for null pointer. 1206 if (initialRule == NULL) { 1207 status = U_MEMORY_ALLOCATION_ERROR; 1208 deleteTransitionRules(); 1209 return; 1210 } 1211 } 1212 1213 transitionRulesInitialized = TRUE; 1214} 1215 1216int32_t 1217SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const { 1218 return (useDaylight) ? 2 : 0; 1219} 1220 1221void 1222SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, 1223 const TimeZoneRule* trsrules[], 1224 int32_t& trscount, 1225 UErrorCode& status) const { 1226 if (U_FAILURE(status)) { 1227 return; 1228 } 1229 checkTransitionRules(status); 1230 if (U_FAILURE(status)) { 1231 return; 1232 } 1233 initial = initialRule; 1234 int32_t cnt = 0; 1235 if (stdRule != NULL) { 1236 if (cnt < trscount) { 1237 trsrules[cnt++] = stdRule; 1238 } 1239 if (cnt < trscount) { 1240 trsrules[cnt++] = dstRule; 1241 } 1242 } 1243 trscount = cnt; 1244} 1245 1246 1247U_NAMESPACE_END 1248 1249#endif /* #if !UCONFIG_NO_FORMATTING */ 1250 1251//eof 1252