• 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 *   Copyright (C) 1996-2016, International Business Machines
6 *   Corporation and others.  All Rights Reserved.
7 */
8 
9 package ohos.global.icu.util;
10 
11 import java.io.IOException;
12 import java.util.Date;
13 
14 import ohos.global.icu.impl.Grego;
15 
16 /**
17  * <strong>[icu enhancement]</strong> ICU's replacement for {@link java.util.SimpleTimeZone}.&nbsp;Methods, fields, and other functionality specific to ICU are labeled '<strong>[icu]</strong>'.
18  *
19  * <p><code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
20  * that represents a time zone for use with a Gregorian calendar. This
21  * class does not handle historical changes.
22  *
23  * <p>Use a negative value for <code>dayOfWeekInMonth</code> to indicate that
24  * <code>SimpleTimeZone</code> should count from the end of the month backwards.  For
25  * example, if Daylight Savings Time starts or ends at the last Sunday in a month, use
26  * <code>dayOfWeekInMonth = -1</code> along with <code>dayOfWeek = Calendar.SUNDAY</code>
27  * to specify the rule.
28  *
29  * @see      Calendar
30  * @see      GregorianCalendar
31  * @see      TimeZone
32  * @author   Deborah Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
33  * @hide exposed on OHOS
34  */
35 public class SimpleTimeZone extends BasicTimeZone {
36     private static final long serialVersionUID = -7034676239311322769L;
37 
38     /**
39      * Constant for a mode of start or end time specified as local wall time.
40      */
41     public static final int WALL_TIME = 0;
42 
43     /**
44      * Constant for a mode of start or end time specified as local standard time.
45      */
46     public static final int STANDARD_TIME = 1;
47 
48     /**
49      * Constant for a mode of start or end time specified as UTC.
50      */
51     public static final int UTC_TIME = 2;
52 
53     /**
54      * Constructs a SimpleTimeZone with the given base time zone offset from GMT
55      * and time zone ID. Timezone IDs can be obtained from
56      * TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to
57      * construct a TimeZone.
58      *
59      * @param rawOffset  The given base time zone offset to GMT.
60      * @param ID         The time zone ID which is obtained from
61      *                   TimeZone.getAvailableIDs.
62      */
SimpleTimeZone(int rawOffset, String ID)63     public SimpleTimeZone(int rawOffset, String ID) {
64         super(ID);
65         construct(rawOffset, 0, 0, 0,
66                 0, WALL_TIME,
67                 0, 0, 0,
68                 0, WALL_TIME,
69                 Grego.MILLIS_PER_HOUR);
70     }
71 
72     /**
73      * Constructs a SimpleTimeZone with the given base time zone offset from
74      * GMT, time zone ID, time to start and end the daylight time. Timezone IDs
75      * can be obtained from TimeZone.getAvailableIDs. Normally you should use
76      * TimeZone.getDefault to create a TimeZone. For a time zone that does not
77      * use daylight saving time, do not use this constructor; instead you should
78      * use SimpleTimeZone(rawOffset, ID).
79      *
80      * By default, this constructor specifies day-of-week-in-month rules. That
81      * is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this
82      * indicates the first Sunday in the startMonth. A startDay of -1 likewise
83      * indicates the last Sunday. However, by using negative or zero values for
84      * certain parameters, other types of rules can be specified.
85      *
86      * Day of month. To specify an exact day of the month, such as March 1, set
87      * startDayOfWeek to zero.
88      *
89      * Day of week after day of month. To specify the first day of the week
90      * occurring on or after an exact day of the month, make the day of the week
91      * negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY,
92      * this indicates the first Monday on or after the 5th day of the
93      * startMonth.
94      *
95      * Day of week before day of month. To specify the last day of the week
96      * occurring on or before an exact day of the month, make the day of the
97      * week and the day of the month negative. For example, if startDay is -21
98      * and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or
99      * before the 21st of the startMonth.
100      *
101      * The above examples refer to the startMonth, startDay, and startDayOfWeek;
102      * the same applies for the endMonth, endDay, and endDayOfWeek.
103      *
104      * @param rawOffset       The given base time zone offset to GMT.
105      * @param ID              The time zone ID which is obtained from
106      *                        TimeZone.getAvailableIDs.
107      * @param startMonth      The daylight savings starting month. Month is
108      *                        0-based. eg, 0 for January.
109      * @param startDay        The daylight savings starting
110      *                        day-of-week-in-month. Please see the member
111      *                        description for an example.
112      * @param startDayOfWeek  The daylight savings starting day-of-week. Please
113      *                        see the member description for an example.
114      * @param startTime       The daylight savings starting time in local wall
115      *                        time, which is standard time in this case. Please see the
116      *                        member description for an example.
117      * @param endMonth        The daylight savings ending month. Month is
118      *                        0-based. eg, 0 for January.
119      * @param endDay          The daylight savings ending day-of-week-in-month.
120      *                        Please see the member description for an example.
121      * @param endDayOfWeek    The daylight savings ending day-of-week. Please
122      *                        see the member description for an example.
123      * @param endTime         The daylight savings ending time in local wall time,
124      *                        which is daylight time in this case. Please see the
125      *                        member description for an example.
126      * @throws IllegalArgumentException the month, day, dayOfWeek, or time
127      * parameters are out of range for the start or end rule
128      */
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)129     public SimpleTimeZone(int rawOffset, String ID,
130                           int startMonth, int startDay, int startDayOfWeek, int startTime,
131                           int endMonth, int endDay, int endDayOfWeek, int endTime) {
132         super(ID);
133         construct(rawOffset,
134                 startMonth, startDay, startDayOfWeek,
135                 startTime, WALL_TIME,
136                 endMonth, endDay, endDayOfWeek,
137                 endTime, WALL_TIME,
138                 Grego.MILLIS_PER_HOUR);
139     }
140 
141     /**
142      * Constructs a SimpleTimeZone with the given base time zone offset from
143      * GMT, time zone ID, time and its mode to start and end the daylight time.
144      * The mode specifies either {@link #WALL_TIME} or {@link #STANDARD_TIME}
145      * or {@link #UTC_TIME}.
146      *
147      * @param rawOffset       The given base time zone offset to GMT.
148      * @param ID              The time zone ID which is obtained from
149      *                        TimeZone.getAvailableIDs.
150      * @param startMonth      The daylight savings starting month. Month is
151      *                        0-based. eg, 0 for January.
152      * @param startDay        The daylight savings starting
153      *                        day-of-week-in-month. Please see the member
154      *                        description for an example.
155      * @param startDayOfWeek  The daylight savings starting day-of-week. Please
156      *                        see the member description for an example.
157      * @param startTime       The daylight savings starting time in local wall
158      *                        time, which is standard time in this case. Please see the
159      *                        member description for an example.
160      * @param startTimeMode   The mode of the start time specified by startTime.
161      * @param endMonth        The daylight savings ending month. Month is
162      *                        0-based. eg, 0 for January.
163      * @param endDay          The daylight savings ending day-of-week-in-month.
164      *                        Please see the member description for an example.
165      * @param endDayOfWeek    The daylight savings ending day-of-week. Please
166      *                        see the member description for an example.
167      * @param endTime         The daylight savings ending time in local wall time,
168      *                        which is daylight time in this case. Please see the
169      *                        member description for an example.
170      * @param endTimeMode     The mode of the end time specified by endTime.
171      * @param dstSavings      The amount of time in ms saved during DST.
172      * @throws IllegalArgumentException the month, day, dayOfWeek, or time
173      * parameters are out of range for the start or end rule
174      */
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, int endMonth, int endDay, int endDayOfWeek, int endTime, int endTimeMode,int dstSavings)175     public SimpleTimeZone(int rawOffset,  String ID,
176                           int startMonth, int startDay,
177                           int startDayOfWeek, int startTime,
178                           int startTimeMode,
179                           int endMonth, int endDay,
180                           int endDayOfWeek, int endTime,
181                           int endTimeMode,int dstSavings){
182         super(ID);
183         construct(rawOffset,
184                   startMonth, startDay, startDayOfWeek,
185                   startTime, startTimeMode,
186                   endMonth, endDay, endDayOfWeek,
187                   endTime, endTimeMode,
188                   dstSavings);
189     }
190 
191     /**
192      * Constructor.  This constructor is identical to the 10-argument
193      * constructor, but also takes a dstSavings parameter.
194      * @param rawOffset       The given base time zone offset to GMT.
195      * @param ID              The time zone ID which is obtained from
196      *                        TimeZone.getAvailableIDs.
197      * @param startMonth      The daylight savings starting month. Month is
198      *                        0-based. eg, 0 for January.
199      * @param startDay        The daylight savings starting
200      *                        day-of-week-in-month. Please see the member
201      *                        description for an example.
202      * @param startDayOfWeek  The daylight savings starting day-of-week. Please
203      *                        see the member description for an example.
204      * @param startTime       The daylight savings starting time in local wall
205      *                        time, which is standard time in this case. Please see the
206      *                        member description for an example.
207      * @param endMonth        The daylight savings ending month. Month is
208      *                        0-based. eg, 0 for January.
209      * @param endDay          The daylight savings ending day-of-week-in-month.
210      *                        Please see the member description for an example.
211      * @param endDayOfWeek    The daylight savings ending day-of-week. Please
212      *                        see the member description for an example.
213      * @param endTime         The daylight savings ending time in local wall time,
214      *                        which is daylight time in this case. Please see the
215      *                        member description for an example.
216      * @param dstSavings      The amount of time in ms saved during DST.
217      * @throws IllegalArgumentException the month, day, dayOfWeek, or time
218      * parameters are out of range for the start or end rule
219      */
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int dstSavings)220     public SimpleTimeZone(int rawOffset, String ID,
221                           int startMonth, int startDay, int startDayOfWeek, int startTime,
222                           int endMonth, int endDay, int endDayOfWeek, int endTime,
223                           int dstSavings) {
224         super(ID);
225         construct(rawOffset,
226                 startMonth, startDay, startDayOfWeek,
227                 startTime, WALL_TIME,
228                 endMonth, endDay, endDayOfWeek,
229                 endTime, WALL_TIME,
230                 dstSavings);
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
setID(String ID)237     public void setID(String ID) {
238         if (isFrozen()) {
239             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
240         }
241         super.setID(ID);
242         transitionRulesInitialized = false;
243     }
244 
245     /**
246      * Overrides TimeZone
247      * Sets the base time zone offset to GMT.
248      * This is the offset to add "to" UTC to get local time.
249      * @param offsetMillis the raw offset of the time zone
250      */
251     @Override
setRawOffset(int offsetMillis)252     public void setRawOffset(int offsetMillis) {
253         if (isFrozen()) {
254             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
255         }
256 
257         raw = offsetMillis;
258         transitionRulesInitialized = false;
259     }
260 
261     /**
262      * Overrides TimeZone
263      * Gets the GMT offset for this time zone.
264      * @return the raw offset
265      */
266     @Override
getRawOffset()267     public int getRawOffset() {
268         return raw;
269     }
270 
271     /**
272      * Sets the daylight savings starting year.
273      *
274      * @param year  The daylight savings starting year.
275      */
setStartYear(int year)276     public void setStartYear(int year) {
277         if (isFrozen()) {
278             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
279         }
280 
281         getSTZInfo().sy = year;
282         this.startYear = year;
283         transitionRulesInitialized = false;
284     }
285 
286     /**
287      * Sets the daylight savings starting rule. For example, Daylight Savings
288      * Time starts at the second Sunday in March, at 2 AM in standard time.
289      * Therefore, you can set the start rule by calling:
290      * setStartRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000);
291      *
292      * @param month             The daylight savings starting month. Month is
293      *                          0-based. eg, 0 for January.
294      * @param dayOfWeekInMonth  The daylight savings starting
295      *                          day-of-week-in-month. Please see the member
296      *                          description for an example.
297      * @param dayOfWeek         The daylight savings starting day-of-week.
298      *                          Please see the member description for an
299      *                          example.
300      * @param time              The daylight savings starting time in local wall
301      *                          time, which is standard time in this case. Please see
302      *                          the member description for an example.
303      * @throws IllegalArgumentException the month, dayOfWeekInMonth,
304      * dayOfWeek, or time parameters are out of range
305      */
setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time)306     public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek,
307                              int time) {
308         if (isFrozen()) {
309             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
310         }
311 
312         getSTZInfo().setStart(month, dayOfWeekInMonth, dayOfWeek, time, -1, false);
313         setStartRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME);
314     }
315 
316     /**
317      * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
318      * Time starts at the second Sunday in March, at 2 AM in standard time.
319      * Therefore, you can set the start rule by calling:
320      * <code>setStartRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000);</code>
321      * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
322      * the exact starting date.  Their exact meaning depend on their respective signs,
323      * allowing various types of rules to be constructed, as follows:<ul>
324      *   <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
325      *       day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
326      *       of the month).
327      *   <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
328      *       the day of week in the month counting backward from the end of the month.
329      *       (e.g., (-1, MONDAY) is the last Monday in the month)
330      *   <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
331      *       specifies the day of the month, regardless of what day of the week it is.
332      *       (e.g., (10, 0) is the tenth day of the month)
333      *   <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
334      *       specifies the day of the month counting backward from the end of the
335      *       month, regardless of what day of the week it is (e.g., (-2, 0) is the
336      *       next-to-last day of the month).
337      *   <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
338      *       first specified day of the week on or after the specfied day of the month.
339      *       (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
340      *       [or the 15th itself if the 15th is a Sunday].)
341      *   <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
342      *       last specified day of the week on or before the specified day of the month.
343      *       (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
344      *       [or the 20th itself if the 20th is a Tuesday].)</ul>
345      * @param month the daylight savings starting month. Month is 0-based.
346      * eg, 0 for January.
347      * @param dayOfWeekInMonth the daylight savings starting
348      * day-of-week-in-month. Please see the member description for an example.
349      * @param dayOfWeek the daylight savings starting day-of-week. Please see
350      * the member description for an example.
351      * @param time the daylight savings starting time. Please see the member
352      * description for an example.
353      */
setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode)354     private void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode) {
355         assert (!isFrozen());
356 
357         startMonth     =  month;
358         startDay       = dayOfWeekInMonth;
359         startDayOfWeek = dayOfWeek;
360         startTime      = time;
361         startTimeMode  = mode;
362         decodeStartRule();
363 
364         transitionRulesInitialized = false;
365     }
366 
367     /**
368      * Sets the DST start rule to a fixed date within a month.
369      *
370      * @param month         The month in which this rule occurs (0-based).
371      * @param dayOfMonth    The date in that month (1-based).
372      * @param time          The time of that day (number of millis after midnight)
373      *                      when DST takes effect in local wall time, which is
374      *                      standard time in this case.
375      * @throws IllegalArgumentException the month,
376      * dayOfMonth, or time parameters are out of range
377      */
setStartRule(int month, int dayOfMonth, int time)378     public void setStartRule(int month, int dayOfMonth, int time) {
379         if (isFrozen()) {
380             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
381         }
382 
383         getSTZInfo().setStart(month, -1, -1, time, dayOfMonth, false);
384         setStartRule(month, dayOfMonth, 0, time, WALL_TIME);
385     }
386 
387     /**
388      * Sets the DST start rule to a weekday before or after a give date within
389      * a month, e.g., the first Monday on or after the 8th.
390      *
391      * @param month         The month in which this rule occurs (0-based).
392      * @param dayOfMonth    A date within that month (1-based).
393      * @param dayOfWeek     The day of the week on which this rule occurs.
394      * @param time          The time of that day (number of millis after midnight)
395      *                      when DST takes effect in local wall time, which is
396      *                      standard time in this case.
397      * @param after         If true, this rule selects the first dayOfWeek on
398      *                      or after dayOfMonth.  If false, this rule selects
399      *                      the last dayOfWeek on or before dayOfMonth.
400      * @throws IllegalArgumentException the month, dayOfMonth,
401      * dayOfWeek, or time parameters are out of range
402      */
setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)403     public void setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) {
404         if (isFrozen()) {
405             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
406         }
407 
408         getSTZInfo().setStart(month, -1, dayOfWeek, time, dayOfMonth, after);
409         setStartRule(month, after ? dayOfMonth : -dayOfMonth,
410                 -dayOfWeek, time, WALL_TIME);
411     }
412 
413     /**
414      * Sets the daylight savings ending rule. For example, if Daylight Savings Time
415      * ends at the last (-1) Sunday in October, at 2 AM in standard time,
416      * you can set the end rule by calling:
417      * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
418      *
419      * @param month             The daylight savings ending month. Month is
420      *                          0-based. eg, 0 for January.
421      * @param dayOfWeekInMonth  The daylight savings ending
422      *                          day-of-week-in-month. Please see the member
423      *                          description for an example.
424      * @param dayOfWeek         The daylight savings ending day-of-week. Please
425      *                          see the member description for an example.
426      * @param time              The daylight savings ending time in local wall time,
427      *                          which is daylight time in this case. Please see the
428      *                          member description for an example.
429      * @throws IllegalArgumentException the month, dayOfWeekInMonth,
430      * dayOfWeek, or time parameters are out of range
431      */
setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time)432     public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time) {
433         if (isFrozen()) {
434             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
435         }
436 
437         getSTZInfo().setEnd(month, dayOfWeekInMonth, dayOfWeek, time, -1, false);
438         setEndRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME);
439     }
440 
441     /**
442      * Sets the DST end rule to a fixed date within a month.
443      *
444      * @param month         The month in which this rule occurs (0-based).
445      * @param dayOfMonth    The date in that month (1-based).
446      * @param time          The time of that day (number of millis after midnight)
447      *                      when DST ends in local wall time, which is daylight
448      *                      time in this case.
449      * @throws IllegalArgumentException the month,
450      * dayOfMonth, or time parameters are out of range
451      */
setEndRule(int month, int dayOfMonth, int time)452     public void setEndRule(int month, int dayOfMonth, int time) {
453         if (isFrozen()) {
454             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
455         }
456 
457         getSTZInfo().setEnd(month, -1, -1, time, dayOfMonth, false);
458         setEndRule(month, dayOfMonth, WALL_TIME, time);
459     }
460 
461     /**
462      * Sets the DST end rule to a weekday before or after a give date within
463      * a month, e.g., the first Monday on or after the 8th.
464      *
465      * @param month         The month in which this rule occurs (0-based).
466      * @param dayOfMonth    A date within that month (1-based).
467      * @param dayOfWeek     The day of the week on which this rule occurs.
468      * @param time          The time of that day (number of millis after midnight)
469      *                      when DST ends in local wall time, which is daylight
470      *                      time in this case.
471      * @param after         If true, this rule selects the first dayOfWeek on
472      *                      or after dayOfMonth.  If false, this rule selects
473      *                      the last dayOfWeek on or before dayOfMonth.
474      * @throws IllegalArgumentException the month, dayOfMonth,
475      * dayOfWeek, or time parameters are out of range
476      */
setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)477     public void setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) {
478         if (isFrozen()) {
479             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
480         }
481 
482         getSTZInfo().setEnd(month, -1, dayOfWeek, time, dayOfMonth, after);
483         setEndRule(month, dayOfMonth, dayOfWeek, time, WALL_TIME, after);
484     }
485 
setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, int mode, boolean after)486     private void setEndRule(int month, int dayOfMonth, int dayOfWeek,
487                                                 int time, int mode, boolean after){
488         assert (!isFrozen());
489         setEndRule(month, after ? dayOfMonth : -dayOfMonth, -dayOfWeek, time, mode);
490     }
491 
492     /**
493      * Sets the daylight savings ending rule. For example, in the U.S., Daylight
494      * Savings Time ends at the first Sunday in November, at 2 AM in standard time.
495      * Therefore, you can set the end rule by calling:
496      * setEndRule(Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2*60*60*1000);
497      * Various other types of rules can be specified by manipulating the dayOfWeek
498      * and dayOfWeekInMonth parameters.  For complete details, see the documentation
499      * for setStartRule().
500      * @param month the daylight savings ending month. Month is 0-based.
501      * eg, 0 for January.
502      * @param dayOfWeekInMonth the daylight savings ending
503      * day-of-week-in-month. See setStartRule() for a complete explanation.
504      * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
505      * for a complete explanation.
506      * @param time the daylight savings ending time. Please see the member
507      * description for an example.
508      */
setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode)509     private void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode){
510         assert (!isFrozen());
511 
512         endMonth     = month;
513         endDay       = dayOfWeekInMonth;
514         endDayOfWeek = dayOfWeek;
515         endTime      = time;
516         endTimeMode  = mode;
517         decodeEndRule();
518 
519         transitionRulesInitialized = false;
520     }
521 
522     /**
523      * Sets the amount of time in ms that the clock is advanced during DST.
524      * @param millisSavedDuringDST the number of milliseconds the time is
525      * advanced with respect to standard time when the daylight savings rules
526      * are in effect. Typically one hour (+3600000). The amount could be negative,
527      * but not 0.
528      */
setDSTSavings(int millisSavedDuringDST)529     public void setDSTSavings(int millisSavedDuringDST) {
530         if (isFrozen()) {
531             throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
532         }
533 
534         if (millisSavedDuringDST == 0) {
535             throw new IllegalArgumentException();
536         }
537         dst = millisSavedDuringDST;
538 
539         transitionRulesInitialized = false;
540     }
541 
542     /**
543      * Returns the amount of time in ms that the clock is advanced during DST.
544      * @return the number of milliseconds the time is
545      * advanced with respect to standard time when the daylight savings rules
546      * are in effect. Typically one hour (3600000). The amount could be negative,
547      * but not 0.
548      */
549     @Override
getDSTSavings()550     public int getDSTSavings() {
551         return dst;
552     }
553 
554     /**
555      * Returns the java.util.SimpleTimeZone that this class wraps.
556      *
557     java.util.SimpleTimeZone unwrapSTZ() {
558         return (java.util.SimpleTimeZone) unwrap();
559     }
560     */
561 
562     // on JDK 1.4 and later, can't deserialize a SimpleTimeZone as a SimpleTimeZone...
readObject(java.io.ObjectInputStream in)563     private void readObject(java.io.ObjectInputStream in) throws IOException,
564         ClassNotFoundException {
565         in.defaultReadObject();
566         /*
567         String id = getID();
568         if (id!=null && !(zone instanceof java.util.SimpleTimeZone && zone.getID().equals(id))) {
569             // System.out.println("*** readjust " + zone.getClass().getName() +
570             // " " + zone.getID() + " ***");
571             java.util.SimpleTimeZone stz =
572                 new java.util.SimpleTimeZone(raw, id);
573             if (dst != 0) {
574                 stz.setDSTSavings(dst);
575                 // if it is 0, then there shouldn't be start/end rules and the default
576                 // behavior should be no dst
577             }
578 
579             if (xinfo != null) {
580                 xinfo.applyTo(stz);
581             }
582             zoneJDK = stz;
583         }
584         */
585         /* set all instance variables in this object
586          * to the values in zone
587          */
588          if (xinfo != null) {
589              xinfo.applyTo(this);
590          }
591     }
592 
593     /**
594      * Returns a string representation of this object.
595      * @return  a string representation of this object
596      */
597     @Override
toString()598     public String toString() {
599         return "SimpleTimeZone: " + getID();
600     }
601 
getSTZInfo()602     private STZInfo getSTZInfo() {
603         if (xinfo == null) {
604             xinfo = new STZInfo();
605         }
606         return xinfo;
607     }
608 
609     //  Use only for decodeStartRule() and decodeEndRule() where the year is not
610     //  available. Set February to 29 days to accomodate rules with that date
611     //  and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
612     //  The compareToRule() method adjusts to February 28 in non-leap years.
613     //
614     //  For actual getOffset() calculations, use TimeZone::monthLength() and
615     //  TimeZone::previousMonthLength() which take leap years into account.
616     //  We handle leap years assuming always
617     //  Gregorian, since we know they didn't have daylight time when
618     //  Gregorian calendar started.
619     private final static byte staticMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
620 
621     /**
622      * {@inheritDoc}
623      */
624     @Override
getOffset(int era, int year, int month, int day, int dayOfWeek, int millis)625     public int getOffset(int era, int year, int month, int day,
626                          int dayOfWeek, int millis)
627     {
628         // Check the month before calling Grego.monthLength(). This
629         // duplicates the test that occurs in the 7-argument getOffset(),
630         // however, this is unavoidable. We don't mind because this method, in
631         // fact, should not be called; internal code should always call the
632         // 7-argument getOffset(), and outside code should use Calendar.get(int
633         // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
634         // this method because it's public API. - liu 8/10/98
635         if(month < Calendar.JANUARY || month > Calendar.DECEMBER) {
636             throw new IllegalArgumentException();
637         }
638 
639         return getOffset(era, year, month, day, dayOfWeek, millis, Grego.monthLength(year, month));
640     }
641 
642     /**
643      * @deprecated This API is ICU internal only.
644      * @hide draft / provisional / internal are hidden on OHOS
645      */
646     @Deprecated
getOffset(int era, int year, int month, int day, int dayOfWeek, int millis, int monthLength)647     public int getOffset(int era, int year, int month, int day,
648                               int dayOfWeek, int millis,
649                               int monthLength)  {
650         // Check the month before calling Grego.monthLength(). This
651         // duplicates a test that occurs in the 9-argument getOffset(),
652         // however, this is unavoidable. We don't mind because this method, in
653         // fact, should not be called; internal code should always call the
654         // 9-argument getOffset(), and outside code should use Calendar.get(int
655         // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
656         // this method because it's public API. - liu 8/10/98
657         if(month < Calendar.JANUARY || month > Calendar.DECEMBER) {
658             throw new IllegalArgumentException();
659         }
660 
661         return getOffset(era, year, month, day, dayOfWeek, millis,
662                          Grego.monthLength(year, month), Grego.previousMonthLength(year, month));
663     }
664 
getOffset(int era, int year, int month, int day, int dayOfWeek, int millis, int monthLength, int prevMonthLength )665     private int getOffset(int era, int year, int month, int day,
666                   int dayOfWeek, int millis,
667                   int monthLength, int prevMonthLength ){
668 
669         if (true) {
670             /* Use this parameter checking code for normal operation.  Only one
671              * of these two blocks should actually get compiled into the class
672              * file.  */
673             if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
674                 || month < Calendar.JANUARY
675                 || month > Calendar.DECEMBER
676                 || day < 1
677                 || day > monthLength
678                 || dayOfWeek < Calendar.SUNDAY
679                 || dayOfWeek > Calendar.SATURDAY
680                 || millis < 0
681                 || millis >= Grego.MILLIS_PER_DAY
682                 || monthLength < 28
683                 || monthLength > 31
684                 || prevMonthLength < 28
685                 || prevMonthLength > 31) {
686                 throw new IllegalArgumentException();
687             }
688         }
689         //Eclipse stated the following is "dead code"
690         /*else {
691             // This parameter checking code is better for debugging, but
692             // overkill for normal operation.  Only one of these two blocks
693             // should actually get compiled into the class file.
694             if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
695                 throw new IllegalArgumentException("Illegal era " + era);
696             }
697             if (month < Calendar.JANUARY
698                 || month > Calendar.DECEMBER) {
699                 throw new IllegalArgumentException("Illegal month " + month);
700             }
701             if (day < 1
702                 || day > monthLength) {
703                 throw new IllegalArgumentException("Illegal day " + day+" max month len: "+monthLength);
704             }
705             if (dayOfWeek < Calendar.SUNDAY
706                 || dayOfWeek > Calendar.SATURDAY) {
707                 throw new IllegalArgumentException("Illegal day of week " + dayOfWeek);
708             }
709             if (millis < 0
710                 || millis >= Grego.MILLIS_PER_DAY) {
711                 throw new IllegalArgumentException("Illegal millis " + millis);
712             }
713             if (monthLength < 28
714                 || monthLength > 31) {
715                 throw new IllegalArgumentException("Illegal month length " + monthLength);
716             }
717             if (prevMonthLength < 28
718                 || prevMonthLength > 31) {
719                 throw new IllegalArgumentException("Illegal previous month length " + prevMonthLength);
720             }
721         }*/
722 
723         int result = raw;
724 
725         // Bail out if we are before the onset of daylight savings time
726         if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result;
727 
728         // Check for southern hemisphere.  We assume that the start and end
729         // month are different.
730         boolean southern = (startMonth > endMonth);
731 
732         // Compare the date to the starting and ending rules.+1 = date>rule, -1
733         // = date<rule, 0 = date==rule.
734         int startCompare = compareToRule(month, monthLength, prevMonthLength,
735                                          day, dayOfWeek, millis,
736                                          startTimeMode == UTC_TIME ? -raw : 0,
737                                          startMode, startMonth, startDayOfWeek,
738                                          startDay, startTime);
739         int endCompare = 0;
740 
741         /* We don't always have to compute endCompare.  For many instances,
742          * startCompare is enough to determine if we are in DST or not.  In the
743          * northern hemisphere, if we are before the start rule, we can't have
744          * DST.  In the southern hemisphere, if we are after the start rule, we
745          * must have DST.  This is reflected in the way the next if statement
746          * (not the one immediately following) short circuits. */
747         if (southern != (startCompare >= 0)) {
748             /* For the ending rule comparison, we add the dstSavings to the millis
749              * passed in to convert them from standard to wall time.  We then must
750              * normalize the millis to the range 0..millisPerDay-1. */
751             endCompare = compareToRule(month, monthLength, prevMonthLength,
752                                        day, dayOfWeek, millis,
753                                        endTimeMode == WALL_TIME ? dst :
754                                         (endTimeMode == UTC_TIME ? -raw : 0),
755                                        endMode, endMonth, endDayOfWeek,
756                                        endDay, endTime);
757         }
758 
759         // Check for both the northern and southern hemisphere cases.  We
760         // assume that in the northern hemisphere, the start rule is before the
761         // end rule within the calendar year, and vice versa for the southern
762         // hemisphere.
763         if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
764             (southern && (startCompare >= 0 || endCompare < 0)))
765             result += dst;
766 
767         return result;
768     }
769 
770     /**
771      * {@inheritDoc}
772      * @deprecated This API is ICU internal only.
773      * @hide draft / provisional / internal are hidden on OHOS
774      */
775     @Override
776     @Deprecated
getOffsetFromLocal(long date, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)777     public void getOffsetFromLocal(long date,
778             int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
779         offsets[0] = getRawOffset();
780         int fields[] = new int[6];
781         Grego.timeToFields(date, fields);
782         offsets[1] = getOffset(GregorianCalendar.AD,
783               fields[0], fields[1], fields[2],
784               fields[3], fields[5]) - offsets[0];
785 
786         boolean recalc = false;
787 
788         // Now, we need some adjustment
789         if (offsets[1] > 0) {
790             if ((nonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD
791                 || (nonExistingTimeOpt & STD_DST_MASK) != LOCAL_DST
792                 && (nonExistingTimeOpt & FORMER_LATTER_MASK) != LOCAL_LATTER) {
793                 date -= getDSTSavings();
794                 recalc = true;
795             }
796         } else {
797             if ((duplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST
798                 || (duplicatedTimeOpt & STD_DST_MASK) != LOCAL_STD
799                 && (duplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
800                 date -= getDSTSavings();
801                 recalc = true;
802             }
803         }
804 
805         if (recalc) {
806             Grego.timeToFields(date, fields);
807             offsets[1] = getOffset(GregorianCalendar.AD,
808                     fields[0], fields[1], fields[2],
809                     fields[3], fields[5]) - offsets[0];
810         }
811     }
812 
813     private static final int
814         DOM_MODE = 1,
815         DOW_IN_MONTH_MODE=2,
816         DOW_GE_DOM_MODE=3,
817         DOW_LE_DOM_MODE=4;
818 
819     /**
820      * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
821      * on whether the date is after, equal to, or before the rule date. The
822      * millis are compared directly against the ruleMillis, so any
823      * standard-daylight adjustments must be handled by the caller.
824      *
825      * @return  1 if the date is after the rule date, -1 if the date is before
826      *          the rule date, or 0 if the date is equal to the rule date.
827      */
compareToRule(int month, int monthLen, int prevMonthLen, int dayOfMonth, int dayOfWeek, int millis, int millisDelta, int ruleMode, int ruleMonth, int ruleDayOfWeek, int ruleDay, int ruleMillis)828     private int compareToRule(int month, int monthLen, int prevMonthLen,
829                                   int dayOfMonth,
830                                   int dayOfWeek, int millis, int millisDelta,
831                                   int ruleMode, int ruleMonth, int ruleDayOfWeek,
832                                   int ruleDay, int ruleMillis)
833     {
834         // Make adjustments for startTimeMode and endTimeMode
835 
836         millis += millisDelta;
837 
838         while (millis >= Grego.MILLIS_PER_DAY) {
839             millis -= Grego.MILLIS_PER_DAY;
840             ++dayOfMonth;
841             dayOfWeek = 1 + (dayOfWeek % 7); // dayOfWeek is one-based
842             if (dayOfMonth > monthLen) {
843                 dayOfMonth = 1;
844                 /* When incrementing the month, it is desirable to overflow
845                  * from DECEMBER to DECEMBER+1, since we use the result to
846                  * compare against a real month. Wraparound of the value
847                  * leads to bug 4173604. */
848                 ++month;
849             }
850         }
851         /*
852          * For some reasons, Sun Java 6 on Solaris/Linux has a problem with
853          * the while loop below (at least Java 6 up to build 1.6.0_02-b08).
854          * It looks the JRE messes up the variable 'millis' while executing
855          * the code in the while block.  The problem is not reproduced with
856          * JVM option -Xint, that is, it is likely a bug of the HotSpot
857          * adaptive compiler.  Moving 'millis += Grego.MILLIS_PER_DAY'
858          * to the end of this while block seems to resolve the problem.
859          * See ticket#5887 about the problem in detail.
860          */
861         while (millis < 0) {
862             //millis += Grego.MILLIS_PER_DAY;
863             --dayOfMonth;
864             dayOfWeek = 1 + ((dayOfWeek+5) % 7); // dayOfWeek is one-based
865             if (dayOfMonth < 1) {
866                 dayOfMonth = prevMonthLen;
867                 --month;
868             }
869             millis += Grego.MILLIS_PER_DAY;
870         }
871 
872         if (month < ruleMonth) return -1;
873         else if (month > ruleMonth) return 1;
874 
875         int ruleDayOfMonth = 0;
876 
877         // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
878         if (ruleDay > monthLen) {
879             ruleDay = monthLen;
880         }
881 
882         switch (ruleMode)
883         {
884         case DOM_MODE:
885             ruleDayOfMonth = ruleDay;
886             break;
887         case DOW_IN_MONTH_MODE:
888             // In this case ruleDay is the day-of-week-in-month
889             if (ruleDay > 0)
890                 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
891                     (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
892             else // Assume ruleDay < 0 here
893             {
894                 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
895                     (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
896             }
897             break;
898         case DOW_GE_DOM_MODE:
899             ruleDayOfMonth = ruleDay +
900                 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
901             break;
902         case DOW_LE_DOM_MODE:
903             ruleDayOfMonth = ruleDay -
904                 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
905             // Note at this point ruleDayOfMonth may be <1, although it will
906             // be >=1 for well-formed rules.
907             break;
908         }
909 
910         if (dayOfMonth < ruleDayOfMonth) return -1;
911         else if (dayOfMonth > ruleDayOfMonth) return 1;
912 
913         if (millis < ruleMillis){
914                 return -1;
915         }else if (millis > ruleMillis){
916                 return 1;
917         }else{
918                 return 0;
919         }
920     }
921 
922     // data needed for streaming mutated SimpleTimeZones in JDK14
923     private int raw;// the TimeZone's raw GMT offset
924     private int dst = 3600000;
925     private STZInfo xinfo = null;
926     private int startMonth, startDay, startDayOfWeek;   // the month, day, DOW, and time DST starts
927     private int startTime;
928     private int startTimeMode, endTimeMode; // Mode for startTime, endTime; see TimeMode
929     private int endMonth, endDay, endDayOfWeek; // the month, day, DOW, and time DST ends
930     private int endTime;
931     private int startYear;  // the year these DST rules took effect
932     private boolean useDaylight; // flag indicating whether this TimeZone uses DST
933     private int startMode, endMode;   // flags indicating what kind of rules the DST rules are
934 
935     /**
936      * Overrides TimeZone
937      * Queries if this time zone uses Daylight Saving Time.
938      */
939     @Override
useDaylightTime()940     public boolean useDaylightTime(){
941         return useDaylight;
942     }
943 
944     /**
945      * {@inheritDoc}
946      */
947     @Override
observesDaylightTime()948     public boolean observesDaylightTime() {
949         return useDaylight;
950     }
951 
952     /**
953      * Overrides TimeZone
954      * Queries if the give date is in Daylight Saving Time.
955      */
956     @Override
inDaylightTime(Date date)957     public boolean inDaylightTime(Date date){
958         GregorianCalendar gc = new GregorianCalendar(this);
959         gc.setTime(date);
960         return gc.inDaylightTime();
961     }
962 
963     /**
964      * Internal construction method.
965      */
construct(int _raw, int _startMonth, int _startDay, int _startDayOfWeek, int _startTime, int _startTimeMode, int _endMonth, int _endDay, int _endDayOfWeek, int _endTime, int _endTimeMode, int _dst)966     private void construct(int _raw,
967                            int _startMonth,
968                            int _startDay,
969                            int _startDayOfWeek,
970                            int _startTime,
971                            int _startTimeMode,
972                            int _endMonth,
973                            int _endDay,
974                            int _endDayOfWeek,
975                            int _endTime,
976                            int _endTimeMode,
977                            int _dst) {
978         raw            = _raw;
979         startMonth     = _startMonth;
980         startDay       = _startDay;
981         startDayOfWeek = _startDayOfWeek;
982         startTime      = _startTime;
983         startTimeMode  = _startTimeMode;
984         endMonth       = _endMonth;
985         endDay         = _endDay;
986         endDayOfWeek   = _endDayOfWeek;
987         endTime        = _endTime;
988         endTimeMode    = _endTimeMode;
989         dst            = _dst;
990         startYear      = 0;
991         startMode      = DOM_MODE;
992         endMode        = DOM_MODE;
993 
994         decodeRules();
995 
996         if (_dst == 0) {
997             throw new IllegalArgumentException();
998         }
999     }
decodeRules()1000     private void decodeRules(){
1001         decodeStartRule();
1002         decodeEndRule();
1003     }
1004 
1005     /**
1006      * Decode the start rule and validate the parameters.  The parameters are
1007      * expected to be in encoded form, which represents the various rule modes
1008      * by negating or zeroing certain values.  Representation formats are:
1009      * <p>
1010      * <pre>
1011      *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
1012      *            ------------  -----  --------  --------  ----------
1013      * month       0..11        same    same      same     don't care
1014      * day        -5..5         1..31   1..31    -1..-31   0
1015      * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
1016      * time        0..ONEDAY    same    same      same     don't care
1017      * </pre>
1018      * The range for month does not include UNDECIMBER since this class is
1019      * really specific to GregorianCalendar, which does not use that month.
1020      * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
1021      * end rule is an exclusive limit point.  That is, the range of times that
1022      * are in DST include those >= the start and < the end.  For this reason,
1023      * it should be possible to specify an end of ONEDAY in order to include the
1024      * entire day.  Although this is equivalent to time 0 of the following day,
1025      * it's not always possible to specify that, for example, on December 31.
1026      * While arguably the start range should still be 0..ONEDAY-1, we keep
1027      * the start and end ranges the same for consistency.
1028      */
decodeStartRule()1029     private void decodeStartRule() {
1030         useDaylight = (startDay != 0) && (endDay != 0);
1031         if (useDaylight && dst == 0) {
1032             dst = Grego.MILLIS_PER_DAY;
1033         }
1034         if (startDay != 0) {
1035             if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
1036                 throw new IllegalArgumentException();
1037             }
1038             if (startTime < 0 || startTime > Grego.MILLIS_PER_DAY ||
1039                 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
1040                 throw new IllegalArgumentException();
1041             }
1042             if (startDayOfWeek == 0) {
1043                 startMode = DOM_MODE;
1044             } else {
1045                 if (startDayOfWeek > 0) {
1046                     startMode = DOW_IN_MONTH_MODE;
1047                 } else {
1048                     startDayOfWeek = -startDayOfWeek;
1049                     if (startDay > 0) {
1050                         startMode = DOW_GE_DOM_MODE;
1051                     } else {
1052                         startDay = -startDay;
1053                         startMode = DOW_LE_DOM_MODE;
1054                     }
1055                 }
1056                 if (startDayOfWeek > Calendar.SATURDAY) {
1057                     throw new IllegalArgumentException();
1058                 }
1059             }
1060             if (startMode == DOW_IN_MONTH_MODE) {
1061                 if (startDay < -5 || startDay > 5) {
1062                     throw new IllegalArgumentException();
1063                 }
1064             } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
1065                 throw new IllegalArgumentException();
1066             }
1067         }
1068     }
1069 
1070     /**
1071      * Decode the end rule and validate the parameters.  This method is exactly
1072      * analogous to decodeStartRule().
1073      * @see #decodeStartRule
1074      */
decodeEndRule()1075     private void decodeEndRule() {
1076         useDaylight = (startDay != 0) && (endDay != 0);
1077         if (useDaylight && dst == 0) {
1078             dst = Grego.MILLIS_PER_DAY;
1079         }
1080         if (endDay != 0) {
1081             if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
1082                 throw new IllegalArgumentException();
1083             }
1084             if (endTime < 0 || endTime > Grego.MILLIS_PER_DAY ||
1085                 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
1086                 throw new IllegalArgumentException();
1087             }
1088             if (endDayOfWeek == 0) {
1089                 endMode = DOM_MODE;
1090             } else {
1091                 if (endDayOfWeek > 0) {
1092                     endMode = DOW_IN_MONTH_MODE;
1093                 } else {
1094                     endDayOfWeek = -endDayOfWeek;
1095                     if (endDay > 0) {
1096                         endMode = DOW_GE_DOM_MODE;
1097                     } else {
1098                         endDay = -endDay;
1099                         endMode = DOW_LE_DOM_MODE;
1100                     }
1101                 }
1102                 if (endDayOfWeek > Calendar.SATURDAY) {
1103                     throw new IllegalArgumentException();
1104                 }
1105             }
1106             if (endMode == DOW_IN_MONTH_MODE) {
1107                 if (endDay < -5 || endDay > 5) {
1108                     throw new IllegalArgumentException();
1109                 }
1110             } else if (endDay<1 || endDay > staticMonthLength[endMonth]) {
1111                 throw new IllegalArgumentException();
1112             }
1113         }
1114     }
1115 
1116     /**
1117      * Overrides equals.
1118      * @return true if obj is a SimpleTimeZone equivalent to this
1119      */
1120     @Override
equals(Object obj)1121     public boolean equals(Object obj){
1122         if (this == obj) return true;
1123         if (obj == null || getClass() != obj.getClass()) return false;
1124         SimpleTimeZone that = (SimpleTimeZone) obj;
1125         return raw     == that.raw &&
1126             useDaylight     == that.useDaylight &&
1127             idEquals(getID(),that.getID()) &&
1128             (!useDaylight
1129              // Only check rules if using DST
1130              || (dst            == that.dst &&
1131                  startMode      == that.startMode &&
1132                  startMonth     == that.startMonth &&
1133                  startDay       == that.startDay &&
1134                  startDayOfWeek == that.startDayOfWeek &&
1135                  startTime      == that.startTime &&
1136                  startTimeMode  == that.startTimeMode &&
1137                  endMode        == that.endMode &&
1138                  endMonth       == that.endMonth &&
1139                  endDay         == that.endDay &&
1140                  endDayOfWeek   == that.endDayOfWeek &&
1141                  endTime        == that.endTime &&
1142                  endTimeMode    == that.endTimeMode &&
1143                  startYear      == that.startYear ));
1144 
1145     }
idEquals(String id1, String id2)1146     private boolean idEquals(String id1, String id2){
1147         if(id1==null && id2==null){
1148             return true;
1149         }
1150         if(id1!=null && id2!=null){
1151             return id1.equals(id2);
1152         }
1153         return false;
1154     }
1155 
1156     /**
1157      * Overrides hashCode.
1158      * @return a hash code value for this object.
1159      */
1160     @Override
hashCode()1161     public int hashCode(){
1162         int ret = super.hashCode()
1163                     + raw ^ (raw >>> 8)
1164                     + (useDaylight ? 0 : 1);
1165         if(!useDaylight){
1166                 ret += dst ^ (dst >>> 10) +
1167                         startMode ^ (startMode>>>11) +
1168                         startMonth ^ (startMonth>>>12) +
1169                         startDay ^ (startDay>>>13) +
1170                         startDayOfWeek ^ (startDayOfWeek>>>14) +
1171                         startTime ^ (startTime>>>15) +
1172                         startTimeMode ^ (startTimeMode>>>16) +
1173                         endMode ^ (endMode>>>17) +
1174                         endMonth ^ (endMonth>>>18) +
1175                         endDay ^ (endDay>>>19) +
1176                         endDayOfWeek ^ (endDayOfWeek>>>20) +
1177                         endTime ^ (endTime>>>21) +
1178                         endTimeMode ^ (endTimeMode>>>22) +
1179                         startYear ^ (startYear>>>23);
1180         }
1181                 return ret;
1182     }
1183 
1184     /**
1185      * Overrides clone.
1186      */
1187     @Override
clone()1188     public Object clone() {
1189         if (isFrozen()) {
1190             return this;
1191         }
1192         return cloneAsThawed();
1193     }
1194 
1195     /**
1196      * Returns true if this zone has the same rules and offset as another zone.
1197      * @param othr the TimeZone object to be compared with
1198      * @return true if the given zone has the same rules and offset as this one
1199      */
1200     @Override
hasSameRules(TimeZone othr)1201     public boolean hasSameRules(TimeZone othr) {
1202         if (this == othr) {
1203             return true;
1204         }
1205         if(!(othr instanceof SimpleTimeZone)){
1206             return false;
1207         }
1208         SimpleTimeZone other = (SimpleTimeZone)othr;
1209         return other != null &&
1210         raw     == other.raw &&
1211         useDaylight     == other.useDaylight &&
1212         (!useDaylight
1213          // Only check rules if using DST
1214          || (dst     == other.dst &&
1215              startMode      == other.startMode &&
1216              startMonth     == other.startMonth &&
1217              startDay       == other.startDay &&
1218              startDayOfWeek == other.startDayOfWeek &&
1219              startTime      == other.startTime &&
1220              startTimeMode  == other.startTimeMode &&
1221              endMode        == other.endMode &&
1222              endMonth       == other.endMonth &&
1223              endDay         == other.endDay &&
1224              endDayOfWeek   == other.endDayOfWeek &&
1225              endTime        == other.endTime &&
1226              endTimeMode    == other.endTimeMode &&
1227              startYear      == other.startYear));
1228     }
1229 
1230     // BasicTimeZone methods
1231 
1232     /**
1233      * {@inheritDoc}
1234      */
1235     @Override
getNextTransition(long base, boolean inclusive)1236     public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
1237         if (!useDaylight) {
1238             return null;
1239         }
1240 
1241         initTransitionRules();
1242         long firstTransitionTime = firstTransition.getTime();
1243         if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
1244             return firstTransition;
1245         }
1246         Date stdDate = stdRule.getNextStart(base, dstRule.getRawOffset(), dstRule.getDSTSavings(),
1247                                             inclusive);
1248         Date dstDate = dstRule.getNextStart(base, stdRule.getRawOffset(), stdRule.getDSTSavings(),
1249                                             inclusive);
1250         if (stdDate != null && (dstDate == null || stdDate.before(dstDate))) {
1251             return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule);
1252         }
1253         if (dstDate != null && (stdDate == null || dstDate.before(stdDate))) {
1254             return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule);
1255         }
1256         return null;
1257     }
1258 
1259     /**
1260      * {@inheritDoc}
1261      */
1262     @Override
getPreviousTransition(long base, boolean inclusive)1263     public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
1264         if (!useDaylight) {
1265             return null;
1266         }
1267 
1268         initTransitionRules();
1269         long firstTransitionTime = firstTransition.getTime();
1270         if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
1271             return null;
1272         }
1273         Date stdDate = stdRule.getPreviousStart(base, dstRule.getRawOffset(),
1274                                                 dstRule.getDSTSavings(), inclusive);
1275         Date dstDate = dstRule.getPreviousStart(base, stdRule.getRawOffset(),
1276                                                 stdRule.getDSTSavings(), inclusive);
1277         if (stdDate != null && (dstDate == null || stdDate.after(dstDate))) {
1278             return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule);
1279         }
1280         if (dstDate != null && (stdDate == null || dstDate.after(stdDate))) {
1281             return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule);
1282         }
1283         return null;
1284     }
1285 
1286     /**
1287      * {@inheritDoc}
1288      */
1289     @Override
getTimeZoneRules()1290     public TimeZoneRule[] getTimeZoneRules() {
1291         initTransitionRules();
1292 
1293         int size = useDaylight ? 3 : 1;
1294         TimeZoneRule[] rules = new TimeZoneRule[size];
1295         rules[0] = initialRule;
1296         if (useDaylight) {
1297             rules[1] = stdRule;
1298             rules[2] = dstRule;
1299         }
1300         return rules;
1301     }
1302 
1303     private transient boolean transitionRulesInitialized;
1304     private transient InitialTimeZoneRule initialRule;
1305     private transient TimeZoneTransition firstTransition;
1306     private transient AnnualTimeZoneRule stdRule;
1307     private transient AnnualTimeZoneRule dstRule;
1308 
initTransitionRules()1309     private synchronized void initTransitionRules() {
1310         if (transitionRulesInitialized) {
1311             return;
1312         }
1313         if (useDaylight) {
1314             DateTimeRule dtRule = null;
1315             int timeRuleType;
1316             long firstStdStart, firstDstStart;
1317 
1318             // Create a TimeZoneRule for daylight saving time
1319             timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME :
1320                 ((startTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME);
1321             switch (startMode) {
1322             case DOM_MODE:
1323              dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
1324              break;
1325             case DOW_IN_MONTH_MODE:
1326              dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime,
1327                                        timeRuleType);
1328              break;
1329             case DOW_GE_DOM_MODE:
1330              dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime,
1331                                        timeRuleType);
1332              break;
1333             case DOW_LE_DOM_MODE:
1334              dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime,
1335                                        timeRuleType);
1336              break;
1337             }
1338             // For now, use ID + "(DST)" as the name
1339             dstRule = new AnnualTimeZoneRule(getID() + "(DST)", getRawOffset(), getDSTSavings(),
1340                  dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR);
1341 
1342             // Calculate the first DST start time
1343             firstDstStart = dstRule.getFirstStart(getRawOffset(), 0).getTime();
1344 
1345             // Create a TimeZoneRule for standard time
1346             timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME :
1347                 ((endTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME);
1348             switch (endMode) {
1349             case DOM_MODE:
1350                 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
1351                 break;
1352             case DOW_IN_MONTH_MODE:
1353                 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
1354                 break;
1355             case DOW_GE_DOM_MODE:
1356                 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime,
1357                                           timeRuleType);
1358                 break;
1359             case DOW_LE_DOM_MODE:
1360                 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime,
1361                                           timeRuleType);
1362                 break;
1363             }
1364             // For now, use ID + "(STD)" as the name
1365             stdRule = new AnnualTimeZoneRule(getID() + "(STD)", getRawOffset(), 0,
1366                     dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR);
1367 
1368             // Calculate the first STD start time
1369             firstStdStart = stdRule.getFirstStart(getRawOffset(), dstRule.getDSTSavings()).getTime();
1370 
1371             // Create a TimeZoneRule for initial time
1372             if (firstStdStart < firstDstStart) {
1373                 initialRule = new InitialTimeZoneRule(getID() + "(DST)", getRawOffset(),
1374                                                       dstRule.getDSTSavings());
1375                 firstTransition = new TimeZoneTransition(firstStdStart, initialRule, stdRule);
1376             } else {
1377                 initialRule = new InitialTimeZoneRule(getID() + "(STD)", getRawOffset(), 0);
1378                 firstTransition = new TimeZoneTransition(firstDstStart, initialRule, dstRule);
1379             }
1380 
1381         } else {
1382             // Create a TimeZoneRule for initial time
1383             initialRule = new InitialTimeZoneRule(getID(), getRawOffset(), 0);
1384         }
1385         transitionRulesInitialized = true;
1386     }
1387 
1388     // Freezable stuffs
1389     private volatile transient boolean isFrozen = false;
1390 
1391     /**
1392      * {@inheritDoc}
1393      */
1394     @Override
isFrozen()1395     public boolean isFrozen() {
1396         return isFrozen;
1397     }
1398 
1399     /**
1400      * {@inheritDoc}
1401      */
1402     @Override
freeze()1403     public TimeZone freeze() {
1404         isFrozen = true;
1405         return this;
1406     }
1407 
1408     /**
1409      * {@inheritDoc}
1410      */
1411     @Override
cloneAsThawed()1412     public TimeZone cloneAsThawed() {
1413         SimpleTimeZone tz = (SimpleTimeZone)super.cloneAsThawed();
1414         tz.isFrozen = false;
1415         return tz;
1416     }
1417 }
1418