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