• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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