1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 #include "unicode/utypes.h" 9 10 #if !UCONFIG_NO_FORMATTING 11 12 #include "unicode/tzrule.h" 13 #include "unicode/ucal.h" 14 #include "gregoimp.h" 15 #include "cmemory.h" 16 #include "uarrsort.h" 17 18 U_CDECL_BEGIN 19 // UComparator function for sorting start times 20 static int32_t U_CALLCONV 21 compareDates(const void * /*context*/, const void *left, const void *right) { 22 UDate l = *((UDate*)left); 23 UDate r = *((UDate*)right); 24 int32_t res = l < r ? -1 : (l == r ? 0 : 1); 25 return res; 26 } 27 U_CDECL_END 28 29 U_NAMESPACE_BEGIN 30 31 TimeZoneRule::TimeZoneRule(const UnicodeString& name, int32_t rawOffset, int32_t dstSavings) 32 : UObject(), fName(name), fRawOffset(rawOffset), fDSTSavings(dstSavings) { 33 } 34 35 TimeZoneRule::TimeZoneRule(const TimeZoneRule& source) 36 : UObject(source), fName(source.fName), fRawOffset(source.fRawOffset), fDSTSavings(source.fDSTSavings) { 37 } 38 39 TimeZoneRule::~TimeZoneRule() { 40 } 41 42 TimeZoneRule& 43 TimeZoneRule::operator=(const TimeZoneRule& right) { 44 if (this != &right) { 45 fName = right.fName; 46 fRawOffset = right.fRawOffset; 47 fDSTSavings = right.fDSTSavings; 48 } 49 return *this; 50 } 51 52 UBool 53 TimeZoneRule::operator==(const TimeZoneRule& that) const { 54 return ((this == &that) || 55 (getDynamicClassID() == that.getDynamicClassID() && 56 fName == that.fName && 57 fRawOffset == that.fRawOffset && 58 fDSTSavings == that.fDSTSavings)); 59 } 60 61 UBool 62 TimeZoneRule::operator!=(const TimeZoneRule& that) const { 63 return !operator==(that); 64 } 65 66 UnicodeString& 67 TimeZoneRule::getName(UnicodeString& name) const { 68 name = fName; 69 return name; 70 } 71 72 int32_t 73 TimeZoneRule::getRawOffset(void) const { 74 return fRawOffset; 75 } 76 77 int32_t 78 TimeZoneRule::getDSTSavings(void) const { 79 return fDSTSavings; 80 } 81 82 UBool 83 TimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { 84 return ((this == &other) || 85 (getDynamicClassID() == other.getDynamicClassID() && 86 fRawOffset == other.fRawOffset && 87 fDSTSavings == other.fDSTSavings)); 88 } 89 90 91 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(InitialTimeZoneRule) 92 93 InitialTimeZoneRule::InitialTimeZoneRule(const UnicodeString& name, 94 int32_t rawOffset, 95 int32_t dstSavings) 96 : TimeZoneRule(name, rawOffset, dstSavings) { 97 } 98 99 InitialTimeZoneRule::InitialTimeZoneRule(const InitialTimeZoneRule& source) 100 : TimeZoneRule(source) { 101 } 102 103 InitialTimeZoneRule::~InitialTimeZoneRule() { 104 } 105 106 InitialTimeZoneRule* 107 InitialTimeZoneRule::clone(void) const { 108 return new InitialTimeZoneRule(*this); 109 } 110 111 InitialTimeZoneRule& 112 InitialTimeZoneRule::operator=(const InitialTimeZoneRule& right) { 113 if (this != &right) { 114 TimeZoneRule::operator=(right); 115 } 116 return *this; 117 } 118 119 UBool 120 InitialTimeZoneRule::operator==(const TimeZoneRule& that) const { 121 return ((this == &that) || 122 (getDynamicClassID() == that.getDynamicClassID() && 123 TimeZoneRule::operator==(that))); 124 } 125 126 UBool 127 InitialTimeZoneRule::operator!=(const TimeZoneRule& that) const { 128 return !operator==(that); 129 } 130 131 UBool 132 InitialTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { 133 if (this == &other) { 134 return TRUE; 135 } 136 if (getDynamicClassID() != other.getDynamicClassID() || 137 TimeZoneRule::isEquivalentTo(other) == FALSE) { 138 return FALSE; 139 } 140 return TRUE; 141 } 142 143 UBool 144 InitialTimeZoneRule::getFirstStart(int32_t /*prevRawOffset*/, 145 int32_t /*prevDSTSavings*/, 146 UDate& /*result*/) const { 147 return FALSE; 148 } 149 150 UBool 151 InitialTimeZoneRule::getFinalStart(int32_t /*prevRawOffset*/, 152 int32_t /*prevDSTSavings*/, 153 UDate& /*result*/) const { 154 return FALSE; 155 } 156 157 UBool 158 InitialTimeZoneRule::getNextStart(const UDate /*base*/, 159 int32_t /*prevRawOffset*/, 160 int32_t /*prevDSTSavings*/, 161 UBool /*inclusive*/, 162 UDate& /*result*/) const { 163 return FALSE; 164 } 165 166 UBool 167 InitialTimeZoneRule::getPreviousStart(const UDate /*base*/, 168 int32_t /*prevRawOffset*/, 169 int32_t /*prevDSTSavings*/, 170 UBool /*inclusive*/, 171 UDate& /*result*/) const { 172 return FALSE; 173 } 174 175 176 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnnualTimeZoneRule) 177 178 const int32_t AnnualTimeZoneRule::MAX_YEAR = 0x7FFFFFFF; /* max signed int32 */ 179 180 AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name, 181 int32_t rawOffset, 182 int32_t dstSavings, 183 const DateTimeRule& dateTimeRule, 184 int32_t startYear, 185 int32_t endYear) 186 : TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(new DateTimeRule(dateTimeRule)), 187 fStartYear(startYear), fEndYear(endYear) { 188 } 189 190 AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name, 191 int32_t rawOffset, 192 int32_t dstSavings, 193 DateTimeRule* dateTimeRule, 194 int32_t startYear, 195 int32_t endYear) 196 : TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(dateTimeRule), 197 fStartYear(startYear), fEndYear(endYear) { 198 } 199 200 AnnualTimeZoneRule::AnnualTimeZoneRule(const AnnualTimeZoneRule& source) 201 : TimeZoneRule(source), fDateTimeRule(new DateTimeRule(*(source.fDateTimeRule))), 202 fStartYear(source.fStartYear), fEndYear(source.fEndYear) { 203 } 204 205 AnnualTimeZoneRule::~AnnualTimeZoneRule() { 206 delete fDateTimeRule; 207 } 208 209 AnnualTimeZoneRule* 210 AnnualTimeZoneRule::clone(void) const { 211 return new AnnualTimeZoneRule(*this); 212 } 213 214 AnnualTimeZoneRule& 215 AnnualTimeZoneRule::operator=(const AnnualTimeZoneRule& right) { 216 if (this != &right) { 217 TimeZoneRule::operator=(right); 218 delete fDateTimeRule; 219 fDateTimeRule = right.fDateTimeRule->clone(); 220 fStartYear = right.fStartYear; 221 fEndYear = right.fEndYear; 222 } 223 return *this; 224 } 225 226 UBool 227 AnnualTimeZoneRule::operator==(const TimeZoneRule& that) const { 228 if (this == &that) { 229 return TRUE; 230 } 231 if (getDynamicClassID() != that.getDynamicClassID()) { 232 return FALSE; 233 } 234 AnnualTimeZoneRule *atzr = (AnnualTimeZoneRule*)&that; 235 return (*fDateTimeRule == *(atzr->fDateTimeRule) && 236 fStartYear == atzr->fStartYear && 237 fEndYear == atzr->fEndYear); 238 } 239 240 UBool 241 AnnualTimeZoneRule::operator!=(const TimeZoneRule& that) const { 242 return !operator==(that); 243 } 244 245 const DateTimeRule* 246 AnnualTimeZoneRule::getRule() const { 247 return fDateTimeRule; 248 } 249 250 int32_t 251 AnnualTimeZoneRule::getStartYear() const { 252 return fStartYear; 253 } 254 255 int32_t 256 AnnualTimeZoneRule::getEndYear() const { 257 return fEndYear; 258 } 259 260 UBool 261 AnnualTimeZoneRule::getStartInYear(int32_t year, 262 int32_t prevRawOffset, 263 int32_t prevDSTSavings, 264 UDate &result) const { 265 if (year < fStartYear || year > fEndYear) { 266 return FALSE; 267 } 268 double ruleDay; 269 DateTimeRule::DateRuleType type = fDateTimeRule->getDateRuleType(); 270 if (type == DateTimeRule::DOM) { 271 ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), fDateTimeRule->getRuleDayOfMonth()); 272 } else { 273 UBool after = TRUE; 274 if (type == DateTimeRule::DOW) { 275 // Normalize DOW rule into DOW_GEQ_DOM or DOW_LEQ_DOM 276 int32_t weeks = fDateTimeRule->getRuleWeekInMonth(); 277 if (weeks > 0) { 278 ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), 1); 279 ruleDay += 7 * (weeks - 1); 280 } else { 281 after = FALSE; 282 ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), 283 Grego::monthLength(year, fDateTimeRule->getRuleMonth())); 284 ruleDay += 7 * (weeks + 1); 285 } 286 } else { 287 int32_t month = fDateTimeRule->getRuleMonth(); 288 int32_t dom = fDateTimeRule->getRuleDayOfMonth(); 289 if (type == DateTimeRule::DOW_LEQ_DOM) { 290 after = FALSE; 291 // Handle Feb <=29 292 if (month == UCAL_FEBRUARY && dom == 29 && !Grego::isLeapYear(year)) { 293 dom--; 294 } 295 } 296 ruleDay = Grego::fieldsToDay(year, month, dom); 297 } 298 int32_t dow = Grego::dayOfWeek(ruleDay); 299 int32_t delta = fDateTimeRule->getRuleDayOfWeek() - dow; 300 if (after) { 301 delta = delta < 0 ? delta + 7 : delta; 302 } else { 303 delta = delta > 0 ? delta - 7 : delta; 304 } 305 ruleDay += delta; 306 } 307 308 result = ruleDay*U_MILLIS_PER_DAY + fDateTimeRule->getRuleMillisInDay(); 309 if (fDateTimeRule->getTimeRuleType() != DateTimeRule::UTC_TIME) { 310 result -= prevRawOffset; 311 } 312 if (fDateTimeRule->getTimeRuleType() == DateTimeRule::WALL_TIME) { 313 result -= prevDSTSavings; 314 } 315 return TRUE; 316 } 317 318 UBool 319 AnnualTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { 320 if (this == &other) { 321 return TRUE; 322 } 323 if (getDynamicClassID() != other.getDynamicClassID() || 324 TimeZoneRule::isEquivalentTo(other) == FALSE) { 325 return FALSE; 326 } 327 AnnualTimeZoneRule* that = (AnnualTimeZoneRule*)&other; 328 return (*fDateTimeRule == *(that->fDateTimeRule) && 329 fStartYear == that->fStartYear && 330 fEndYear == that->fEndYear); 331 } 332 333 UBool 334 AnnualTimeZoneRule::getFirstStart(int32_t prevRawOffset, 335 int32_t prevDSTSavings, 336 UDate& result) const { 337 return getStartInYear(fStartYear, prevRawOffset, prevDSTSavings, result); 338 } 339 340 UBool 341 AnnualTimeZoneRule::getFinalStart(int32_t prevRawOffset, 342 int32_t prevDSTSavings, 343 UDate& result) const { 344 if (fEndYear == MAX_YEAR) { 345 return FALSE; 346 } 347 return getStartInYear(fEndYear, prevRawOffset, prevDSTSavings, result); 348 } 349 350 UBool 351 AnnualTimeZoneRule::getNextStart(const UDate base, 352 int32_t prevRawOffset, 353 int32_t prevDSTSavings, 354 UBool inclusive, 355 UDate& result) const { 356 int32_t year, month, dom, dow, doy, mid; 357 Grego::timeToFields(base, year, month, dom, dow, doy, mid); 358 if (year < fStartYear) { 359 return getFirstStart(prevRawOffset, prevDSTSavings, result); 360 } 361 UDate tmp; 362 if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) { 363 if (tmp < base || (!inclusive && (tmp == base))) { 364 // Return the next one 365 return getStartInYear(year + 1, prevRawOffset, prevDSTSavings, result); 366 } else { 367 result = tmp; 368 return TRUE; 369 } 370 } 371 return FALSE; 372 } 373 374 UBool 375 AnnualTimeZoneRule::getPreviousStart(const UDate base, 376 int32_t prevRawOffset, 377 int32_t prevDSTSavings, 378 UBool inclusive, 379 UDate& result) const { 380 int32_t year, month, dom, dow, doy, mid; 381 Grego::timeToFields(base, year, month, dom, dow, doy, mid); 382 if (year > fEndYear) { 383 return getFinalStart(prevRawOffset, prevDSTSavings, result); 384 } 385 UDate tmp; 386 if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) { 387 if (tmp > base || (!inclusive && (tmp == base))) { 388 // Return the previous one 389 return getStartInYear(year - 1, prevRawOffset, prevDSTSavings, result); 390 } else { 391 result = tmp; 392 return TRUE; 393 } 394 } 395 return FALSE; 396 } 397 398 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeArrayTimeZoneRule) 399 400 TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const UnicodeString& name, 401 int32_t rawOffset, 402 int32_t dstSavings, 403 const UDate* startTimes, 404 int32_t numStartTimes, 405 DateTimeRule::TimeRuleType timeRuleType) 406 : TimeZoneRule(name, rawOffset, dstSavings), fTimeRuleType(timeRuleType), 407 fStartTimes(NULL) { 408 UErrorCode status = U_ZERO_ERROR; 409 initStartTimes(startTimes, numStartTimes, status); 410 //TODO - status? 411 } 412 413 414 TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const TimeArrayTimeZoneRule& source) 415 : TimeZoneRule(source), fTimeRuleType(source.fTimeRuleType), fStartTimes(NULL) { 416 UErrorCode status = U_ZERO_ERROR; 417 initStartTimes(source.fStartTimes, source.fNumStartTimes, status); 418 //TODO - status? 419 } 420 421 422 TimeArrayTimeZoneRule::~TimeArrayTimeZoneRule() { 423 if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { 424 uprv_free(fStartTimes); 425 } 426 } 427 428 TimeArrayTimeZoneRule* 429 TimeArrayTimeZoneRule::clone(void) const { 430 return new TimeArrayTimeZoneRule(*this); 431 } 432 433 434 TimeArrayTimeZoneRule& 435 TimeArrayTimeZoneRule::operator=(const TimeArrayTimeZoneRule& right) { 436 if (this != &right) { 437 TimeZoneRule::operator=(right); 438 UErrorCode status = U_ZERO_ERROR; 439 initStartTimes(right.fStartTimes, right.fNumStartTimes, status); 440 //TODO - status? 441 fTimeRuleType = right.fTimeRuleType; 442 } 443 return *this; 444 } 445 446 UBool 447 TimeArrayTimeZoneRule::operator==(const TimeZoneRule& that) const { 448 if (this == &that) { 449 return TRUE; 450 } 451 if (getDynamicClassID() != that.getDynamicClassID() 452 || TimeZoneRule::operator==(that) == FALSE) { 453 return FALSE; 454 } 455 TimeArrayTimeZoneRule *tatzr = (TimeArrayTimeZoneRule*)&that; 456 if (fTimeRuleType != tatzr->fTimeRuleType || 457 fNumStartTimes != tatzr->fNumStartTimes) { 458 return FALSE; 459 } 460 // Compare start times 461 UBool res = TRUE; 462 for (int32_t i = 0; i < fNumStartTimes; i++) { 463 if (fStartTimes[i] != tatzr->fStartTimes[i]) { 464 res = FALSE; 465 break; 466 } 467 } 468 return res; 469 } 470 471 UBool 472 TimeArrayTimeZoneRule::operator!=(const TimeZoneRule& that) const { 473 return !operator==(that); 474 } 475 476 DateTimeRule::TimeRuleType 477 TimeArrayTimeZoneRule::getTimeType(void) const { 478 return fTimeRuleType; 479 } 480 481 UBool 482 TimeArrayTimeZoneRule::getStartTimeAt(int32_t index, UDate& result) const { 483 if (index >= fNumStartTimes || index < 0) { 484 return FALSE; 485 } 486 result = fStartTimes[index]; 487 return TRUE; 488 } 489 490 int32_t 491 TimeArrayTimeZoneRule::countStartTimes(void) const { 492 return fNumStartTimes; 493 } 494 495 UBool 496 TimeArrayTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { 497 if (this == &other) { 498 return TRUE; 499 } 500 if (getDynamicClassID() != other.getDynamicClassID() 501 || TimeZoneRule::isEquivalentTo(other) == FALSE) { 502 return FALSE; 503 } 504 TimeArrayTimeZoneRule* that = (TimeArrayTimeZoneRule*)&other; 505 if (fTimeRuleType != that->fTimeRuleType || 506 fNumStartTimes != that->fNumStartTimes) { 507 return FALSE; 508 } 509 // Compare start times 510 UBool res = TRUE; 511 for (int32_t i = 0; i < fNumStartTimes; i++) { 512 if (fStartTimes[i] != that->fStartTimes[i]) { 513 res = FALSE; 514 break; 515 } 516 } 517 return res; 518 } 519 520 UBool 521 TimeArrayTimeZoneRule::getFirstStart(int32_t prevRawOffset, 522 int32_t prevDSTSavings, 523 UDate& result) const { 524 if (fNumStartTimes <= 0 || fStartTimes == NULL) { 525 return FALSE; 526 } 527 result = getUTC(fStartTimes[0], prevRawOffset, prevDSTSavings); 528 return TRUE; 529 } 530 531 UBool 532 TimeArrayTimeZoneRule::getFinalStart(int32_t prevRawOffset, 533 int32_t prevDSTSavings, 534 UDate& result) const { 535 if (fNumStartTimes <= 0 || fStartTimes == NULL) { 536 return FALSE; 537 } 538 result = getUTC(fStartTimes[fNumStartTimes - 1], prevRawOffset, prevDSTSavings); 539 return TRUE; 540 } 541 542 UBool 543 TimeArrayTimeZoneRule::getNextStart(const UDate base, 544 int32_t prevRawOffset, 545 int32_t prevDSTSavings, 546 UBool inclusive, 547 UDate& result) const { 548 int32_t i = fNumStartTimes - 1; 549 for (; i >= 0; i--) { 550 UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings); 551 if (time < base || (!inclusive && time == base)) { 552 break; 553 } 554 result = time; 555 } 556 if (i == fNumStartTimes - 1) { 557 return FALSE; 558 } 559 return TRUE; 560 } 561 562 UBool 563 TimeArrayTimeZoneRule::getPreviousStart(const UDate base, 564 int32_t prevRawOffset, 565 int32_t prevDSTSavings, 566 UBool inclusive, 567 UDate& result) const { 568 int32_t i = fNumStartTimes - 1; 569 for (; i >= 0; i--) { 570 UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings); 571 if (time < base || (inclusive && time == base)) { 572 result = time; 573 return TRUE; 574 } 575 } 576 return FALSE; 577 } 578 579 580 // ---- private methods ------ 581 582 UBool 583 TimeArrayTimeZoneRule::initStartTimes(const UDate source[], int32_t size, UErrorCode& status) { 584 // Free old array 585 if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { 586 uprv_free(fStartTimes); 587 } 588 // Allocate new one if needed 589 if (size > TIMEARRAY_STACK_BUFFER_SIZE) { 590 fStartTimes = (UDate*)uprv_malloc(sizeof(UDate)*size); 591 if (fStartTimes == NULL) { 592 status = U_MEMORY_ALLOCATION_ERROR; 593 fNumStartTimes = 0; 594 return FALSE; 595 } 596 } else { 597 fStartTimes = (UDate*)fLocalStartTimes; 598 } 599 uprv_memcpy(fStartTimes, source, sizeof(UDate)*size); 600 fNumStartTimes = size; 601 // Sort dates 602 uprv_sortArray(fStartTimes, fNumStartTimes, (int32_t)sizeof(UDate), compareDates, NULL, TRUE, &status); 603 if (U_FAILURE(status)) { 604 if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { 605 uprv_free(fStartTimes); 606 } 607 fNumStartTimes = 0; 608 return FALSE; 609 } 610 return TRUE; 611 } 612 613 UDate 614 TimeArrayTimeZoneRule::getUTC(UDate time, int32_t raw, int32_t dst) const { 615 if (fTimeRuleType != DateTimeRule::UTC_TIME) { 616 time -= raw; 617 } 618 if (fTimeRuleType == DateTimeRule::WALL_TIME) { 619 time -= dst; 620 } 621 return time; 622 } 623 624 U_NAMESPACE_END 625 626 #endif /* #if !UCONFIG_NO_FORMATTING */ 627 628 //eof 629 630