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