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