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