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-2013, International Business Machines Corporation and 6* others. All Rights Reserved. 7******************************************************************************* 8*/ 9 10#include "unicode/utypes.h" 11 12#if !UCONFIG_NO_FORMATTING 13 14#include "unicode/basictz.h" 15#include "gregoimp.h" 16#include "uvector.h" 17#include "cmemory.h" 18 19U_NAMESPACE_BEGIN 20 21#define MILLIS_PER_YEAR (365*24*60*60*1000.0) 22 23BasicTimeZone::BasicTimeZone() 24: TimeZone() { 25} 26 27BasicTimeZone::BasicTimeZone(const UnicodeString &id) 28: TimeZone(id) { 29} 30 31BasicTimeZone::BasicTimeZone(const BasicTimeZone& source) 32: TimeZone(source) { 33} 34 35BasicTimeZone::~BasicTimeZone() { 36} 37 38UBool 39BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end, 40 UBool ignoreDstAmount, UErrorCode& status) const { 41 if (U_FAILURE(status)) { 42 return FALSE; 43 } 44 if (hasSameRules(tz)) { 45 return TRUE; 46 } 47 // Check the offsets at the start time 48 int32_t raw1, raw2, dst1, dst2; 49 getOffset(start, FALSE, raw1, dst1, status); 50 if (U_FAILURE(status)) { 51 return FALSE; 52 } 53 tz.getOffset(start, FALSE, raw2, dst2, status); 54 if (U_FAILURE(status)) { 55 return FALSE; 56 } 57 if (ignoreDstAmount) { 58 if ((raw1 + dst1 != raw2 + dst2) 59 || (dst1 != 0 && dst2 == 0) 60 || (dst1 == 0 && dst2 != 0)) { 61 return FALSE; 62 } 63 } else { 64 if (raw1 != raw2 || dst1 != dst2) { 65 return FALSE; 66 } 67 } 68 // Check transitions in the range 69 UDate time = start; 70 TimeZoneTransition tr1, tr2; 71 while (TRUE) { 72 UBool avail1 = getNextTransition(time, FALSE, tr1); 73 UBool avail2 = tz.getNextTransition(time, FALSE, tr2); 74 75 if (ignoreDstAmount) { 76 // Skip a transition which only differ the amount of DST savings 77 while (TRUE) { 78 if (avail1 79 && tr1.getTime() <= end 80 && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings() 81 == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()) 82 && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) { 83 getNextTransition(tr1.getTime(), FALSE, tr1); 84 } else { 85 break; 86 } 87 } 88 while (TRUE) { 89 if (avail2 90 && tr2.getTime() <= end 91 && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings() 92 == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()) 93 && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) { 94 tz.getNextTransition(tr2.getTime(), FALSE, tr2); 95 } else { 96 break; 97 } 98 } 99 } 100 101 UBool inRange1 = (avail1 && tr1.getTime() <= end); 102 UBool inRange2 = (avail2 && tr2.getTime() <= end); 103 if (!inRange1 && !inRange2) { 104 // No more transition in the range 105 break; 106 } 107 if (!inRange1 || !inRange2) { 108 return FALSE; 109 } 110 if (tr1.getTime() != tr2.getTime()) { 111 return FALSE; 112 } 113 if (ignoreDstAmount) { 114 if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings() 115 != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings() 116 || (tr1.getTo()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() == 0) 117 || (tr1.getTo()->getDSTSavings() == 0 && tr2.getTo()->getDSTSavings() != 0)) { 118 return FALSE; 119 } 120 } else { 121 if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() || 122 tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) { 123 return FALSE; 124 } 125 } 126 time = tr1.getTime(); 127 } 128 return TRUE; 129} 130 131void 132BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, 133 AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const { 134 initial = NULL; 135 std = NULL; 136 dst = NULL; 137 if (U_FAILURE(status)) { 138 return; 139 } 140 int32_t initialRaw, initialDst; 141 UnicodeString initialName; 142 143 AnnualTimeZoneRule *ar1 = NULL; 144 AnnualTimeZoneRule *ar2 = NULL; 145 UnicodeString name; 146 147 UBool avail; 148 TimeZoneTransition tr; 149 // Get the next transition 150 avail = getNextTransition(date, FALSE, tr); 151 if (avail) { 152 tr.getFrom()->getName(initialName); 153 initialRaw = tr.getFrom()->getRawOffset(); 154 initialDst = tr.getFrom()->getDSTSavings(); 155 156 // Check if the next transition is either DST->STD or STD->DST and 157 // within roughly 1 year from the specified date 158 UDate nextTransitionTime = tr.getTime(); 159 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) 160 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) 161 && (date + MILLIS_PER_YEAR > nextTransitionTime)) { 162 163 int32_t year, month, dom, dow, doy, mid; 164 UDate d; 165 166 // Get local wall time for the next transition time 167 Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, 168 year, month, dom, dow, doy, mid); 169 int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 170 // Create DOW rule 171 DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 172 tr.getTo()->getName(name); 173 174 // Note: SimpleTimeZone does not support raw offset change. 175 // So we always use raw offset of the given time for the rule, 176 // even raw offset is changed. This will result that the result 177 // zone to return wrong offset after the transition. 178 // When we encounter such case, we do not inspect next next 179 // transition for another rule. 180 ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(), 181 dtr, year, AnnualTimeZoneRule::MAX_YEAR); 182 183 if (tr.getTo()->getRawOffset() == initialRaw) { 184 // Get the next next transition 185 avail = getNextTransition(nextTransitionTime, FALSE, tr); 186 if (avail) { 187 // Check if the next next transition is either DST->STD or STD->DST 188 // and within roughly 1 year from the next transition 189 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) 190 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) 191 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { 192 193 // Get local wall time for the next transition time 194 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), 195 year, month, dom, dow, doy, mid); 196 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 197 // Generate another DOW rule 198 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 199 tr.getTo()->getName(name); 200 ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(), 201 dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR); 202 203 // Make sure this rule can be applied to the specified date 204 avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d); 205 if (!avail || d > date 206 || initialRaw != tr.getTo()->getRawOffset() 207 || initialDst != tr.getTo()->getDSTSavings()) { 208 // We cannot use this rule as the second transition rule 209 delete ar2; 210 ar2 = NULL; 211 } 212 } 213 } 214 } 215 if (ar2 == NULL) { 216 // Try previous transition 217 avail = getPreviousTransition(date, TRUE, tr); 218 if (avail) { 219 // Check if the previous transition is either DST->STD or STD->DST. 220 // The actual transition time does not matter here. 221 if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) 222 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) { 223 224 // Generate another DOW rule 225 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), 226 year, month, dom, dow, doy, mid); 227 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 228 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 229 tr.getTo()->getName(name); 230 231 // second rule raw/dst offsets should match raw/dst offsets 232 // at the given time 233 ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst, 234 dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR); 235 236 // Check if this rule start after the first rule after the specified date 237 avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d); 238 if (!avail || d <= nextTransitionTime) { 239 // We cannot use this rule as the second transition rule 240 delete ar2; 241 ar2 = NULL; 242 } 243 } 244 } 245 } 246 if (ar2 == NULL) { 247 // Cannot find a good pair of AnnualTimeZoneRule 248 delete ar1; 249 ar1 = NULL; 250 } else { 251 // The initial rule should represent the rule before the previous transition 252 ar1->getName(initialName); 253 initialRaw = ar1->getRawOffset(); 254 initialDst = ar1->getDSTSavings(); 255 } 256 } 257 } 258 else { 259 // Try the previous one 260 avail = getPreviousTransition(date, TRUE, tr); 261 if (avail) { 262 tr.getTo()->getName(initialName); 263 initialRaw = tr.getTo()->getRawOffset(); 264 initialDst = tr.getTo()->getDSTSavings(); 265 } else { 266 // No transitions in the past. Just use the current offsets 267 getOffset(date, FALSE, initialRaw, initialDst, status); 268 if (U_FAILURE(status)) { 269 return; 270 } 271 } 272 } 273 // Set the initial rule 274 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst); 275 276 // Set the standard and daylight saving rules 277 if (ar1 != NULL && ar2 != NULL) { 278 if (ar1->getDSTSavings() != 0) { 279 dst = ar1; 280 std = ar2; 281 } else { 282 std = ar1; 283 dst = ar2; 284 } 285 } 286} 287 288void 289BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, 290 UVector*& transitionRules, UErrorCode& status) const { 291 if (U_FAILURE(status)) { 292 return; 293 } 294 295 const InitialTimeZoneRule *orgini; 296 const TimeZoneRule **orgtrs = NULL; 297 TimeZoneTransition tzt; 298 UBool avail; 299 UVector *orgRules = NULL; 300 int32_t ruleCount; 301 TimeZoneRule *r = NULL; 302 UBool *done = NULL; 303 InitialTimeZoneRule *res_initial = NULL; 304 UVector *filteredRules = NULL; 305 UnicodeString name; 306 int32_t i; 307 UDate time, t; 308 UDate *newTimes = NULL; 309 UDate firstStart; 310 UBool bFinalStd = FALSE, bFinalDst = FALSE; 311 312 // Original transition rules 313 ruleCount = countTransitionRules(status); 314 if (U_FAILURE(status)) { 315 return; 316 } 317 orgRules = new UVector(ruleCount, status); 318 if (U_FAILURE(status)) { 319 return; 320 } 321 orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount); 322 if (orgtrs == NULL) { 323 status = U_MEMORY_ALLOCATION_ERROR; 324 goto error; 325 } 326 getTimeZoneRules(orgini, orgtrs, ruleCount, status); 327 if (U_FAILURE(status)) { 328 goto error; 329 } 330 for (i = 0; i < ruleCount; i++) { 331 orgRules->addElement(orgtrs[i]->clone(), status); 332 if (U_FAILURE(status)) { 333 goto error; 334 } 335 } 336 uprv_free(orgtrs); 337 orgtrs = NULL; 338 339 avail = getPreviousTransition(start, TRUE, tzt); 340 if (!avail) { 341 // No need to filter out rules only applicable to time before the start 342 initial = orgini->clone(); 343 transitionRules = orgRules; 344 return; 345 } 346 347 done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount); 348 if (done == NULL) { 349 status = U_MEMORY_ALLOCATION_ERROR; 350 goto error; 351 } 352 filteredRules = new UVector(status); 353 if (U_FAILURE(status)) { 354 goto error; 355 } 356 357 // Create initial rule 358 tzt.getTo()->getName(name); 359 res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), 360 tzt.getTo()->getDSTSavings()); 361 362 // Mark rules which does not need to be processed 363 for (i = 0; i < ruleCount; i++) { 364 r = (TimeZoneRule*)orgRules->elementAt(i); 365 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time); 366 done[i] = !avail; 367 } 368 369 time = start; 370 while (!bFinalStd || !bFinalDst) { 371 avail = getNextTransition(time, FALSE, tzt); 372 if (!avail) { 373 break; 374 } 375 UDate updatedTime = tzt.getTime(); 376 if (updatedTime == time) { 377 // Can get here if rules for start & end of daylight time have exactly 378 // the same time. 379 // TODO: fix getNextTransition() to prevent it? 380 status = U_INVALID_STATE_ERROR; 381 goto error; 382 } 383 time = updatedTime; 384 385 const TimeZoneRule *toRule = tzt.getTo(); 386 for (i = 0; i < ruleCount; i++) { 387 r = (TimeZoneRule*)orgRules->elementAt(i); 388 if (*r == *toRule) { 389 break; 390 } 391 } 392 if (i >= ruleCount) { 393 // This case should never happen 394 status = U_INVALID_STATE_ERROR; 395 goto error; 396 } 397 if (done[i]) { 398 continue; 399 } 400 const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule); 401 const AnnualTimeZoneRule *ar; 402 if (tar != NULL) { 403 // Get the previous raw offset and DST savings before the very first start time 404 TimeZoneTransition tzt0; 405 t = start; 406 while (TRUE) { 407 avail = getNextTransition(t, FALSE, tzt0); 408 if (!avail) { 409 break; 410 } 411 if (*(tzt0.getTo()) == *tar) { 412 break; 413 } 414 t = tzt0.getTime(); 415 } 416 if (avail) { 417 // Check if the entire start times to be added 418 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); 419 if (firstStart > start) { 420 // Just add the rule as is 421 filteredRules->addElement(tar->clone(), status); 422 if (U_FAILURE(status)) { 423 goto error; 424 } 425 } else { 426 // Colllect transitions after the start time 427 int32_t startTimes; 428 DateTimeRule::TimeRuleType timeType; 429 int32_t idx; 430 431 startTimes = tar->countStartTimes(); 432 timeType = tar->getTimeType(); 433 for (idx = 0; idx < startTimes; idx++) { 434 tar->getStartTimeAt(idx, t); 435 if (timeType == DateTimeRule::STANDARD_TIME) { 436 t -= tzt.getFrom()->getRawOffset(); 437 } 438 if (timeType == DateTimeRule::WALL_TIME) { 439 t -= tzt.getFrom()->getDSTSavings(); 440 } 441 if (t > start) { 442 break; 443 } 444 } 445 int32_t asize = startTimes - idx; 446 if (asize > 0) { 447 newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize); 448 if (newTimes == NULL) { 449 status = U_MEMORY_ALLOCATION_ERROR; 450 goto error; 451 } 452 for (int32_t newidx = 0; newidx < asize; newidx++) { 453 tar->getStartTimeAt(idx + newidx, newTimes[newidx]); 454 if (U_FAILURE(status)) { 455 uprv_free(newTimes); 456 newTimes = NULL; 457 goto error; 458 } 459 } 460 tar->getName(name); 461 TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name, 462 tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType); 463 uprv_free(newTimes); 464 filteredRules->addElement(newTar, status); 465 if (U_FAILURE(status)) { 466 goto error; 467 } 468 } 469 } 470 } 471 } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) { 472 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); 473 if (firstStart == tzt.getTime()) { 474 // Just add the rule as is 475 filteredRules->addElement(ar->clone(), status); 476 if (U_FAILURE(status)) { 477 goto error; 478 } 479 } else { 480 // Calculate the transition year 481 int32_t year, month, dom, dow, doy, mid; 482 Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid); 483 // Re-create the rule 484 ar->getName(name); 485 AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), 486 *(ar->getRule()), year, ar->getEndYear()); 487 filteredRules->addElement(newAr, status); 488 if (U_FAILURE(status)) { 489 goto error; 490 } 491 } 492 // check if this is a final rule 493 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { 494 // After bot final standard and dst rules are processed, 495 // exit this while loop. 496 if (ar->getDSTSavings() == 0) { 497 bFinalStd = TRUE; 498 } else { 499 bFinalDst = TRUE; 500 } 501 } 502 } 503 done[i] = TRUE; 504 } 505 506 // Set the results 507 if (orgRules != NULL) { 508 while (!orgRules->isEmpty()) { 509 r = (TimeZoneRule*)orgRules->orphanElementAt(0); 510 delete r; 511 } 512 delete orgRules; 513 } 514 if (done != NULL) { 515 uprv_free(done); 516 } 517 518 initial = res_initial; 519 transitionRules = filteredRules; 520 return; 521 522error: 523 if (orgtrs != NULL) { 524 uprv_free(orgtrs); 525 } 526 if (orgRules != NULL) { 527 while (!orgRules->isEmpty()) { 528 r = (TimeZoneRule*)orgRules->orphanElementAt(0); 529 delete r; 530 } 531 delete orgRules; 532 } 533 if (done != NULL) { 534 if (filteredRules != NULL) { 535 while (!filteredRules->isEmpty()) { 536 r = (TimeZoneRule*)filteredRules->orphanElementAt(0); 537 delete r; 538 } 539 delete filteredRules; 540 } 541 delete res_initial; 542 uprv_free(done); 543 } 544 545 initial = NULL; 546 transitionRules = NULL; 547} 548 549void 550BasicTimeZone::getOffsetFromLocal(UDate /*date*/, int32_t /*nonExistingTimeOpt*/, int32_t /*duplicatedTimeOpt*/, 551 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, UErrorCode& status) const { 552 if (U_FAILURE(status)) { 553 return; 554 } 555 status = U_UNSUPPORTED_ERROR; 556} 557 558U_NAMESPACE_END 559 560#endif /* #if !UCONFIG_NO_FORMATTING */ 561 562//eof 563