1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 /* 3 ******************************************************************************* 4 * Copyright (C) 2007-2014, International Business Machines Corporation and * 5 * others. All Rights Reserved. * 6 ******************************************************************************* 7 */ 8 package android.icu.util; 9 import java.util.ArrayList; 10 import java.util.BitSet; 11 import java.util.Date; 12 import java.util.List; 13 14 import android.icu.impl.Grego; 15 16 /** 17 * <code>RuleBasedTimeZone</code> is a concrete subclass of <code>TimeZone</code> that allows users to define 18 * custom historic time transition rules. 19 * 20 * @see android.icu.util.TimeZoneRule 21 * 22 * @hide Only a subset of ICU is exposed in Android 23 */ 24 public class RuleBasedTimeZone extends BasicTimeZone { 25 26 private static final long serialVersionUID = 7580833058949327935L; 27 28 private final InitialTimeZoneRule initialRule; 29 private List<TimeZoneRule> historicRules; 30 private AnnualTimeZoneRule[] finalRules; 31 32 private transient List<TimeZoneTransition> historicTransitions; 33 private transient boolean upToDate; 34 35 /** 36 * Constructs a <code>RuleBasedTimeZone</code> object with the ID and the 37 * <code>InitialTimeZoneRule</code> 38 * 39 * @param id The time zone ID. 40 * @param initialRule The initial time zone rule. 41 */ RuleBasedTimeZone(String id, InitialTimeZoneRule initialRule)42 public RuleBasedTimeZone(String id, InitialTimeZoneRule initialRule) { 43 super(id); 44 this.initialRule = initialRule; 45 } 46 47 /** 48 * Adds the <code>TimeZoneRule</code> which represents time transitions. 49 * The <code>TimeZoneRule</code> must have start times, that is, the result 50 * of {@link android.icu.util.TimeZoneRule#isTransitionRule()} must be true. 51 * Otherwise, <code>IllegalArgumentException</code> is thrown. 52 * 53 * @param rule The <code>TimeZoneRule</code>. 54 */ addTransitionRule(TimeZoneRule rule)55 public void addTransitionRule(TimeZoneRule rule) { 56 if (isFrozen()) { 57 throw new UnsupportedOperationException("Attempt to modify a frozen RuleBasedTimeZone instance."); 58 } 59 if (!rule.isTransitionRule()) { 60 throw new IllegalArgumentException("Rule must be a transition rule"); 61 } 62 if (rule instanceof AnnualTimeZoneRule 63 && ((AnnualTimeZoneRule)rule).getEndYear() == AnnualTimeZoneRule.MAX_YEAR) { 64 // One of the final rules applicable in future forever 65 if (finalRules == null) { 66 finalRules = new AnnualTimeZoneRule[2]; 67 finalRules[0] = (AnnualTimeZoneRule)rule; 68 } else if (finalRules[1] == null) { 69 finalRules[1] = (AnnualTimeZoneRule)rule; 70 } else { 71 // Only a pair of AnnualTimeZoneRule is allowed. 72 throw new IllegalStateException("Too many final rules"); 73 } 74 } else { 75 // If this is not a final rule, add it to the historic rule list 76 if (historicRules == null) { 77 historicRules = new ArrayList<TimeZoneRule>(); 78 } 79 historicRules.add(rule); 80 } 81 // Mark dirty, so transitions are recalculated when offset information is 82 // accessed next time. 83 upToDate = false; 84 } 85 86 /** 87 * {@inheritDoc} 88 */ 89 @Override getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds)90 public int getOffset(int era, int year, int month, int day, int dayOfWeek, 91 int milliseconds) { 92 if (era == GregorianCalendar.BC) { 93 // Convert to extended year 94 year = 1 - year; 95 } 96 long time = Grego.fieldsToDay(year, month, day) * Grego.MILLIS_PER_DAY + milliseconds; 97 int[] offsets = new int[2]; 98 getOffset(time, true, LOCAL_DST, LOCAL_STD, offsets); 99 return (offsets[0] + offsets[1]); 100 } 101 102 /** 103 * {@inheritDoc} 104 */ 105 @Override getOffset(long time, boolean local, int[] offsets)106 public void getOffset(long time, boolean local, int[] offsets) { 107 getOffset(time, local, LOCAL_FORMER, LOCAL_LATTER, offsets); 108 } 109 110 /** 111 * {@inheritDoc} 112 * @deprecated This API is ICU internal only. 113 * @hide draft / provisional / internal are hidden on Android 114 */ 115 @Deprecated 116 @Override getOffsetFromLocal(long date, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)117 public void getOffsetFromLocal(long date, 118 int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) { 119 getOffset(date, true, nonExistingTimeOpt, duplicatedTimeOpt, offsets); 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override getRawOffset()126 public int getRawOffset() { 127 // Note: This implementation returns standard GMT offset 128 // as of current time. 129 long now = System.currentTimeMillis(); 130 int[] offsets = new int[2]; 131 getOffset(now, false, offsets); 132 return offsets[0]; 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override inDaylightTime(Date date)139 public boolean inDaylightTime(Date date) { 140 int[] offsets = new int[2]; 141 getOffset(date.getTime(), false, offsets); 142 return (offsets[1] != 0); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override 149 ///CLOVER:OFF setRawOffset(int offsetMillis)150 public void setRawOffset(int offsetMillis) { 151 // TODO: Do nothing for now.. 152 throw new UnsupportedOperationException("setRawOffset in RuleBasedTimeZone is not supported."); 153 } 154 ///CLOVER:ON 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override useDaylightTime()160 public boolean useDaylightTime() { 161 // Note: This implementation returns true when 162 // daylight saving time is used as of now or 163 // after the next transition. 164 long now = System.currentTimeMillis(); 165 int[] offsets = new int[2]; 166 getOffset(now, false, offsets); 167 if (offsets[1] != 0) { 168 return true; 169 } 170 // If DST is not used now, check if DST is used after the next transition 171 TimeZoneTransition tt = getNextTransition(now, false); 172 if (tt != null && tt.getTo().getDSTSavings() != 0) { 173 return true; 174 } 175 return false; 176 } 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override observesDaylightTime()182 public boolean observesDaylightTime() { 183 long time = System.currentTimeMillis(); 184 185 // Check if daylight saving time is observed now. 186 int[] offsets = new int[2]; 187 getOffset(time, false, offsets); 188 if (offsets[1] != 0) { 189 return true; 190 } 191 192 // If DST is not used now, check if DST is used after each transition. 193 BitSet checkFinals = finalRules == null ? null : new BitSet(finalRules.length); 194 while (true) { 195 TimeZoneTransition tt = getNextTransition(time, false); 196 if (tt == null) { 197 // no more transition 198 break; 199 } 200 TimeZoneRule toRule = tt.getTo(); 201 if (toRule.getDSTSavings() != 0) { 202 return true; 203 } 204 if (checkFinals != null) { 205 // final rules exist - check if we saw all of them 206 for (int i = 0; i < finalRules.length; i++) { 207 if (finalRules[i].equals(toRule)) { 208 checkFinals.set(i); 209 } 210 } 211 if (checkFinals.cardinality() == finalRules.length) { 212 // already saw all final rules 213 break; 214 } 215 } 216 time = tt.getTime(); 217 } 218 return false; 219 } 220 221 /** 222 * {@inheritDoc} 223 */ 224 @Override hasSameRules(TimeZone other)225 public boolean hasSameRules(TimeZone other) { 226 if (this == other) { 227 return true; 228 } 229 230 if (!(other instanceof RuleBasedTimeZone)) { 231 // We cannot reasonably compare rules in different types 232 return false; 233 } 234 RuleBasedTimeZone otherRBTZ = (RuleBasedTimeZone)other; 235 236 // initial rule 237 if (!initialRule.isEquivalentTo(otherRBTZ.initialRule)) { 238 return false; 239 } 240 241 // final rules 242 if (finalRules != null && otherRBTZ.finalRules != null) { 243 for (int i = 0; i < finalRules.length; i++) { 244 if (finalRules[i] == null && otherRBTZ.finalRules[i] == null) { 245 continue; 246 } 247 if (finalRules[i] != null && otherRBTZ.finalRules[i] != null 248 && finalRules[i].isEquivalentTo(otherRBTZ.finalRules[i])) { 249 continue; 250 251 } 252 return false; 253 } 254 } else if (finalRules != null || otherRBTZ.finalRules != null) { 255 return false; 256 } 257 258 // historic rules 259 if (historicRules != null && otherRBTZ.historicRules != null) { 260 if (historicRules.size() != otherRBTZ.historicRules.size()) { 261 return false; 262 } 263 for (TimeZoneRule rule : historicRules) { 264 boolean foundSameRule = false; 265 for (TimeZoneRule orule : otherRBTZ.historicRules) { 266 if (rule.isEquivalentTo(orule)) { 267 foundSameRule = true; 268 break; 269 } 270 } 271 if (!foundSameRule) { 272 return false; 273 } 274 } 275 } else if (historicRules != null || otherRBTZ.historicRules != null) { 276 return false; 277 } 278 return true; 279 } 280 281 // BasicTimeZone methods 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override getTimeZoneRules()287 public TimeZoneRule[] getTimeZoneRules() { 288 int size = 1; 289 if (historicRules != null) { 290 size += historicRules.size(); 291 } 292 293 if (finalRules != null) { 294 if (finalRules[1] != null) { 295 size += 2; 296 } else { 297 size++; 298 } 299 } 300 TimeZoneRule[] rules = new TimeZoneRule[size]; 301 rules[0] = initialRule; 302 303 int idx = 1; 304 if (historicRules != null) { 305 for (; idx < historicRules.size() + 1; idx++) { 306 rules[idx] = historicRules.get(idx - 1); 307 } 308 } 309 if (finalRules != null) { 310 rules[idx++] = finalRules[0]; 311 if (finalRules[1] != null) { 312 rules[idx] = finalRules[1]; 313 } 314 } 315 return rules; 316 } 317 318 /** 319 * {@inheritDoc} 320 */ 321 @Override getNextTransition(long base, boolean inclusive)322 public TimeZoneTransition getNextTransition(long base, boolean inclusive) { 323 complete(); 324 if (historicTransitions == null) { 325 return null; 326 } 327 boolean isFinal = false; 328 TimeZoneTransition result = null; 329 TimeZoneTransition tzt = historicTransitions.get(0); 330 long tt = tzt.getTime(); 331 if (tt > base || (inclusive && tt == base)) { 332 result = tzt; 333 } else { 334 int idx = historicTransitions.size() - 1; 335 tzt = historicTransitions.get(idx); 336 tt = tzt.getTime(); 337 if (inclusive && tt == base) { 338 result = tzt; 339 } else if (tt <= base) { 340 if (finalRules != null) { 341 // Find a transion time with finalRules 342 Date start0 = finalRules[0].getNextStart(base, 343 finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), inclusive); 344 Date start1 = finalRules[1].getNextStart(base, 345 finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), inclusive); 346 347 if (start1.after(start0)) { 348 tzt = new TimeZoneTransition(start0.getTime(), finalRules[1], finalRules[0]); 349 } else { 350 tzt = new TimeZoneTransition(start1.getTime(), finalRules[0], finalRules[1]); 351 } 352 result = tzt; 353 isFinal = true; 354 } else { 355 return null; 356 } 357 } else { 358 // Find a transition within the historic transitions 359 idx--; 360 TimeZoneTransition prev = tzt; 361 while (idx > 0) { 362 tzt = historicTransitions.get(idx); 363 tt = tzt.getTime(); 364 if (tt < base || (!inclusive && tt == base)) { 365 break; 366 } 367 idx--; 368 prev = tzt; 369 } 370 result = prev; 371 } 372 } 373 if (result != null) { 374 // For now, this implementation ignore transitions with only zone name changes. 375 TimeZoneRule from = result.getFrom(); 376 TimeZoneRule to = result.getTo(); 377 if (from.getRawOffset() == to.getRawOffset() 378 && from.getDSTSavings() == to.getDSTSavings()) { 379 // No offset changes. Try next one if not final 380 if (isFinal) { 381 return null; 382 } else { 383 result = getNextTransition(result.getTime(), false /* always exclusive */); 384 } 385 } 386 } 387 return result; 388 } 389 390 /** 391 * {@inheritDoc} 392 */ 393 @Override getPreviousTransition(long base, boolean inclusive)394 public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) { 395 complete(); 396 if (historicTransitions == null) { 397 return null; 398 } 399 TimeZoneTransition result = null; 400 TimeZoneTransition tzt = historicTransitions.get(0); 401 long tt = tzt.getTime(); 402 if (inclusive && tt == base) { 403 result = tzt; 404 } else if (tt >= base) { 405 return null; 406 } else { 407 int idx = historicTransitions.size() - 1; 408 tzt = historicTransitions.get(idx); 409 tt = tzt.getTime(); 410 if (inclusive && tt == base) { 411 result = tzt; 412 } else if (tt < base) { 413 if (finalRules != null) { 414 // Find a transion time with finalRules 415 Date start0 = finalRules[0].getPreviousStart(base, 416 finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), inclusive); 417 Date start1 = finalRules[1].getPreviousStart(base, 418 finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), inclusive); 419 420 if (start1.before(start0)) { 421 tzt = new TimeZoneTransition(start0.getTime(), finalRules[1], finalRules[0]); 422 } else { 423 tzt = new TimeZoneTransition(start1.getTime(), finalRules[0], finalRules[1]); 424 } 425 } 426 result = tzt; 427 } else { 428 // Find a transition within the historic transitions 429 idx--; 430 while (idx >= 0) { 431 tzt = historicTransitions.get(idx); 432 tt = tzt.getTime(); 433 if (tt < base || (inclusive && tt == base)) { 434 break; 435 } 436 idx--; 437 } 438 result = tzt; 439 } 440 } 441 if (result != null) { 442 // For now, this implementation ignore transitions with only zone name changes. 443 TimeZoneRule from = result.getFrom(); 444 TimeZoneRule to = result.getTo(); 445 if (from.getRawOffset() == to.getRawOffset() 446 && from.getDSTSavings() == to.getDSTSavings()) { 447 // No offset changes. Try previous one 448 result = getPreviousTransition(result.getTime(), false /* always exclusive */); 449 } 450 } 451 return result; 452 } 453 454 /** 455 * {@inheritDoc} 456 */ 457 @Override clone()458 public Object clone() { 459 if (isFrozen()) { 460 return this; 461 } 462 return cloneAsThawed(); 463 } 464 465 // private stuff 466 467 /* 468 * Resolve historic transition times and update fields used for offset 469 * calculation. 470 */ complete()471 private void complete() { 472 if (upToDate) { 473 // No rules were added since last time. 474 return; 475 } 476 477 // Make sure either no final rules or a pair of AnnualTimeZoneRules 478 // are available. 479 if (finalRules != null && finalRules[1] == null) { 480 throw new IllegalStateException("Incomplete final rules"); 481 } 482 483 // Create a TimezoneTransition and add to the list 484 if (historicRules != null || finalRules != null) { 485 TimeZoneRule curRule = initialRule; 486 long lastTransitionTime = Grego.MIN_MILLIS; 487 488 // Build the transition array which represents historical time zone 489 // transitions. 490 if (historicRules != null) { 491 BitSet done = new BitSet(historicRules.size()); // for skipping rules already processed 492 493 while (true) { 494 int curStdOffset = curRule.getRawOffset(); 495 int curDstSavings = curRule.getDSTSavings(); 496 long nextTransitionTime = Grego.MAX_MILLIS; 497 TimeZoneRule nextRule = null; 498 Date d; 499 long tt; 500 501 for (int i = 0; i < historicRules.size(); i++) { 502 if (done.get(i)) { 503 continue; 504 } 505 TimeZoneRule r = historicRules.get(i); 506 d = r.getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false); 507 if (d == null) { 508 // No more transitions from this rule - skip this rule next time 509 done.set(i); 510 } else { 511 if (r == curRule || 512 (r.getName().equals(curRule.getName()) 513 && r.getRawOffset() == curRule.getRawOffset() 514 && r.getDSTSavings() == curRule.getDSTSavings())) { 515 continue; 516 } 517 tt = d.getTime(); 518 if (tt < nextTransitionTime) { 519 nextTransitionTime = tt; 520 nextRule = r; 521 } 522 } 523 } 524 525 if (nextRule == null) { 526 // Check if all historic rules are done 527 boolean bDoneAll = true; 528 for (int j = 0; j < historicRules.size(); j++) { 529 if (!done.get(j)) { 530 bDoneAll = false; 531 break; 532 } 533 } 534 if (bDoneAll) { 535 break; 536 } 537 } 538 539 if (finalRules != null) { 540 // Check if one of final rules has earlier transition date 541 for (int i = 0; i < 2 /* finalRules.length */; i++) { 542 if (finalRules[i] == curRule) { 543 continue; 544 } 545 d = finalRules[i].getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false); 546 if (d != null) { 547 tt = d.getTime(); 548 if (tt < nextTransitionTime) { 549 nextTransitionTime = tt; 550 nextRule = finalRules[i]; 551 } 552 } 553 } 554 } 555 556 if (nextRule == null) { 557 // Nothing more 558 break; 559 } 560 561 if (historicTransitions == null) { 562 historicTransitions = new ArrayList<TimeZoneTransition>(); 563 } 564 historicTransitions.add(new TimeZoneTransition(nextTransitionTime, curRule, nextRule)); 565 lastTransitionTime = nextTransitionTime; 566 curRule = nextRule; 567 } 568 } 569 if (finalRules != null) { 570 if (historicTransitions == null) { 571 historicTransitions = new ArrayList<TimeZoneTransition>(); 572 } 573 // Append the first transition for each 574 Date d0 = finalRules[0].getNextStart(lastTransitionTime, curRule.getRawOffset(), curRule.getDSTSavings(), false); 575 Date d1 = finalRules[1].getNextStart(lastTransitionTime, curRule.getRawOffset(), curRule.getDSTSavings(), false); 576 if (d1.after(d0)) { 577 historicTransitions.add(new TimeZoneTransition(d0.getTime(), curRule, finalRules[0])); 578 d1 = finalRules[1].getNextStart(d0.getTime(), finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), false); 579 historicTransitions.add(new TimeZoneTransition(d1.getTime(), finalRules[0], finalRules[1])); 580 } else { 581 historicTransitions.add(new TimeZoneTransition(d1.getTime(), curRule, finalRules[1])); 582 d0 = finalRules[0].getNextStart(d1.getTime(), finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), false); 583 historicTransitions.add(new TimeZoneTransition(d0.getTime(), finalRules[1], finalRules[0])); 584 } 585 } 586 } 587 upToDate = true; 588 } 589 590 /* 591 * getOffset internal implementation 592 */ getOffset(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets)593 private void getOffset(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt, int[] offsets) { 594 complete(); 595 TimeZoneRule rule = null; 596 if (historicTransitions == null) { 597 rule = initialRule; 598 } else { 599 long tstart = getTransitionTime(historicTransitions.get(0), 600 local, NonExistingTimeOpt, DuplicatedTimeOpt); 601 if (time < tstart) { 602 rule = initialRule; 603 } else { 604 int idx = historicTransitions.size() - 1; 605 long tend = getTransitionTime(historicTransitions.get(idx), 606 local, NonExistingTimeOpt, DuplicatedTimeOpt); 607 if (time > tend) { 608 if (finalRules != null) { 609 rule = findRuleInFinal(time, local, NonExistingTimeOpt, DuplicatedTimeOpt); 610 } 611 if (rule == null) { 612 // no final rules or the given time is before the first transition 613 // specified by the final rules -> use the last rule 614 rule = (historicTransitions.get(idx)).getTo(); 615 } 616 } else { 617 // Find a historical transition 618 while (idx >= 0) { 619 if (time >= getTransitionTime(historicTransitions.get(idx), 620 local, NonExistingTimeOpt, DuplicatedTimeOpt)) { 621 break; 622 } 623 idx--; 624 } 625 rule = (historicTransitions.get(idx)).getTo(); 626 } 627 } 628 } 629 offsets[0] = rule.getRawOffset(); 630 offsets[1] = rule.getDSTSavings(); 631 } 632 633 /* 634 * Find a time zone rule applicable to the specified time 635 */ findRuleInFinal(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt)636 private TimeZoneRule findRuleInFinal(long time, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt) { 637 if (finalRules == null || finalRules.length != 2 || finalRules[0] == null || finalRules[1] == null) { 638 return null; 639 } 640 641 Date start0, start1; 642 long base; 643 int localDelta; 644 645 base = time; 646 if (local) { 647 localDelta = getLocalDelta(finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), 648 finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), 649 NonExistingTimeOpt, DuplicatedTimeOpt); 650 base -= localDelta; 651 } 652 start0 = finalRules[0].getPreviousStart(base, finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), true); 653 654 base = time; 655 if (local) { 656 localDelta = getLocalDelta(finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), 657 finalRules[1].getRawOffset(), finalRules[1].getDSTSavings(), 658 NonExistingTimeOpt, DuplicatedTimeOpt); 659 base -= localDelta; 660 } 661 start1 = finalRules[1].getPreviousStart(base, finalRules[0].getRawOffset(), finalRules[0].getDSTSavings(), true); 662 663 if (start0 == null || start1 == null) { 664 if (start0 != null) { 665 return finalRules[0]; 666 } else if (start1 != null) { 667 return finalRules[1]; 668 } 669 // Both rules take effect after the given time 670 return null; 671 } 672 673 return start0.after(start1) ? finalRules[0] : finalRules[1]; 674 } 675 676 /* 677 * Get the transition time in local wall clock 678 */ getTransitionTime(TimeZoneTransition tzt, boolean local, int NonExistingTimeOpt, int DuplicatedTimeOpt)679 private static long getTransitionTime(TimeZoneTransition tzt, boolean local, 680 int NonExistingTimeOpt, int DuplicatedTimeOpt) { 681 long time = tzt.getTime(); 682 if (local) { 683 time += getLocalDelta(tzt.getFrom().getRawOffset(), tzt.getFrom().getDSTSavings(), 684 tzt.getTo().getRawOffset(), tzt.getTo().getDSTSavings(), 685 NonExistingTimeOpt, DuplicatedTimeOpt); 686 } 687 return time; 688 } 689 690 /* 691 * Returns amount of local time adjustment used for checking rule transitions 692 */ getLocalDelta(int rawBefore, int dstBefore, int rawAfter, int dstAfter, int NonExistingTimeOpt, int DuplicatedTimeOpt)693 private static int getLocalDelta(int rawBefore, int dstBefore, int rawAfter, int dstAfter, 694 int NonExistingTimeOpt, int DuplicatedTimeOpt) { 695 int delta = 0; 696 697 int offsetBefore = rawBefore + dstBefore; 698 int offsetAfter = rawAfter + dstAfter; 699 700 boolean dstToStd = (dstBefore != 0) && (dstAfter == 0); 701 boolean stdToDst = (dstBefore == 0) && (dstAfter != 0); 702 703 if (offsetAfter - offsetBefore >= 0) { 704 // Positive transition, which makes a non-existing local time range 705 if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd) 706 || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) { 707 delta = offsetBefore; 708 } else if (((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst) 709 || ((NonExistingTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) { 710 delta = offsetAfter; 711 } else if ((NonExistingTimeOpt & FORMER_LATTER_MASK) == LOCAL_LATTER) { 712 delta = offsetBefore; 713 } else { 714 // Interprets the time with rule before the transition, 715 // default for non-existing time range 716 delta = offsetAfter; 717 } 718 } else { 719 // Negative transition, which makes a duplicated local time range 720 if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && dstToStd) 721 || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && stdToDst)) { 722 delta = offsetAfter; 723 } else if (((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_STD && stdToDst) 724 || ((DuplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST && dstToStd)) { 725 delta = offsetBefore; 726 } else if ((DuplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) { 727 delta = offsetBefore; 728 } else { 729 // Interprets the time with rule after the transition, 730 // default for duplicated local time range 731 delta = offsetAfter; 732 } 733 } 734 return delta; 735 } 736 737 // Freezable stuffs 738 private volatile transient boolean isFrozen = false; 739 740 /** 741 * {@inheritDoc} 742 */ isFrozen()743 public boolean isFrozen() { 744 return isFrozen; 745 } 746 747 /** 748 * {@inheritDoc} 749 */ freeze()750 public TimeZone freeze() { 751 complete(); 752 isFrozen = true; 753 return this; 754 } 755 756 /** 757 * {@inheritDoc} 758 */ cloneAsThawed()759 public TimeZone cloneAsThawed() { 760 RuleBasedTimeZone tz = (RuleBasedTimeZone)super.cloneAsThawed(); 761 if (historicRules != null) { 762 tz.historicRules = new ArrayList<TimeZoneRule>(historicRules); // rules are immutable 763 } 764 if (finalRules != null) { 765 tz.finalRules = finalRules.clone(); 766 } 767 tz.isFrozen = false; 768 return tz; 769 } 770 } 771 772