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