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