basictz.cpp (21483B)
1 // © 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 19 U_NAMESPACE_BEGIN 20 21 #define MILLIS_PER_YEAR (365*24*60*60*1000.0) 22 23 BasicTimeZone::BasicTimeZone() 24 : TimeZone() { 25 } 26 27 BasicTimeZone::BasicTimeZone(const UnicodeString &id) 28 : TimeZone(id) { 29 } 30 31 BasicTimeZone::BasicTimeZone(const BasicTimeZone& source) 32 : TimeZone(source) { 33 } 34 35 BasicTimeZone::~BasicTimeZone() { 36 } 37 38 UBool 39 BasicTimeZone::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 131 void 132 BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, 133 AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const { 134 initial = nullptr; 135 std = nullptr; 136 dst = nullptr; 137 if (U_FAILURE(status)) { 138 return; 139 } 140 int32_t initialRaw, initialDst; 141 UnicodeString initialName; 142 143 LocalPointer<AnnualTimeZoneRule> ar1; 144 LocalPointer<AnnualTimeZoneRule> ar2; 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, mid; 164 int8_t month, dom, dow; 165 UDate d; 166 167 // Get local wall time for the next transition time 168 Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, 169 year, month, dom, dow, mid, status); 170 if (U_FAILURE(status)) return; 171 int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 172 // Create DOW rule 173 DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 174 tr.getTo()->getName(name); 175 176 // Note: SimpleTimeZone does not support raw offset change. 177 // So we always use raw offset of the given time for the rule, 178 // even raw offset is changed. This will result that the result 179 // zone to return wrong offset after the transition. 180 // When we encounter such case, we do not inspect next next 181 // transition for another rule. 182 ar1.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(), 183 dtr, year, AnnualTimeZoneRule::MAX_YEAR)); 184 185 if (tr.getTo()->getRawOffset() == initialRaw) { 186 // Get the next next transition 187 avail = getNextTransition(nextTransitionTime, false, tr); 188 if (avail) { 189 // Check if the next next transition is either DST->STD or STD->DST 190 // and within roughly 1 year from the next transition 191 if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) 192 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) 193 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { 194 195 // Get local wall time for the next transition time 196 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), 197 year, month, dom, dow, mid, status); 198 if (U_FAILURE(status)) return; 199 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 200 // Generate another DOW rule 201 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 202 tr.getTo()->getName(name); 203 ar2.adoptInstead(new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(), 204 dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR)); 205 206 // Make sure this rule can be applied to the specified date 207 avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d); 208 if (!avail || d > date 209 || initialRaw != tr.getTo()->getRawOffset() 210 || initialDst != tr.getTo()->getDSTSavings()) { 211 // We cannot use this rule as the second transition rule 212 ar2.adoptInstead(nullptr); 213 } 214 } 215 } 216 } 217 if (ar2.isNull()) { 218 // Try previous transition 219 avail = getPreviousTransition(date, true, tr); 220 if (avail) { 221 // Check if the previous transition is either DST->STD or STD->DST. 222 // The actual transition time does not matter here. 223 if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) 224 || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) { 225 226 // Generate another DOW rule 227 Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), 228 year, month, dom, dow, mid, status); 229 if (U_FAILURE(status)) return; 230 weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); 231 dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); 232 tr.getTo()->getName(name); 233 234 // second rule raw/dst offsets should match raw/dst offsets 235 // at the given time 236 ar2.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, initialDst, 237 dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR)); 238 239 // Check if this rule start after the first rule after the specified date 240 avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d); 241 if (!avail || d <= nextTransitionTime) { 242 // We cannot use this rule as the second transition rule 243 ar2.adoptInstead(nullptr); 244 } 245 } 246 } 247 } 248 if (ar2.isNull()) { 249 // Cannot find a good pair of AnnualTimeZoneRule 250 ar1.adoptInstead(nullptr); 251 } else { 252 // The initial rule should represent the rule before the previous transition 253 ar1->getName(initialName); 254 initialRaw = ar1->getRawOffset(); 255 initialDst = ar1->getDSTSavings(); 256 } 257 } 258 } 259 else { 260 // Try the previous one 261 avail = getPreviousTransition(date, true, tr); 262 if (avail) { 263 tr.getTo()->getName(initialName); 264 initialRaw = tr.getTo()->getRawOffset(); 265 initialDst = tr.getTo()->getDSTSavings(); 266 } else { 267 // No transitions in the past. Just use the current offsets 268 getOffset(date, false, initialRaw, initialDst, status); 269 if (U_FAILURE(status)) { 270 return; 271 } 272 } 273 } 274 // Set the initial rule 275 initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst); 276 277 // Set the standard and daylight saving rules 278 if (ar1.isValid() && ar2.isValid()) { 279 if (ar1->getDSTSavings() != 0) { 280 dst = ar1.orphan(); 281 std = ar2.orphan(); 282 } else { 283 std = ar1.orphan(); 284 dst = ar2.orphan(); 285 } 286 } 287 } 288 289 void 290 BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, 291 UVector*& transitionRules, UErrorCode& status) const { 292 if (U_FAILURE(status)) { 293 return; 294 } 295 296 const InitialTimeZoneRule *orgini; 297 TimeZoneTransition tzt; 298 bool avail; 299 int32_t ruleCount; 300 TimeZoneRule *r = nullptr; 301 UnicodeString name; 302 int32_t i; 303 UDate time, t; 304 UDate firstStart; 305 UBool bFinalStd = false, bFinalDst = false; 306 307 initial = nullptr; 308 transitionRules = nullptr; 309 310 // Original transition rules 311 ruleCount = countTransitionRules(status); 312 if (U_FAILURE(status)) { 313 return; 314 } 315 LocalPointer<UVector> orgRules( 316 new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status); 317 if (U_FAILURE(status)) { 318 return; 319 } 320 LocalMemory<const TimeZoneRule *> orgtrs( 321 static_cast<const TimeZoneRule **>(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount))); 322 if (orgtrs.isNull()) { 323 status = U_MEMORY_ALLOCATION_ERROR; 324 return; 325 } 326 getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status); 327 if (U_FAILURE(status)) { 328 return; 329 } 330 for (i = 0; i < ruleCount; i++) { 331 LocalPointer<TimeZoneRule> lpRule(orgtrs[i]->clone(), status); 332 orgRules->adoptElement(lpRule.orphan(), status); 333 if (U_FAILURE(status)) { 334 return; 335 } 336 } 337 338 avail = getPreviousTransition(start, true, tzt); 339 if (!avail) { 340 // No need to filter out rules only applicable to time before the start 341 initial = orgini->clone(); 342 if (initial == nullptr) { 343 status = U_MEMORY_ALLOCATION_ERROR; 344 return; 345 } 346 transitionRules = orgRules.orphan(); 347 return; 348 } 349 350 LocalMemory<bool> done(static_cast<bool *>(uprv_malloc(sizeof(bool)*ruleCount))); 351 if (done.isNull()) { 352 status = U_MEMORY_ALLOCATION_ERROR; 353 return; 354 } 355 LocalPointer<UVector> filteredRules( 356 new UVector(uprv_deleteUObject, nullptr, status), status); 357 if (U_FAILURE(status)) { 358 return; 359 } 360 361 // Create initial rule 362 tzt.getTo()->getName(name); 363 LocalPointer<InitialTimeZoneRule> res_initial( 364 new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status); 365 if (U_FAILURE(status)) { 366 return; 367 } 368 369 // Mark rules which does not need to be processed 370 for (i = 0; i < ruleCount; i++) { 371 r = static_cast<TimeZoneRule*>(orgRules->elementAt(i)); 372 avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time); 373 done[i] = !avail; 374 } 375 376 time = start; 377 while (!bFinalStd || !bFinalDst) { 378 avail = getNextTransition(time, false, tzt); 379 if (!avail) { 380 break; 381 } 382 UDate updatedTime = tzt.getTime(); 383 if (updatedTime == time) { 384 // Can get here if rules for start & end of daylight time have exactly 385 // the same time. 386 // TODO: fix getNextTransition() to prevent it? 387 status = U_INVALID_STATE_ERROR; 388 return; 389 } 390 time = updatedTime; 391 392 const TimeZoneRule *toRule = tzt.getTo(); 393 for (i = 0; i < ruleCount; i++) { 394 r = static_cast<TimeZoneRule*>(orgRules->elementAt(i)); 395 if (*r == *toRule) { 396 break; 397 } 398 } 399 if (i >= ruleCount) { 400 // This case should never happen 401 status = U_INVALID_STATE_ERROR; 402 return; 403 } 404 if (done[i]) { 405 continue; 406 } 407 const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule); 408 const AnnualTimeZoneRule *ar; 409 if (tar != nullptr) { 410 // Get the previous raw offset and DST savings before the very first start time 411 TimeZoneTransition tzt0; 412 t = start; 413 while (true) { 414 avail = getNextTransition(t, false, tzt0); 415 if (!avail) { 416 break; 417 } 418 if (*(tzt0.getTo()) == *tar) { 419 break; 420 } 421 t = tzt0.getTime(); 422 } 423 if (avail) { 424 // Check if the entire start times to be added 425 tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); 426 if (firstStart > start) { 427 // Just add the rule as is 428 LocalPointer<TimeArrayTimeZoneRule> lpTar(tar->clone(), status); 429 filteredRules->adoptElement(lpTar.orphan(), status); 430 if (U_FAILURE(status)) { 431 return; 432 } 433 } else { 434 // Collect transitions after the start time 435 int32_t startTimes; 436 DateTimeRule::TimeRuleType timeType; 437 int32_t idx; 438 439 startTimes = tar->countStartTimes(); 440 timeType = tar->getTimeType(); 441 for (idx = 0; idx < startTimes; idx++) { 442 tar->getStartTimeAt(idx, t); 443 if (timeType == DateTimeRule::STANDARD_TIME) { 444 t -= tzt.getFrom()->getRawOffset(); 445 } 446 if (timeType == DateTimeRule::WALL_TIME) { 447 t -= tzt.getFrom()->getDSTSavings(); 448 } 449 if (t > start) { 450 break; 451 } 452 } 453 if (U_FAILURE(status)) { 454 return; 455 } 456 int32_t asize = startTimes - idx; 457 if (asize > 0) { 458 LocalMemory<UDate> newTimes(static_cast<UDate *>(uprv_malloc(sizeof(UDate) * asize))); 459 if (newTimes.isNull()) { 460 status = U_MEMORY_ALLOCATION_ERROR; 461 return; 462 } 463 for (int32_t newidx = 0; newidx < asize; newidx++) { 464 tar->getStartTimeAt(idx + newidx, newTimes[newidx]); 465 } 466 tar->getName(name); 467 LocalPointer<TimeArrayTimeZoneRule> newTar(new TimeArrayTimeZoneRule( 468 name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status); 469 filteredRules->adoptElement(newTar.orphan(), status); 470 if (U_FAILURE(status)) { 471 return; 472 } 473 } 474 } 475 } 476 } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != nullptr) { 477 ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); 478 if (firstStart == tzt.getTime()) { 479 // Just add the rule as is 480 LocalPointer<AnnualTimeZoneRule> arClone(ar->clone(), status); 481 filteredRules->adoptElement(arClone.orphan(), status); 482 if (U_FAILURE(status)) { 483 return; 484 } 485 } else { 486 // Calculate the transition year 487 int32_t year = Grego::timeToYear(tzt.getTime(), status); 488 if (U_FAILURE(status)) { 489 return; 490 } 491 // Re-create the rule 492 ar->getName(name); 493 LocalPointer<AnnualTimeZoneRule> newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), 494 *(ar->getRule()), year, ar->getEndYear()), status); 495 filteredRules->adoptElement(newAr.orphan(), status); 496 if (U_FAILURE(status)) { 497 return; 498 } 499 } 500 // check if this is a final rule 501 if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { 502 // After bot final standard and dst rules are processed, 503 // exit this while loop. 504 if (ar->getDSTSavings() == 0) { 505 bFinalStd = true; 506 } else { 507 bFinalDst = true; 508 } 509 } 510 } 511 done[i] = true; 512 } 513 514 // Set the results 515 initial = res_initial.orphan(); 516 transitionRules = filteredRules.orphan(); 517 } 518 519 void 520 BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/, 521 UTimeZoneLocalOption /*duplicatedTimeOpt*/, 522 int32_t& /*rawOffset*/, int32_t& /*dstOffset*/, 523 UErrorCode& status) const { 524 if (U_FAILURE(status)) { 525 return; 526 } 527 status = U_UNSUPPORTED_ERROR; 528 } 529 530 void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, 531 int32_t& rawOffset, int32_t& dstOffset, 532 UErrorCode& status) const { 533 getOffsetFromLocal(date, static_cast<UTimeZoneLocalOption>(nonExistingTimeOpt), 534 static_cast<UTimeZoneLocalOption>(duplicatedTimeOpt), rawOffset, dstOffset, status); 535 } 536 537 U_NAMESPACE_END 538 539 #endif /* #if !UCONFIG_NO_FORMATTING */ 540 541 //eof