• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29  *
30  *   The original version of this source code and documentation is copyrighted
31  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32  * materials are provided under terms of a License Agreement between Taligent
33  * and Sun. This technology is protected by multiple US and International
34  * patents. This notice and attribution to Taligent may not be removed.
35  *   Taligent is a registered trademark of Taligent, Inc.
36  *
37  */
38 
39 package java.util;
40 
41 import java.io.ObjectInputStream;
42 import java.io.ObjectOutputStream;
43 import java.io.IOException;
44 import java.io.InvalidObjectException;
45 import sun.util.calendar.CalendarSystem;
46 import sun.util.calendar.CalendarUtils;
47 import sun.util.calendar.BaseCalendar;
48 import sun.util.calendar.Gregorian;
49 
50 /**
51  * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone}
52  * that represents a time zone for use with a Gregorian calendar.
53  * The class holds an offset from GMT, called <em>raw offset</em>, and start
54  * and end rules for a daylight saving time schedule.  Since it only holds
55  * single values for each, it cannot handle historical changes in the offset
56  * from GMT and the daylight saving schedule, except that the {@link
57  * #setStartYear setStartYear} method can specify the year when the daylight
58  * saving time schedule starts in effect.
59  * <p>
60  * To construct a {@code SimpleTimeZone} with a daylight saving time
61  * schedule, the schedule can be described with a set of rules,
62  * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time
63  * starts or ends is specified by a combination of <em>month</em>,
64  * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em>
65  * value is represented by a Calendar {@link Calendar#MONTH MONTH} field
66  * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is
67  * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value,
68  * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations
69  * are as follows.
70  *
71  * <ul>
72  * <li><b>Exact day of month</b><br>
73  * To specify an exact day of month, set the <em>month</em> and
74  * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For
75  * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH
76  * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li>
77  *
78  * <li><b>Day of week on or after day of month</b><br>
79  * To specify a day of week on or after an exact day of month, set the
80  * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on
81  * or after which the rule is applied, and <em>day-of-week</em> to a negative {@link
82  * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the
83  * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL},
84  * <em>day-of-month</em> to 8, and <em>day-of-week</em> to {@code -}{@link
85  * Calendar#SUNDAY SUNDAY}.</li>
86  *
87  * <li><b>Day of week on or before day of month</b><br>
88  * To specify a day of the week on or before an exact day of the month, set
89  * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For
90  * example, to specify the last Wednesday on or before the 21st of March, set
91  * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21
92  * and <em>day-of-week</em> is {@code -}{@link Calendar#WEDNESDAY WEDNESDAY}. </li>
93  *
94  * <li><b>Last day-of-week of month</b><br>
95  * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a
96  * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to
97  * -1. For example, to specify the last Sunday of October, set <em>month</em>
98  * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link
99  * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1.  </li>
100  *
101  * </ul>
102  * The time of the day at which daylight saving time starts or ends is
103  * specified by a millisecond value within the day. There are three kinds of
104  * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link
105  * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight
106  * saving time ends
107  * at 2:00 am in the wall clock time, it can be specified by 7200000
108  * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time
109  * for an <em>end-rule</em> means the same thing as the daylight time.
110  * <p>
111  * The following are examples of parameters for constructing time zone objects.
112  * <pre><code>
113  *      // Base GMT offset: -8:00
114  *      // DST starts:      at 2:00am in standard time
115  *      //                  on the first Sunday in April
116  *      // DST ends:        at 2:00am in daylight time
117  *      //                  on the last Sunday in October
118  *      // Save:            1 hour
119  *      SimpleTimeZone(-28800000,
120  *                     "America/Los_Angeles",
121  *                     Calendar.APRIL, 1, -Calendar.SUNDAY,
122  *                     7200000,
123  *                     Calendar.OCTOBER, -1, Calendar.SUNDAY,
124  *                     7200000,
125  *                     3600000)
126  *
127  *      // Base GMT offset: +1:00
128  *      // DST starts:      at 1:00am in UTC time
129  *      //                  on the last Sunday in March
130  *      // DST ends:        at 1:00am in UTC time
131  *      //                  on the last Sunday in October
132  *      // Save:            1 hour
133  *      SimpleTimeZone(3600000,
134  *                     "Europe/Paris",
135  *                     Calendar.MARCH, -1, Calendar.SUNDAY,
136  *                     3600000, SimpleTimeZone.UTC_TIME,
137  *                     Calendar.OCTOBER, -1, Calendar.SUNDAY,
138  *                     3600000, SimpleTimeZone.UTC_TIME,
139  *                     3600000)
140  * </code></pre>
141  * These parameter rules are also applicable to the set rule methods, such as
142  * {@code setStartRule}.
143  *
144  * @since 1.1
145  * @see      Calendar
146  * @see      GregorianCalendar
147  * @see      TimeZone
148  * @author   David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
149  */
150 
151 public class SimpleTimeZone extends TimeZone {
152     /**
153      * Constructs a SimpleTimeZone with the given base time zone offset from GMT
154      * and time zone ID with no daylight saving time schedule.
155      *
156      * @param rawOffset  The base time zone offset in milliseconds to GMT.
157      * @param ID         The time zone name that is given to this instance.
158      */
SimpleTimeZone(int rawOffset, String ID)159     public SimpleTimeZone(int rawOffset, String ID)
160     {
161         this.rawOffset = rawOffset;
162         setID (ID);
163         dstSavings = millisPerHour; // In case user sets rules later
164     }
165 
166     /**
167      * Constructs a SimpleTimeZone with the given base time zone offset from
168      * GMT, time zone ID, and rules for starting and ending the daylight
169      * time.
170      * Both {@code startTime} and {@code endTime} are specified to be
171      * represented in the wall clock time. The amount of daylight saving is
172      * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is
173      * equivalent to:
174      * <pre><code>
175      *     SimpleTimeZone(rawOffset,
176      *                    ID,
177      *                    startMonth,
178      *                    startDay,
179      *                    startDayOfWeek,
180      *                    startTime,
181      *                    SimpleTimeZone.{@link #WALL_TIME},
182      *                    endMonth,
183      *                    endDay,
184      *                    endDayOfWeek,
185      *                    endTime,
186      *                    SimpleTimeZone.{@link #WALL_TIME},
187      *                    3600000)
188      * </code></pre>
189      *
190      * @param rawOffset       The given base time zone offset from GMT.
191      * @param ID              The time zone ID which is given to this object.
192      * @param startMonth      The daylight saving time starting month. Month is
193      *                        a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0
194      *                        for January).
195      * @param startDay        The day of the month on which the daylight saving time starts.
196      *                        See the class description for the special cases of this parameter.
197      * @param startDayOfWeek  The daylight saving time starting day-of-week.
198      *                        See the class description for the special cases of this parameter.
199      * @param startTime       The daylight saving time starting time in local wall clock
200      *                        time (in milliseconds within the day), which is local
201      *                        standard time in this case.
202      * @param endMonth        The daylight saving time ending month. Month is
203      *                        a {@link Calendar#MONTH MONTH} field
204      *                        value (0-based. e.g., 9 for October).
205      * @param endDay          The day of the month on which the daylight saving time ends.
206      *                        See the class description for the special cases of this parameter.
207      * @param endDayOfWeek    The daylight saving time ending day-of-week.
208      *                        See the class description for the special cases of this parameter.
209      * @param endTime         The daylight saving ending time in local wall clock time,
210      *                        (in milliseconds within the day) which is local daylight
211      *                        time in this case.
212      * @throws    IllegalArgumentException if the month, day, dayOfWeek, or time
213      * parameters are out of range for the start or end rule
214      */
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)215     public SimpleTimeZone(int rawOffset, String ID,
216                           int startMonth, int startDay, int startDayOfWeek, int startTime,
217                           int endMonth, int endDay, int endDayOfWeek, int endTime)
218     {
219         this(rawOffset, ID,
220              startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
221              endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
222              millisPerHour);
223     }
224 
225     /**
226      * Constructs a SimpleTimeZone with the given base time zone offset from
227      * GMT, time zone ID, and rules for starting and ending the daylight
228      * time.
229      * Both {@code startTime} and {@code endTime} are assumed to be
230      * represented in the wall clock time. This constructor is equivalent to:
231      * <pre><code>
232      *     SimpleTimeZone(rawOffset,
233      *                    ID,
234      *                    startMonth,
235      *                    startDay,
236      *                    startDayOfWeek,
237      *                    startTime,
238      *                    SimpleTimeZone.{@link #WALL_TIME},
239      *                    endMonth,
240      *                    endDay,
241      *                    endDayOfWeek,
242      *                    endTime,
243      *                    SimpleTimeZone.{@link #WALL_TIME},
244      *                    dstSavings)
245      * </code></pre>
246      *
247      * @param rawOffset       The given base time zone offset from GMT.
248      * @param ID              The time zone ID which is given to this object.
249      * @param startMonth      The daylight saving time starting month. Month is
250      *                        a {@link Calendar#MONTH MONTH} field
251      *                        value (0-based. e.g., 0 for January).
252      * @param startDay        The day of the month on which the daylight saving time starts.
253      *                        See the class description for the special cases of this parameter.
254      * @param startDayOfWeek  The daylight saving time starting day-of-week.
255      *                        See the class description for the special cases of this parameter.
256      * @param startTime       The daylight saving time starting time in local wall clock
257      *                        time, which is local standard time in this case.
258      * @param endMonth        The daylight saving time ending month. Month is
259      *                        a {@link Calendar#MONTH MONTH} field
260      *                        value (0-based. e.g., 9 for October).
261      * @param endDay          The day of the month on which the daylight saving time ends.
262      *                        See the class description for the special cases of this parameter.
263      * @param endDayOfWeek    The daylight saving time ending day-of-week.
264      *                        See the class description for the special cases of this parameter.
265      * @param endTime         The daylight saving ending time in local wall clock time,
266      *                        which is local daylight time in this case.
267      * @param dstSavings      The amount of time in milliseconds saved during
268      *                        daylight saving time.
269      * @throws    IllegalArgumentException if the month, day, dayOfWeek, or time
270      * parameters are out of range for the start or end rule
271      * @since 1.2
272      */
SimpleTimeZone(int rawOffset, String ID, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int dstSavings)273     public SimpleTimeZone(int rawOffset, String ID,
274                           int startMonth, int startDay, int startDayOfWeek, int startTime,
275                           int endMonth, int endDay, int endDayOfWeek, int endTime,
276                           int dstSavings)
277     {
278         this(rawOffset, ID,
279              startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
280              endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
281              dstSavings);
282     }
283 
284     /**
285      * Constructs a SimpleTimeZone with the given base time zone offset from
286      * GMT, time zone ID, and rules for starting and ending the daylight
287      * time.
288      * This constructor takes the full set of the start and end rules
289      * parameters, including modes of {@code startTime} and
290      * {@code endTime}. The mode specifies either {@link #WALL_TIME wall
291      * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC
292      * time}.
293      *
294      * @param rawOffset       The given base time zone offset from GMT.
295      * @param ID              The time zone ID which is given to this object.
296      * @param startMonth      The daylight saving time starting month. Month is
297      *                        a {@link Calendar#MONTH MONTH} field
298      *                        value (0-based. e.g., 0 for January).
299      * @param startDay        The day of the month on which the daylight saving time starts.
300      *                        See the class description for the special cases of this parameter.
301      * @param startDayOfWeek  The daylight saving time starting day-of-week.
302      *                        See the class description for the special cases of this parameter.
303      * @param startTime       The daylight saving time starting time in the time mode
304      *                        specified by {@code startTimeMode}.
305      * @param startTimeMode   The mode of the start time specified by startTime.
306      * @param endMonth        The daylight saving time ending month. Month is
307      *                        a {@link Calendar#MONTH MONTH} field
308      *                        value (0-based. e.g., 9 for October).
309      * @param endDay          The day of the month on which the daylight saving time ends.
310      *                        See the class description for the special cases of this parameter.
311      * @param endDayOfWeek    The daylight saving time ending day-of-week.
312      *                        See the class description for the special cases of this parameter.
313      * @param endTime         The daylight saving ending time in time mode
314      *                        specified by {@code endTimeMode}.
315      * @param endTimeMode     The mode of the end time specified by endTime
316      * @param dstSavings      The amount of time in milliseconds saved during
317      *                        daylight saving time.
318      *
319      * @throws    IllegalArgumentException if the month, day, dayOfWeek, time more, or
320      * time parameters are out of range for the start or end rule, or if a time mode
321      * value is invalid.
322      *
323      * @see #WALL_TIME
324      * @see #STANDARD_TIME
325      * @see #UTC_TIME
326      *
327      * @since 1.4
328      */
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)329     public SimpleTimeZone(int rawOffset, String ID,
330                           int startMonth, int startDay, int startDayOfWeek,
331                           int startTime, int startTimeMode,
332                           int endMonth, int endDay, int endDayOfWeek,
333                           int endTime, int endTimeMode,
334                           int dstSavings) {
335 
336         setID(ID);
337         this.rawOffset      = rawOffset;
338         this.startMonth     = startMonth;
339         this.startDay       = startDay;
340         this.startDayOfWeek = startDayOfWeek;
341         this.startTime      = startTime;
342         this.startTimeMode  = startTimeMode;
343         this.endMonth       = endMonth;
344         this.endDay         = endDay;
345         this.endDayOfWeek   = endDayOfWeek;
346         this.endTime        = endTime;
347         this.endTimeMode    = endTimeMode;
348         this.dstSavings     = dstSavings;
349 
350         // this.useDaylight is set by decodeRules
351         decodeRules();
352         if (dstSavings <= 0) {
353             throw new IllegalArgumentException("Illegal daylight saving value: " + dstSavings);
354         }
355     }
356 
357     /**
358      * Sets the daylight saving time starting year.
359      *
360      * @param year  The daylight saving starting year.
361      */
setStartYear(int year)362     public void setStartYear(int year)
363     {
364         startYear = year;
365         invalidateCache();
366     }
367 
368     /**
369      * Sets the daylight saving time start rule. For example, if daylight saving
370      * time starts on the first Sunday in April at 2 am in local wall clock
371      * time, you can set the start rule by calling:
372      * <pre>{@code setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);}</pre>
373      *
374      * @param startMonth      The daylight saving time starting month. Month is
375      *                        a {@link Calendar#MONTH MONTH} field
376      *                        value (0-based. e.g., 0 for January).
377      * @param startDay        The day of the month on which the daylight saving time starts.
378      *                        See the class description for the special cases of this parameter.
379      * @param startDayOfWeek  The daylight saving time starting day-of-week.
380      *                        See the class description for the special cases of this parameter.
381      * @param startTime       The daylight saving time starting time in local wall clock
382      *                        time, which is local standard time in this case.
383      * @throws    IllegalArgumentException if the {@code startMonth}, {@code startDay},
384      * {@code startDayOfWeek}, or {@code startTime} parameters are out of range
385      */
setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)386     public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)
387     {
388         this.startMonth = startMonth;
389         this.startDay = startDay;
390         this.startDayOfWeek = startDayOfWeek;
391         this.startTime = startTime;
392         startTimeMode = WALL_TIME;
393         decodeStartRule();
394         invalidateCache();
395     }
396 
397     /**
398      * Sets the daylight saving time start rule to a fixed date within a month.
399      * This method is equivalent to:
400      * <pre>{@code setStartRule(startMonth, startDay, 0, startTime)}</pre>
401      *
402      * @param startMonth      The daylight saving time starting month. Month is
403      *                        a {@link Calendar#MONTH MONTH} field
404      *                        value (0-based. e.g., 0 for January).
405      * @param startDay        The day of the month on which the daylight saving time starts.
406      * @param startTime       The daylight saving time starting time in local wall clock
407      *                        time, which is local standard time in this case.
408      *                        See the class description for the special cases of this parameter.
409      * @throws    IllegalArgumentException if the {@code startMonth},
410      * {@code startDayOfMonth}, or {@code startTime} parameters are out of range
411      * @since 1.2
412      */
setStartRule(int startMonth, int startDay, int startTime)413     public void setStartRule(int startMonth, int startDay, int startTime) {
414         setStartRule(startMonth, startDay, 0, startTime);
415     }
416 
417     /**
418      * Sets the daylight saving time start rule to a weekday before or after the given date within
419      * a month, e.g., the first Monday on or after the 8th.
420      *
421      * @param startMonth      The daylight saving time starting month. Month is
422      *                        a {@link Calendar#MONTH MONTH} field
423      *                        value (0-based. e.g., 0 for January).
424      * @param startDay        The day of the month on which the daylight saving time starts.
425      * @param startDayOfWeek  The daylight saving time starting day-of-week.
426      * @param startTime       The daylight saving time starting time in local wall clock
427      *                        time, which is local standard time in this case.
428      * @param after           If true, this rule selects the first {@code dayOfWeek} on or
429      *                        <em>after</em> {@code dayOfMonth}.  If false, this rule
430      *                        selects the last {@code dayOfWeek} on or <em>before</em>
431      *                        {@code dayOfMonth}.
432      * @throws    IllegalArgumentException if the {@code startMonth}, {@code startDay},
433      * {@code startDayOfWeek}, or {@code startTime} parameters are out of range
434      * @since 1.2
435      */
setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime, boolean after)436     public void setStartRule(int startMonth, int startDay, int startDayOfWeek,
437                              int startTime, boolean after)
438     {
439         // TODO: this method doesn't check the initial values of dayOfMonth or dayOfWeek.
440         if (after) {
441             setStartRule(startMonth, startDay, -startDayOfWeek, startTime);
442         } else {
443             setStartRule(startMonth, -startDay, -startDayOfWeek, startTime);
444         }
445     }
446 
447     /**
448      * Sets the daylight saving time end rule. For example, if daylight saving time
449      * ends on the last Sunday in October at 2 am in wall clock time,
450      * you can set the end rule by calling:
451      * {@code setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);}
452      *
453      * @param endMonth        The daylight saving time ending month. Month is
454      *                        a {@link Calendar#MONTH MONTH} field
455      *                        value (0-based. e.g., 9 for October).
456      * @param endDay          The day of the month on which the daylight saving time ends.
457      *                        See the class description for the special cases of this parameter.
458      * @param endDayOfWeek    The daylight saving time ending day-of-week.
459      *                        See the class description for the special cases of this parameter.
460      * @param endTime         The daylight saving ending time in local wall clock time,
461      *                        (in milliseconds within the day) which is local daylight
462      *                        time in this case.
463      * @throws    IllegalArgumentException if the {@code endMonth}, {@code endDay},
464      * {@code endDayOfWeek}, or {@code endTime} parameters are out of range
465      */
setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime)466     public void setEndRule(int endMonth, int endDay, int endDayOfWeek,
467                            int endTime)
468     {
469         this.endMonth = endMonth;
470         this.endDay = endDay;
471         this.endDayOfWeek = endDayOfWeek;
472         this.endTime = endTime;
473         this.endTimeMode = WALL_TIME;
474         decodeEndRule();
475         invalidateCache();
476     }
477 
478     /**
479      * Sets the daylight saving time end rule to a fixed date within a month.
480      * This method is equivalent to:
481      * <pre>{@code setEndRule(endMonth, endDay, 0, endTime)}</pre>
482      *
483      * @param endMonth        The daylight saving time ending month. Month is
484      *                        a {@link Calendar#MONTH MONTH} field
485      *                        value (0-based. e.g., 9 for October).
486      * @param endDay          The day of the month on which the daylight saving time ends.
487      * @param endTime         The daylight saving ending time in local wall clock time,
488      *                        (in milliseconds within the day) which is local daylight
489      *                        time in this case.
490      * @throws    IllegalArgumentException the {@code endMonth}, {@code endDay},
491      * or {@code endTime} parameters are out of range
492      * @since 1.2
493      */
setEndRule(int endMonth, int endDay, int endTime)494     public void setEndRule(int endMonth, int endDay, int endTime)
495     {
496         setEndRule(endMonth, endDay, 0, endTime);
497     }
498 
499     /**
500      * Sets the daylight saving time end rule to a weekday before or after the given date within
501      * a month, e.g., the first Monday on or after the 8th.
502      *
503      * @param endMonth        The daylight saving time ending month. Month is
504      *                        a {@link Calendar#MONTH MONTH} field
505      *                        value (0-based. e.g., 9 for October).
506      * @param endDay          The day of the month on which the daylight saving time ends.
507      * @param endDayOfWeek    The daylight saving time ending day-of-week.
508      * @param endTime         The daylight saving ending time in local wall clock time,
509      *                        (in milliseconds within the day) which is local daylight
510      *                        time in this case.
511      * @param after           If true, this rule selects the first {@code endDayOfWeek} on
512      *                        or <em>after</em> {@code endDay}.  If false, this rule
513      *                        selects the last {@code endDayOfWeek} on or before
514      *                        {@code endDay} of the month.
515      * @throws    IllegalArgumentException the {@code endMonth}, {@code endDay},
516      * {@code endDayOfWeek}, or {@code endTime} parameters are out of range
517      * @since 1.2
518      */
setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)519     public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)
520     {
521         if (after) {
522             setEndRule(endMonth, endDay, -endDayOfWeek, endTime);
523         } else {
524             setEndRule(endMonth, -endDay, -endDayOfWeek, endTime);
525         }
526     }
527 
528     /**
529      * Returns the offset of this time zone from UTC at the given
530      * time. If daylight saving time is in effect at the given time,
531      * the offset value is adjusted with the amount of daylight
532      * saving.
533      *
534      * @param date the time at which the time zone offset is found
535      * @return the amount of time in milliseconds to add to UTC to get
536      * local time.
537      * @since 1.4
538      */
getOffset(long date)539     public int getOffset(long date) {
540         return getOffsets(date, null);
541     }
542 
543     /**
544      * @see TimeZone#getOffsets
545      */
getOffsets(long date, int[] offsets)546     int getOffsets(long date, int[] offsets) {
547         int offset = rawOffset;
548 
549       computeOffset:
550         if (useDaylight) {
551             Cache cache = this.cache;
552             if (cache != null) {
553                 if (date >= cache.start && date < cache.end) {
554                     offset += dstSavings;
555                     break computeOffset;
556                 }
557             }
558             BaseCalendar cal = date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
559                 gcal : (BaseCalendar) CalendarSystem.forName("julian");
560             BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
561             // Get the year in local time
562             cal.getCalendarDate(date + rawOffset, cdate);
563             int year = cdate.getNormalizedYear();
564             if (year >= startYear) {
565                 // Clear time elements for the transition calculations
566                 cdate.setTimeOfDay(0, 0, 0, 0);
567                 offset = getOffset(cal, cdate, year, date);
568             }
569         }
570 
571         if (offsets != null) {
572             offsets[0] = rawOffset;
573             offsets[1] = offset - rawOffset;
574         }
575         return offset;
576     }
577 
578     /**
579      * Returns the difference in milliseconds between local time and
580      * UTC, taking into account both the raw offset and the effect of
581      * daylight saving, for the specified date and time.  This method
582      * assumes that the start and end month are distinct.  It also
583      * uses a default {@link GregorianCalendar} object as its
584      * underlying calendar, such as for determining leap years.  Do
585      * not use the result of this method with a calendar other than a
586      * default {@code GregorianCalendar}.
587      *
588      * <p><em>Note:  In general, clients should use
589      * {@code Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)}
590      * instead of calling this method.</em>
591      *
592      * @param era       The era of the given date.
593      * @param year      The year in the given date.
594      * @param month     The month in the given date. Month is 0-based. e.g.,
595      *                  0 for January.
596      * @param day       The day-in-month of the given date.
597      * @param dayOfWeek The day-of-week of the given date.
598      * @param millis    The milliseconds in day in <em>standard</em> local time.
599      * @return          The milliseconds to add to UTC to get local time.
600      * @throws          IllegalArgumentException the {@code era},
601      *                  {@code month}, {@code day}, {@code dayOfWeek},
602      *                  or {@code millis} parameters are out of range
603      */
getOffset(int era, int year, int month, int day, int dayOfWeek, int millis)604     public int getOffset(int era, int year, int month, int day, int dayOfWeek,
605                          int millis)
606     {
607         if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
608             throw new IllegalArgumentException("Illegal era " + era);
609         }
610 
611         int y = year;
612         if (era == GregorianCalendar.BC) {
613             // adjust y with the GregorianCalendar-style year numbering.
614             y = 1 - y;
615         }
616 
617         // If the year isn't representable with the 64-bit long
618         // integer in milliseconds, convert the year to an
619         // equivalent year. This is required to pass some JCK test cases
620         // which are actually useless though because the specified years
621         // can't be supported by the Java time system.
622         if (y >= 292278994) {
623             y = 2800 + y % 2800;
624         } else if (y <= -292269054) {
625             // y %= 28 also produces an equivalent year, but positive
626             // year numbers would be convenient to use the UNIX cal
627             // command.
628             y = (int) CalendarUtils.mod((long) y, 28);
629         }
630 
631         // convert year to its 1-based month value
632         int m = month + 1;
633 
634         // First, calculate time as a Gregorian date.
635         BaseCalendar cal = gcal;
636         BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
637         cdate.setDate(y, m, day);
638         long time = cal.getTime(cdate); // normalize cdate
639         time += millis - rawOffset; // UTC time
640 
641         // If the time value represents a time before the default
642         // Gregorian cutover, recalculate time using the Julian
643         // calendar system. For the Julian calendar system, the
644         // normalized year numbering is ..., -2 (BCE 2), -1 (BCE 1),
645         // 1, 2 ... which is different from the GregorianCalendar
646         // style year numbering (..., -1, 0 (BCE 1), 1, 2, ...).
647         if (time < GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER) {
648             cal = (BaseCalendar) CalendarSystem.forName("julian");
649             cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
650             cdate.setNormalizedDate(y, m, day);
651             time = cal.getTime(cdate) + millis - rawOffset;
652         }
653 
654         if ((cdate.getNormalizedYear() != y)
655             || (cdate.getMonth() != m)
656             || (cdate.getDayOfMonth() != day)
657             // The validation should be cdate.getDayOfWeek() ==
658             // dayOfWeek. However, we don't check dayOfWeek for
659             // compatibility.
660             || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
661             || (millis < 0 || millis >= (24*60*60*1000))) {
662             throw new IllegalArgumentException();
663         }
664 
665         if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
666             return rawOffset;
667         }
668 
669         return getOffset(cal, cdate, y, time);
670     }
671 
getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time)672     private int getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time) {
673         Cache cache = this.cache;
674         if (cache != null) {
675             if (time >= cache.start && time < cache.end) {
676                 return rawOffset + dstSavings;
677             }
678             if (year == cache.year) {
679                 return rawOffset;
680             }
681         }
682 
683         long start = getStart(cal, cdate, year);
684         long end = getEnd(cal, cdate, year);
685         int offset = rawOffset;
686         if (start <= end) {
687             if (time >= start && time < end) {
688                 offset += dstSavings;
689             }
690             this.cache = new Cache(year, start, end);
691         } else {
692             if (time < end) {
693                 // TODO: support Gregorian cutover. The previous year
694                 // may be in the other calendar system.
695                 start = getStart(cal, cdate, year - 1);
696                 if (time >= start) {
697                     offset += dstSavings;
698                 }
699             } else if (time >= start) {
700                 // TODO: support Gregorian cutover. The next year
701                 // may be in the other calendar system.
702                 end = getEnd(cal, cdate, year + 1);
703                 if (time < end) {
704                     offset += dstSavings;
705                 }
706             }
707             if (start <= end) {
708                 this.cache = new Cache((long) startYear - 1, start, end);
709             }
710         }
711         return offset;
712     }
713 
getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year)714     private long getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
715         int time = startTime;
716         if (startTimeMode != UTC_TIME) {
717             time -= rawOffset;
718         }
719         return getTransition(cal, cdate, startMode, year, startMonth, startDay,
720                              startDayOfWeek, time);
721     }
722 
getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year)723     private long getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
724         int time = endTime;
725         if (endTimeMode != UTC_TIME) {
726             time -= rawOffset;
727         }
728         if (endTimeMode == WALL_TIME) {
729             time -= dstSavings;
730         }
731         return getTransition(cal, cdate, endMode, year, endMonth, endDay,
732                                         endDayOfWeek, time);
733     }
734 
getTransition(BaseCalendar cal, BaseCalendar.Date cdate, int mode, int year, int month, int dayOfMonth, int dayOfWeek, int timeOfDay)735     private long getTransition(BaseCalendar cal, BaseCalendar.Date cdate,
736                                int mode, int year, int month, int dayOfMonth,
737                                int dayOfWeek, int timeOfDay) {
738         cdate.setNormalizedYear(year);
739         cdate.setMonth(month + 1);
740         switch (mode) {
741             case DOM_MODE -> cdate.setDayOfMonth(dayOfMonth);
742             case DOW_IN_MONTH_MODE -> {
743                 cdate.setDayOfMonth(1);
744                 if (dayOfMonth < 0) {
745                     cdate.setDayOfMonth(cal.getMonthLength(cdate));
746                 }
747                 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(dayOfMonth, dayOfWeek, cdate);
748             }
749             case DOW_GE_DOM_MODE -> {
750                 cdate.setDayOfMonth(dayOfMonth);
751                 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(1, dayOfWeek, cdate);
752             }
753             case DOW_LE_DOM_MODE -> {
754                 cdate.setDayOfMonth(dayOfMonth);
755                 cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(-1, dayOfWeek, cdate);
756             }
757         }
758         return cal.getTime(cdate) + timeOfDay;
759     }
760 
761     /**
762      * Gets the GMT offset for this time zone.
763      * @return the GMT offset value in milliseconds
764      * @see #setRawOffset
765      */
getRawOffset()766     public int getRawOffset()
767     {
768         // The given date will be taken into account while
769         // we have the historical time zone data in place.
770         return rawOffset;
771     }
772 
773     /**
774      * Sets the base time zone offset to GMT.
775      * This is the offset to add to UTC to get local time.
776      * @see #getRawOffset
777      */
setRawOffset(int offsetMillis)778     public void setRawOffset(int offsetMillis)
779     {
780         this.rawOffset = offsetMillis;
781     }
782 
783     /**
784      * Sets the amount of time in milliseconds that the clock is advanced
785      * during daylight saving time.
786      * @param millisSavedDuringDST the number of milliseconds the time is
787      * advanced with respect to standard time when the daylight saving time rules
788      * are in effect. A positive number, typically one hour (3600000).
789      * @see #getDSTSavings
790      * @since 1.2
791      */
setDSTSavings(int millisSavedDuringDST)792     public void setDSTSavings(int millisSavedDuringDST) {
793         if (millisSavedDuringDST <= 0) {
794             throw new IllegalArgumentException("Illegal daylight saving value: "
795                                                + millisSavedDuringDST);
796         }
797         dstSavings = millisSavedDuringDST;
798     }
799 
800     /**
801      * Returns the amount of time in milliseconds that the clock is
802      * advanced during daylight saving time.
803      *
804      * @return the number of milliseconds the time is advanced with
805      * respect to standard time when the daylight saving rules are in
806      * effect, or 0 (zero) if this time zone doesn't observe daylight
807      * saving time.
808      *
809      * @see #setDSTSavings
810      * @since 1.2
811      */
getDSTSavings()812     public int getDSTSavings() {
813         return useDaylight ? dstSavings : 0;
814     }
815 
816     /**
817      * Queries if this time zone uses daylight saving time.
818      * @return true if this time zone uses daylight saving time;
819      * false otherwise.
820      */
useDaylightTime()821     public boolean useDaylightTime()
822     {
823         return useDaylight;
824     }
825 
826     /**
827      * Returns {@code true} if this {@code SimpleTimeZone} observes
828      * Daylight Saving Time. This method is equivalent to {@link
829      * #useDaylightTime()}.
830      *
831      * @return {@code true} if this {@code SimpleTimeZone} observes
832      * Daylight Saving Time; {@code false} otherwise.
833      * @since 1.7
834      */
835     @Override
observesDaylightTime()836     public boolean observesDaylightTime() {
837         return useDaylightTime();
838     }
839 
840     /**
841      * Queries if the given date is in daylight saving time.
842      * @return true if daylight saving time is in effective at the
843      * given date; false otherwise.
844      */
inDaylightTime(Date date)845     public boolean inDaylightTime(Date date)
846     {
847         return (getOffset(date.getTime()) != rawOffset);
848     }
849 
850     /**
851      * Returns a clone of this {@code SimpleTimeZone} instance.
852      * @return a clone of this instance.
853      */
clone()854     public Object clone()
855     {
856         return super.clone();
857     }
858 
859     /**
860      * Generates the hash code for the SimpleDateFormat object.
861      * @return the hash code for this object
862      */
hashCode()863     public int hashCode()
864     {
865         return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
866             endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
867     }
868 
869     /**
870      * Compares the equality of two {@code SimpleTimeZone} objects.
871      *
872      * @param obj  The {@code SimpleTimeZone} object to be compared with.
873      * @return     True if the given {@code obj} is the same as this
874      *             {@code SimpleTimeZone} object; false otherwise.
875      */
equals(Object obj)876     public boolean equals(Object obj) {
877         if (this == obj) {
878             return true;
879         }
880 
881         return obj instanceof SimpleTimeZone that
882                 && getID().equals(that.getID())
883                 && hasSameRules(that);
884     }
885 
886     /**
887      * Returns {@code true} if this zone has the same rules and offset as another zone.
888      * @param other the TimeZone object to be compared with
889      * @return {@code true} if the given zone is a SimpleTimeZone and has the
890      * same rules and offset as this one
891      * @since 1.2
892      */
hasSameRules(TimeZone other)893     public boolean hasSameRules(TimeZone other) {
894         if (this == other) {
895             return true;
896         }
897         return other instanceof SimpleTimeZone that
898                 && rawOffset == that.rawOffset
899                 && useDaylight == that.useDaylight
900                 && (!useDaylight ||
901                         // Only check rules if using DST
902                         (dstSavings == that.dstSavings
903                         && startMode == that.startMode
904                         && startMonth == that.startMonth
905                         && startDay == that.startDay
906                         && startDayOfWeek == that.startDayOfWeek
907                         && startTime == that.startTime
908                         && startTimeMode == that.startTimeMode
909                         && endMode == that.endMode
910                         && endMonth == that.endMonth
911                         && endDay == that.endDay
912                         && endDayOfWeek == that.endDayOfWeek
913                         && endTime == that.endTime
914                         && endTimeMode == that.endTimeMode
915                         && startYear == that.startYear)
916                     );
917     }
918 
919     /**
920      * Returns a string representation of this time zone.
921      * @return a string representation of this time zone.
922      */
toString()923     public String toString() {
924         return getClass().getName() +
925             "[id=" + getID() +
926             ",offset=" + rawOffset +
927             ",dstSavings=" + dstSavings +
928             ",useDaylight=" + useDaylight +
929             ",startYear=" + startYear +
930             ",startMode=" + startMode +
931             ",startMonth=" + startMonth +
932             ",startDay=" + startDay +
933             ",startDayOfWeek=" + startDayOfWeek +
934             ",startTime=" + startTime +
935             ",startTimeMode=" + startTimeMode +
936             ",endMode=" + endMode +
937             ",endMonth=" + endMonth +
938             ",endDay=" + endDay +
939             ",endDayOfWeek=" + endDayOfWeek +
940             ",endTime=" + endTime +
941             ",endTimeMode=" + endTimeMode + ']';
942     }
943 
944     // =======================privates===============================
945 
946     /**
947      * The month in which daylight saving time starts.  This value must be
948      * between {@code Calendar.JANUARY} and
949      * {@code Calendar.DECEMBER} inclusive.  This value must not equal
950      * {@code endMonth}.
951      * <p>If {@code useDaylight} is false, this value is ignored.
952      * @serial
953      */
954     private int startMonth;
955 
956     /**
957      * This field has two possible interpretations:
958      * <dl>
959      * <dt>{@code startMode == DOW_IN_MONTH}</dt>
960      * <dd>
961      * {@code startDay} indicates the day of the month of
962      * {@code startMonth} on which daylight
963      * saving time starts, from 1 to 28, 30, or 31, depending on the
964      * {@code startMonth}.
965      * </dd>
966      * <dt>{@code startMode != DOW_IN_MONTH}</dt>
967      * <dd>
968      * {@code startDay} indicates which {@code startDayOfWeek} in the
969      * month {@code startMonth} daylight
970      * saving time starts on.  For example, a value of +1 and a
971      * {@code startDayOfWeek} of {@code Calendar.SUNDAY} indicates the
972      * first Sunday of {@code startMonth}.  Likewise, +2 would indicate the
973      * second Sunday, and -1 the last Sunday.  A value of 0 is illegal.
974      * </dd>
975      * </dl>
976      * <p>If {@code useDaylight} is false, this value is ignored.
977      * @serial
978      */
979     private int startDay;
980 
981     /**
982      * The day of the week on which daylight saving time starts.  This value
983      * must be between {@code Calendar.SUNDAY} and
984      * {@code Calendar.SATURDAY} inclusive.
985      * <p>If {@code useDaylight} is false or
986      * {@code startMode == DAY_OF_MONTH}, this value is ignored.
987      * @serial
988      */
989     private int startDayOfWeek;
990 
991     /**
992      * The time in milliseconds after midnight at which daylight saving
993      * time starts.  This value is expressed as wall time, standard time,
994      * or UTC time, depending on the setting of {@code startTimeMode}.
995      * <p>If {@code useDaylight} is false, this value is ignored.
996      * @serial
997      */
998     private int startTime;
999 
1000     /**
1001      * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
1002      * @serial
1003      * @since 1.3
1004      */
1005     private int startTimeMode;
1006 
1007     /**
1008      * The month in which daylight saving time ends.  This value must be
1009      * between {@code Calendar.JANUARY} and
1010      * {@code Calendar.UNDECIMBER}.  This value must not equal
1011      * {@code startMonth}.
1012      * <p>If {@code useDaylight} is false, this value is ignored.
1013      * @serial
1014      */
1015     private int endMonth;
1016 
1017     /**
1018      * This field has two possible interpretations:
1019      * <dl>
1020      * <dt>{@code endMode == DOW_IN_MONTH}</dt>
1021      * <dd>
1022      * {@code endDay} indicates the day of the month of
1023      * {@code endMonth} on which daylight
1024      * saving time ends, from 1 to 28, 30, or 31, depending on the
1025      * {@code endMonth}.
1026      * </dd>
1027      * <dt>{@code endMode != DOW_IN_MONTH}</dt>
1028      * <dd>
1029      * {@code endDay} indicates which {@code endDayOfWeek} in th
1030      * month {@code endMonth} daylight
1031      * saving time ends on.  For example, a value of +1 and a
1032      * {@code endDayOfWeek} of {@code Calendar.SUNDAY} indicates the
1033      * first Sunday of {@code endMonth}.  Likewise, +2 would indicate the
1034      * second Sunday, and -1 the last Sunday.  A value of 0 is illegal.
1035      * </dd>
1036      * </dl>
1037      * <p>If {@code useDaylight} is false, this value is ignored.
1038      * @serial
1039      */
1040     private int endDay;
1041 
1042     /**
1043      * The day of the week on which daylight saving time ends.  This value
1044      * must be between {@code Calendar.SUNDAY} and
1045      * {@code Calendar.SATURDAY} inclusive.
1046      * <p>If {@code useDaylight} is false or
1047      * {@code endMode == DAY_OF_MONTH}, this value is ignored.
1048      * @serial
1049      */
1050     private int endDayOfWeek;
1051 
1052     /**
1053      * The time in milliseconds after midnight at which daylight saving
1054      * time ends.  This value is expressed as wall time, standard time,
1055      * or UTC time, depending on the setting of {@code endTimeMode}.
1056      * <p>If {@code useDaylight} is false, this value is ignored.
1057      * @serial
1058      */
1059     private int endTime;
1060 
1061     /**
1062      * The format of endTime, either {@code WALL_TIME},
1063      * {@code STANDARD_TIME}, or {@code UTC_TIME}.
1064      * @serial
1065      * @since 1.3
1066      */
1067     private int endTimeMode;
1068 
1069     /**
1070      * The year in which daylight saving time is first observed.  This is an {@link GregorianCalendar#AD AD}
1071      * value.  If this value is less than 1 then daylight saving time is observed
1072      * for all {@code AD} years.
1073      * <p>If {@code useDaylight} is false, this value is ignored.
1074      * @serial
1075      */
1076     private int startYear;
1077 
1078     /**
1079      * The offset in milliseconds between this zone and GMT.  Negative offsets
1080      * are to the west of Greenwich.  To obtain local <em>standard</em> time,
1081      * add the offset to GMT time.  To obtain local wall time it may also be
1082      * necessary to add {@code dstSavings}.
1083      * @serial
1084      */
1085     private int rawOffset;
1086 
1087     /**
1088      * A boolean value which is true if and only if this zone uses daylight
1089      * saving time.  If this value is false, several other fields are ignored.
1090      * @serial
1091      */
1092     private boolean useDaylight=false; // indicate if this time zone uses DST
1093 
1094     private static final int millisPerHour = 60*60*1000;
1095     private static final int millisPerDay  = 24*millisPerHour;
1096 
1097     /**
1098      * This field was serialized in JDK 1.1, so we have to keep it that way
1099      * to maintain serialization compatibility. However, there's no need to
1100      * recreate the array each time we create a new time zone.
1101      * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
1102      * 31, 31, 30, 31, 30, 31}.  This is ignored as of the Java 2 platform v1.2, however, it must
1103      * be streamed out for compatibility with JDK 1.1.
1104      */
1105     private final byte monthLength[] = staticMonthLength;
1106     private static final byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
1107     private static final byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
1108 
1109     /**
1110      * Variables specifying the mode of the start rule.  Takes the following
1111      * values:
1112      * <dl>
1113      * <dt>{@code DOM_MODE}</dt>
1114      * <dd>
1115      * Exact day of week; e.g., March 1.
1116      * </dd>
1117      * <dt>{@code DOW_IN_MONTH_MODE}</dt>
1118      * <dd>
1119      * Day of week in month; e.g., last Sunday in March.
1120      * </dd>
1121      * <dt>{@code DOW_GE_DOM_MODE}</dt>
1122      * <dd>
1123      * Day of week after day of month; e.g., Sunday on or after March 15.
1124      * </dd>
1125      * <dt>{@code DOW_LE_DOM_MODE}</dt>
1126      * <dd>
1127      * Day of week before day of month; e.g., Sunday on or before March 15.
1128      * </dd>
1129      * </dl>
1130      * The setting of this field affects the interpretation of the
1131      * {@code startDay} field.
1132      * <p>If {@code useDaylight} is false, this value is ignored.
1133      * @serial
1134      * @since 1.1.4
1135      */
1136     private int startMode;
1137 
1138     /**
1139      * Variables specifying the mode of the end rule.  Takes the following
1140      * values:
1141      * <dl>
1142      * <dt>{@code DOM_MODE}</dt>
1143      * <dd>
1144      * Exact day of week; e.g., March 1.
1145      * </dd>
1146      * <dt>{@code DOW_IN_MONTH_MODE}</dt>
1147      * <dd>
1148      * Day of week in month; e.g., last Sunday in March.
1149      * </dd>
1150      * <dt>{@code DOW_GE_DOM_MODE}</dt>
1151      * <dd>
1152      * Day of week after day of month; e.g., Sunday on or after March 15.
1153      * </dd>
1154      * <dt>{@code DOW_LE_DOM_MODE}</dt>
1155      * <dd>
1156      * Day of week before day of month; e.g., Sunday on or before March 15.
1157      * </dd>
1158      * </dl>
1159      * The setting of this field affects the interpretation of the
1160      * {@code endDay} field.
1161      * <p>If {@code useDaylight} is false, this value is ignored.
1162      * @serial
1163      * @since 1.1.4
1164      */
1165     private int endMode;
1166 
1167     /**
1168      * A positive value indicating the amount of time saved during DST in
1169      * milliseconds.
1170      * Typically one hour (3600000); sometimes 30 minutes (1800000).
1171      * <p>If {@code useDaylight} is false, this value is ignored.
1172      * @serial
1173      * @since 1.1.4
1174      */
1175     private int dstSavings;
1176 
1177     private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
1178 
1179     /**
1180      * Cache values representing a single period of daylight saving
1181      * time. Cache.start is the start time (inclusive) of daylight
1182      * saving time and Cache.end is the end time (exclusive).
1183      *
1184      * Cache.year has a year value if both Cache.start and Cache.end are
1185      * in the same year. Cache.year is set to startYear - 1 if
1186      * Cache.start and Cache.end are in different years.
1187      * Cache.year is a long to support Integer.MIN_VALUE - 1 (JCK requirement).
1188      */
1189     private static final class Cache {
1190         final long year;
1191         final long start;
1192         final long end;
1193 
Cache(long year, long start, long end)1194         Cache(long year, long start, long end) {
1195             this.year = year;
1196             this.start = start;
1197             this.end = end;
1198         }
1199     }
1200 
1201     private transient volatile Cache cache;
1202 
1203     /**
1204      * Constants specifying values of startMode and endMode.
1205      */
1206     private static final int DOM_MODE          = 1; // Exact day of month, "Mar 1"
1207     private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
1208     private static final int DOW_GE_DOM_MODE   = 3; // Day of week after day of month, "Sun>=15"
1209     private static final int DOW_LE_DOM_MODE   = 4; // Day of week before day of month, "Sun<=21"
1210 
1211     /**
1212      * Constant for a mode of start or end time specified as wall clock
1213      * time.  Wall clock time is standard time for the onset rule, and
1214      * daylight time for the end rule.
1215      * @since 1.4
1216      */
1217     public static final int WALL_TIME = 0; // Zero for backward compatibility
1218 
1219     /**
1220      * Constant for a mode of start or end time specified as standard time.
1221      * @since 1.4
1222      */
1223     public static final int STANDARD_TIME = 1;
1224 
1225     /**
1226      * Constant for a mode of start or end time specified as UTC. European
1227      * Union rules are specified as UTC time, for example.
1228      * @since 1.4
1229      */
1230     public static final int UTC_TIME = 2;
1231 
1232     // Proclaim compatibility with 1.1
1233     @java.io.Serial
1234     static final long serialVersionUID = -403250971215465050L;
1235 
1236     // the internal serial version which says which version was written
1237     // - 0 (default) for version up to JDK 1.1.3
1238     // - 1 for version from JDK 1.1.4, which includes 3 new fields
1239     // - 2 for JDK 1.3, which includes 2 new fields
1240     static final int currentSerialVersion = 2;
1241 
1242     /**
1243      * The version of the serialized data on the stream.  Possible values:
1244      * <dl>
1245      * <dt><b>0</b> or not present on stream</dt>
1246      * <dd>
1247      * JDK 1.1.3 or earlier.
1248      * </dd>
1249      * <dt><b>1</b></dt>
1250      * <dd>
1251      * JDK 1.1.4 or later.  Includes three new fields: {@code startMode},
1252      * {@code endMode}, and {@code dstSavings}.
1253      * </dd>
1254      * <dt><b>2</b></dt>
1255      * <dd>
1256      * JDK 1.3 or later.  Includes two new fields: {@code startTimeMode}
1257      * and {@code endTimeMode}.
1258      * </dd>
1259      * </dl>
1260      * When streaming out this class, the most recent format
1261      * and the highest allowable {@code serialVersionOnStream}
1262      * is written.
1263      * @serial
1264      * @since 1.1.4
1265      */
1266     private int serialVersionOnStream = currentSerialVersion;
1267 
1268     // Maximum number of rules.
1269     private static final int MAX_RULE_NUM = 6;
1270 
invalidateCache()1271     private void invalidateCache() {
1272         cache = null;
1273     }
1274 
1275     //----------------------------------------------------------------------
1276     // Rule representation
1277     //
1278     // We represent the following flavors of rules:
1279     //       5        the fifth of the month
1280     //       lastSun  the last Sunday in the month
1281     //       lastMon  the last Monday in the month
1282     //       Sun>=8   first Sunday on or after the eighth
1283     //       Sun<=25  last Sunday on or before the 25th
1284     // This is further complicated by the fact that we need to remain
1285     // backward compatible with the 1.1 FCS.  Finally, we need to minimize
1286     // API changes.  In order to satisfy these requirements, we support
1287     // three representation systems, and we translate between them.
1288     //
1289     // INTERNAL REPRESENTATION
1290     // This is the format SimpleTimeZone objects take after construction or
1291     // streaming in is complete.  Rules are represented directly, using an
1292     // unencoded format.  We will discuss the start rule only below; the end
1293     // rule is analogous.
1294     //   startMode      Takes on enumerated values DAY_OF_MONTH,
1295     //                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
1296     //   startDay       The day of the month, or for DOW_IN_MONTH mode, a
1297     //                  value indicating which DOW, such as +1 for first,
1298     //                  +2 for second, -1 for last, etc.
1299     //   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
1300     //
1301     // ENCODED REPRESENTATION
1302     // This is the format accepted by the constructor and by setStartRule()
1303     // and setEndRule().  It uses various combinations of positive, negative,
1304     // and zero values to encode the different rules.  This representation
1305     // allows us to specify all the different rule flavors without altering
1306     // the API.
1307     //   MODE              startMonth    startDay    startDayOfWeek
1308     //   DOW_IN_MONTH_MODE >=0           !=0         >0
1309     //   DOM_MODE          >=0           >0          ==0
1310     //   DOW_GE_DOM_MODE   >=0           >0          <0
1311     //   DOW_LE_DOM_MODE   >=0           <0          <0
1312     //   (no DST)          don't care    ==0         don't care
1313     //
1314     // STREAMED REPRESENTATION
1315     // We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
1316     // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
1317     // flag useDaylight.  When we stream an object out, we translate into an
1318     // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
1319     // and used by 1.1 code.  Following that, we write out the full
1320     // representation separately so that contemporary code can recognize and
1321     // parse it.  The full representation is written in a "packed" format,
1322     // consisting of a version number, a length, and an array of bytes.  Future
1323     // versions of this class may specify different versions.  If they wish to
1324     // include additional data, they should do so by storing them after the
1325     // packed representation below.
1326     //----------------------------------------------------------------------
1327 
1328     /**
1329      * Given a set of encoded rules in startDay and startDayOfMonth, decode
1330      * them and set the startMode appropriately.  Do the same for endDay and
1331      * endDayOfMonth.  Upon entry, the day of week variables may be zero or
1332      * negative, in order to indicate special modes.  The day of month
1333      * variables may also be negative.  Upon exit, the mode variables will be
1334      * set, and the day of week and day of month variables will be positive.
1335      * This method also recognizes a startDay or endDay of zero as indicating
1336      * no DST.
1337      */
decodeRules()1338     private void decodeRules()
1339     {
1340         decodeStartRule();
1341         decodeEndRule();
1342     }
1343 
1344     /**
1345      * Decode the start rule and validate the parameters.  The parameters are
1346      * expected to be in encoded form, which represents the various rule modes
1347      * by negating or zeroing certain values.  Representation formats are:
1348      * <p>
1349      * <pre>
1350      *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
1351      *            ------------  -----  --------  --------  ----------
1352      * month       0..11        same    same      same     don't care
1353      * day        -5..5         1..31   1..31    -1..-31   0
1354      * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
1355      * time        0..ONEDAY    same    same      same     don't care
1356      * </pre>
1357      * The range for month does not include UNDECIMBER since this class is
1358      * really specific to GregorianCalendar, which does not use that month.
1359      * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
1360      * end rule is an exclusive limit point.  That is, the range of times that
1361      * are in DST include those >= the start and < the end.  For this reason,
1362      * it should be possible to specify an end of ONEDAY in order to include the
1363      * entire day.  Although this is equivalent to time 0 of the following day,
1364      * it's not always possible to specify that, for example, on December 31.
1365      * While arguably the start range should still be 0..ONEDAY-1, we keep
1366      * the start and end ranges the same for consistency.
1367      */
decodeStartRule()1368     private void decodeStartRule() {
1369         useDaylight = (startDay != 0) && (endDay != 0);
1370         if (startDay != 0) {
1371             if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
1372                 throw new IllegalArgumentException(
1373                         "Illegal start month " + startMonth);
1374             }
1375             if (startTime < 0 || startTime > millisPerDay) {
1376                 throw new IllegalArgumentException(
1377                         "Illegal start time " + startTime);
1378             }
1379             if (startDayOfWeek == 0) {
1380                 startMode = DOM_MODE;
1381             } else {
1382                 if (startDayOfWeek > 0) {
1383                     startMode = DOW_IN_MONTH_MODE;
1384                 } else {
1385                     startDayOfWeek = -startDayOfWeek;
1386                     if (startDay > 0) {
1387                         startMode = DOW_GE_DOM_MODE;
1388                     } else {
1389                         startDay = -startDay;
1390                         startMode = DOW_LE_DOM_MODE;
1391                     }
1392                 }
1393                 if (startDayOfWeek > Calendar.SATURDAY) {
1394                     throw new IllegalArgumentException(
1395                            "Illegal start day of week " + startDayOfWeek);
1396                 }
1397             }
1398             if (startMode == DOW_IN_MONTH_MODE) {
1399                 if (startDay < -5 || startDay > 5) {
1400                     throw new IllegalArgumentException(
1401                             "Illegal start day of week in month " + startDay);
1402                 }
1403             } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
1404                 throw new IllegalArgumentException(
1405                         "Illegal start day " + startDay);
1406             }
1407         }
1408     }
1409 
1410     /**
1411      * Decode the end rule and validate the parameters.  This method is exactly
1412      * analogous to decodeStartRule().
1413      * @see decodeStartRule
1414      */
decodeEndRule()1415     private void decodeEndRule() {
1416         useDaylight = (startDay != 0) && (endDay != 0);
1417         if (endDay != 0) {
1418             if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
1419                 throw new IllegalArgumentException(
1420                         "Illegal end month " + endMonth);
1421             }
1422             if (endTime < 0 || endTime > millisPerDay) {
1423                 throw new IllegalArgumentException(
1424                         "Illegal end time " + endTime);
1425             }
1426             if (endDayOfWeek == 0) {
1427                 endMode = DOM_MODE;
1428             } else {
1429                 if (endDayOfWeek > 0) {
1430                     endMode = DOW_IN_MONTH_MODE;
1431                 } else {
1432                     endDayOfWeek = -endDayOfWeek;
1433                     if (endDay > 0) {
1434                         endMode = DOW_GE_DOM_MODE;
1435                     } else {
1436                         endDay = -endDay;
1437                         endMode = DOW_LE_DOM_MODE;
1438                     }
1439                 }
1440                 if (endDayOfWeek > Calendar.SATURDAY) {
1441                     throw new IllegalArgumentException(
1442                            "Illegal end day of week " + endDayOfWeek);
1443                 }
1444             }
1445             if (endMode == DOW_IN_MONTH_MODE) {
1446                 if (endDay < -5 || endDay > 5) {
1447                     throw new IllegalArgumentException(
1448                             "Illegal end day of week in month " + endDay);
1449                 }
1450             } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) {
1451                 throw new IllegalArgumentException(
1452                         "Illegal end day " + endDay);
1453             }
1454         }
1455     }
1456 
1457     /**
1458      * Make rules compatible to 1.1 FCS code.  Since 1.1 FCS code only understands
1459      * day-of-week-in-month rules, we must modify other modes of rules to their
1460      * approximate equivalent in 1.1 FCS terms.  This method is used when streaming
1461      * out objects of this class.  After it is called, the rules will be modified,
1462      * with a possible loss of information.  startMode and endMode will NOT be
1463      * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
1464      * since the rule modification is only intended to be temporary.
1465      */
makeRulesCompatible()1466     private void makeRulesCompatible()
1467     {
1468         switch (startMode) {
1469         case DOM_MODE:
1470             startDay = 1 + (startDay / 7);
1471             startDayOfWeek = Calendar.SUNDAY;
1472             break;
1473 
1474         case DOW_GE_DOM_MODE:
1475             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1476             // that is, Sun>=1 == firstSun.
1477             if (startDay != 1) {
1478                 startDay = 1 + (startDay / 7);
1479             }
1480             break;
1481 
1482         case DOW_LE_DOM_MODE:
1483             if (startDay >= 30) {
1484                 startDay = -1;
1485             } else {
1486                 startDay = 1 + (startDay / 7);
1487             }
1488             break;
1489         }
1490 
1491         switch (endMode) {
1492         case DOM_MODE:
1493             endDay = 1 + (endDay / 7);
1494             endDayOfWeek = Calendar.SUNDAY;
1495             break;
1496 
1497         case DOW_GE_DOM_MODE:
1498             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1499             // that is, Sun>=1 == firstSun.
1500             if (endDay != 1) {
1501                 endDay = 1 + (endDay / 7);
1502             }
1503             break;
1504 
1505         case DOW_LE_DOM_MODE:
1506             if (endDay >= 30) {
1507                 endDay = -1;
1508             } else {
1509                 endDay = 1 + (endDay / 7);
1510             }
1511             break;
1512         }
1513 
1514         /*
1515          * Adjust the start and end times to wall time.  This works perfectly
1516          * well unless it pushes into the next or previous day.  If that
1517          * happens, we attempt to adjust the day rule somewhat crudely.  The day
1518          * rules have been forced into DOW_IN_MONTH mode already, so we change
1519          * the day of week to move forward or back by a day.  It's possible to
1520          * make a more refined adjustment of the original rules first, but in
1521          * most cases this extra effort will go to waste once we adjust the day
1522          * rules anyway.
1523          */
1524         switch (startTimeMode) {
1525             case UTC_TIME -> startTime += rawOffset;
1526         }
1527         while (startTime < 0) {
1528             startTime += millisPerDay;
1529             startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day
1530         }
1531         while (startTime >= millisPerDay) {
1532             startTime -= millisPerDay;
1533             startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day
1534         }
1535 
1536         switch (endTimeMode) {
1537             case UTC_TIME -> endTime += rawOffset + dstSavings;
1538             case STANDARD_TIME -> endTime += dstSavings;
1539         }
1540         while (endTime < 0) {
1541             endTime += millisPerDay;
1542             endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day
1543         }
1544         while (endTime >= millisPerDay) {
1545             endTime -= millisPerDay;
1546             endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day
1547         }
1548     }
1549 
1550     /**
1551      * Pack the start and end rules into an array of bytes.  Only pack
1552      * data which is not preserved by makeRulesCompatible.
1553      */
packRules()1554     private byte[] packRules()
1555     {
1556         byte[] rules = new byte[MAX_RULE_NUM];
1557         rules[0] = (byte)startDay;
1558         rules[1] = (byte)startDayOfWeek;
1559         rules[2] = (byte)endDay;
1560         rules[3] = (byte)endDayOfWeek;
1561 
1562         // As of serial version 2, include time modes
1563         rules[4] = (byte)startTimeMode;
1564         rules[5] = (byte)endTimeMode;
1565 
1566         return rules;
1567     }
1568 
1569     /**
1570      * Given an array of bytes produced by packRules, interpret them
1571      * as the start and end rules.
1572      */
unpackRules(byte[] rules)1573     private void unpackRules(byte[] rules)
1574     {
1575         startDay       = rules[0];
1576         startDayOfWeek = rules[1];
1577         endDay         = rules[2];
1578         endDayOfWeek   = rules[3];
1579 
1580         // As of serial version 2, include time modes
1581         if (rules.length >= MAX_RULE_NUM) {
1582             startTimeMode = rules[4];
1583             endTimeMode   = rules[5];
1584         }
1585     }
1586 
1587     /**
1588      * Pack the start and end times into an array of bytes.  This is required
1589      * as of serial version 2.
1590      */
packTimes()1591     private int[] packTimes() {
1592         int[] times = new int[2];
1593         times[0] = startTime;
1594         times[1] = endTime;
1595         return times;
1596     }
1597 
1598     /**
1599      * Unpack the start and end times from an array of bytes.  This is required
1600      * as of serial version 2.
1601      */
unpackTimes(int[] times)1602     private void unpackTimes(int[] times) {
1603         startTime = times[0];
1604         endTime = times[1];
1605     }
1606 
1607     /**
1608      * Save the state of this object to a stream (i.e., serialize it).
1609      *
1610      * @serialData We write out two formats, a JDK 1.1 compatible format, using
1611      * {@code DOW_IN_MONTH_MODE} rules, in the required section, followed
1612      * by the full rules, in packed format, in the optional section.  The
1613      * optional section will be ignored by JDK 1.1 code upon stream in.
1614      * <p> Contents of the optional section: The length of a byte array is
1615      * emitted (int); this is 4 as of this release. The byte array of the given
1616      * length is emitted. The contents of the byte array are the true values of
1617      * the fields {@code startDay}, {@code startDayOfWeek},
1618      * {@code endDay}, and {@code endDayOfWeek}.  The values of these
1619      * fields in the required section are approximate values suited to the rule
1620      * mode {@code DOW_IN_MONTH_MODE}, which is the only mode recognized by
1621      * JDK 1.1.
1622      */
1623     @java.io.Serial
writeObject(ObjectOutputStream stream)1624     private void writeObject(ObjectOutputStream stream)
1625          throws IOException
1626     {
1627         // Construct a binary rule
1628         byte[] rules = packRules();
1629         int[] times = packTimes();
1630 
1631         // Convert to 1.1 FCS rules.  This step may cause us to lose information.
1632         makeRulesCompatible();
1633 
1634         // Write out the 1.1 FCS rules
1635         stream.defaultWriteObject();
1636 
1637         // Write out the binary rules in the optional data area of the stream.
1638         stream.writeInt(rules.length);
1639         stream.write(rules);
1640         stream.writeObject(times);
1641 
1642         // Recover the original rules.  This recovers the information lost
1643         // by makeRulesCompatible.
1644         unpackRules(rules);
1645         unpackTimes(times);
1646     }
1647 
1648     /**
1649      * Reconstitute this object from a stream (i.e., deserialize it).
1650      *
1651      * We handle both JDK 1.1
1652      * binary formats and full formats with a packed byte array.
1653      */
1654     @java.io.Serial
readObject(ObjectInputStream stream)1655     private void readObject(ObjectInputStream stream)
1656          throws IOException, ClassNotFoundException
1657     {
1658         stream.defaultReadObject();
1659 
1660         if (serialVersionOnStream < 1) {
1661             // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
1662             // startDayOfWeek and endDayOfWeek were usually uninitialized.  We can't do
1663             // too much, so we assume SUNDAY, which actually works most of the time.
1664             if (startDayOfWeek == 0) {
1665                 startDayOfWeek = Calendar.SUNDAY;
1666             }
1667             if (endDayOfWeek == 0) {
1668                 endDayOfWeek = Calendar.SUNDAY;
1669             }
1670 
1671             // The variables dstSavings, startMode, and endMode are post-1.1, so they
1672             // won't be present if we're reading from a 1.1 stream.  Fix them up.
1673             startMode = endMode = DOW_IN_MONTH_MODE;
1674             dstSavings = millisPerHour;
1675         } else {
1676             // For 1.1.4, in addition to the 3 new instance variables, we also
1677             // store the actual rules (which have not be made compatible with 1.1)
1678             // in the optional area.  Read them in here and parse them.
1679             int length = stream.readInt();
1680             if (length <= MAX_RULE_NUM) {
1681                 byte[] rules = new byte[length];
1682                 stream.readFully(rules);
1683                 unpackRules(rules);
1684             } else {
1685                 throw new InvalidObjectException("Too many rules: " + length);
1686             }
1687         }
1688 
1689         if (serialVersionOnStream >= 2) {
1690             int[] times = (int[]) stream.readObject();
1691             unpackTimes(times);
1692         }
1693 
1694         serialVersionOnStream = currentSerialVersion;
1695     }
1696 }
1697