• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1996, 2023, 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      * @implSpec The default implementation throws a
843      * {@code NullPointerException} if {@code date} is {@code null}
844      * @return true if daylight saving time is in effective at the
845      * given date; false otherwise.
846      * @throws NullPointerException This method may throw a
847      * {@code NullPointerException} if {@code date} is {@code null}
848      */
inDaylightTime(Date date)849     public boolean inDaylightTime(Date date)
850     {
851         return (getOffset(date.getTime()) != rawOffset);
852     }
853 
854     /**
855      * Returns a clone of this {@code SimpleTimeZone} instance.
856      * @return a clone of this instance.
857      */
clone()858     public Object clone()
859     {
860         return super.clone();
861     }
862 
863     /**
864      * Generates the hash code for the SimpleDateFormat object.
865      * @return the hash code for this object
866      */
hashCode()867     public int hashCode()
868     {
869         return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
870             endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
871     }
872 
873     /**
874      * Compares the equality of two {@code SimpleTimeZone} objects.
875      *
876      * @param obj  The {@code SimpleTimeZone} object to be compared with.
877      * @return     True if the given {@code obj} is the same as this
878      *             {@code SimpleTimeZone} object; false otherwise.
879      */
equals(Object obj)880     public boolean equals(Object obj) {
881         if (this == obj) {
882             return true;
883         }
884 
885         return obj instanceof SimpleTimeZone that
886                 && getID().equals(that.getID())
887                 && hasSameRules(that);
888     }
889 
890     /**
891      * Returns {@code true} if this zone has the same rules and offset as another zone.
892      * @param other the TimeZone object to be compared with
893      * @return {@code true} if the given zone is a SimpleTimeZone and has the
894      * same rules and offset as this one
895      * @since 1.2
896      */
hasSameRules(TimeZone other)897     public boolean hasSameRules(TimeZone other) {
898         if (this == other) {
899             return true;
900         }
901         return other instanceof SimpleTimeZone that
902                 && rawOffset == that.rawOffset
903                 && useDaylight == that.useDaylight
904                 && (!useDaylight ||
905                         // Only check rules if using DST
906                         (dstSavings == that.dstSavings
907                         && startMode == that.startMode
908                         && startMonth == that.startMonth
909                         && startDay == that.startDay
910                         && startDayOfWeek == that.startDayOfWeek
911                         && startTime == that.startTime
912                         && startTimeMode == that.startTimeMode
913                         && endMode == that.endMode
914                         && endMonth == that.endMonth
915                         && endDay == that.endDay
916                         && endDayOfWeek == that.endDayOfWeek
917                         && endTime == that.endTime
918                         && endTimeMode == that.endTimeMode
919                         && startYear == that.startYear)
920                     );
921     }
922 
923     /**
924      * Returns a string representation of this time zone.
925      * @return a string representation of this time zone.
926      */
toString()927     public String toString() {
928         return getClass().getName() +
929             "[id=" + getID() +
930             ",offset=" + rawOffset +
931             ",dstSavings=" + dstSavings +
932             ",useDaylight=" + useDaylight +
933             ",startYear=" + startYear +
934             ",startMode=" + startMode +
935             ",startMonth=" + startMonth +
936             ",startDay=" + startDay +
937             ",startDayOfWeek=" + startDayOfWeek +
938             ",startTime=" + startTime +
939             ",startTimeMode=" + startTimeMode +
940             ",endMode=" + endMode +
941             ",endMonth=" + endMonth +
942             ",endDay=" + endDay +
943             ",endDayOfWeek=" + endDayOfWeek +
944             ",endTime=" + endTime +
945             ",endTimeMode=" + endTimeMode + ']';
946     }
947 
948     // =======================privates===============================
949 
950     /**
951      * The month in which daylight saving time starts.  This value must be
952      * between {@code Calendar.JANUARY} and
953      * {@code Calendar.DECEMBER} inclusive.  This value must not equal
954      * {@code endMonth}.
955      * <p>If {@code useDaylight} is false, this value is ignored.
956      * @serial
957      */
958     private int startMonth;
959 
960     /**
961      * This field has two possible interpretations:
962      * <dl>
963      * <dt>{@code startMode == DOW_IN_MONTH}</dt>
964      * <dd>
965      * {@code startDay} indicates the day of the month of
966      * {@code startMonth} on which daylight
967      * saving time starts, from 1 to 28, 30, or 31, depending on the
968      * {@code startMonth}.
969      * </dd>
970      * <dt>{@code startMode != DOW_IN_MONTH}</dt>
971      * <dd>
972      * {@code startDay} indicates which {@code startDayOfWeek} in the
973      * month {@code startMonth} daylight
974      * saving time starts on.  For example, a value of +1 and a
975      * {@code startDayOfWeek} of {@code Calendar.SUNDAY} indicates the
976      * first Sunday of {@code startMonth}.  Likewise, +2 would indicate the
977      * second Sunday, and -1 the last Sunday.  A value of 0 is illegal.
978      * </dd>
979      * </dl>
980      * <p>If {@code useDaylight} is false, this value is ignored.
981      * @serial
982      */
983     private int startDay;
984 
985     /**
986      * The day of the week on which daylight saving time starts.  This value
987      * must be between {@code Calendar.SUNDAY} and
988      * {@code Calendar.SATURDAY} inclusive.
989      * <p>If {@code useDaylight} is false or
990      * {@code startMode == DAY_OF_MONTH}, this value is ignored.
991      * @serial
992      */
993     private int startDayOfWeek;
994 
995     /**
996      * The time in milliseconds after midnight at which daylight saving
997      * time starts.  This value is expressed as wall time, standard time,
998      * or UTC time, depending on the setting of {@code startTimeMode}.
999      * <p>If {@code useDaylight} is false, this value is ignored.
1000      * @serial
1001      */
1002     private int startTime;
1003 
1004     /**
1005      * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
1006      * @serial
1007      * @since 1.3
1008      */
1009     private int startTimeMode;
1010 
1011     /**
1012      * The month in which daylight saving time ends.  This value must be
1013      * between {@code Calendar.JANUARY} and
1014      * {@code Calendar.UNDECIMBER}.  This value must not equal
1015      * {@code startMonth}.
1016      * <p>If {@code useDaylight} is false, this value is ignored.
1017      * @serial
1018      */
1019     private int endMonth;
1020 
1021     /**
1022      * This field has two possible interpretations:
1023      * <dl>
1024      * <dt>{@code endMode == DOW_IN_MONTH}</dt>
1025      * <dd>
1026      * {@code endDay} indicates the day of the month of
1027      * {@code endMonth} on which daylight
1028      * saving time ends, from 1 to 28, 30, or 31, depending on the
1029      * {@code endMonth}.
1030      * </dd>
1031      * <dt>{@code endMode != DOW_IN_MONTH}</dt>
1032      * <dd>
1033      * {@code endDay} indicates which {@code endDayOfWeek} in th
1034      * month {@code endMonth} daylight
1035      * saving time ends on.  For example, a value of +1 and a
1036      * {@code endDayOfWeek} of {@code Calendar.SUNDAY} indicates the
1037      * first Sunday of {@code endMonth}.  Likewise, +2 would indicate the
1038      * second Sunday, and -1 the last Sunday.  A value of 0 is illegal.
1039      * </dd>
1040      * </dl>
1041      * <p>If {@code useDaylight} is false, this value is ignored.
1042      * @serial
1043      */
1044     private int endDay;
1045 
1046     /**
1047      * The day of the week on which daylight saving time ends.  This value
1048      * must be between {@code Calendar.SUNDAY} and
1049      * {@code Calendar.SATURDAY} inclusive.
1050      * <p>If {@code useDaylight} is false or
1051      * {@code endMode == DAY_OF_MONTH}, this value is ignored.
1052      * @serial
1053      */
1054     private int endDayOfWeek;
1055 
1056     /**
1057      * The time in milliseconds after midnight at which daylight saving
1058      * time ends.  This value is expressed as wall time, standard time,
1059      * or UTC time, depending on the setting of {@code endTimeMode}.
1060      * <p>If {@code useDaylight} is false, this value is ignored.
1061      * @serial
1062      */
1063     private int endTime;
1064 
1065     /**
1066      * The format of endTime, either {@code WALL_TIME},
1067      * {@code STANDARD_TIME}, or {@code UTC_TIME}.
1068      * @serial
1069      * @since 1.3
1070      */
1071     private int endTimeMode;
1072 
1073     /**
1074      * The year in which daylight saving time is first observed.  This is an {@link GregorianCalendar#AD AD}
1075      * value.  If this value is less than 1 then daylight saving time is observed
1076      * for all {@code AD} years.
1077      * <p>If {@code useDaylight} is false, this value is ignored.
1078      * @serial
1079      */
1080     private int startYear;
1081 
1082     /**
1083      * The offset in milliseconds between this zone and GMT.  Negative offsets
1084      * are to the west of Greenwich.  To obtain local <em>standard</em> time,
1085      * add the offset to GMT time.  To obtain local wall time it may also be
1086      * necessary to add {@code dstSavings}.
1087      * @serial
1088      */
1089     private int rawOffset;
1090 
1091     /**
1092      * A boolean value which is true if and only if this zone uses daylight
1093      * saving time.  If this value is false, several other fields are ignored.
1094      * @serial
1095      */
1096     private boolean useDaylight=false; // indicate if this time zone uses DST
1097 
1098     private static final int millisPerHour = 60*60*1000;
1099     private static final int millisPerDay  = 24*millisPerHour;
1100 
1101     /**
1102      * This field was serialized in JDK 1.1, so we have to keep it that way
1103      * to maintain serialization compatibility. However, there's no need to
1104      * recreate the array each time we create a new time zone.
1105      * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
1106      * 31, 31, 30, 31, 30, 31}.  This is ignored as of the Java 2 platform v1.2, however, it must
1107      * be streamed out for compatibility with JDK 1.1.
1108      */
1109     private final byte monthLength[] = staticMonthLength;
1110     private static final byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
1111     private static final byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
1112 
1113     /**
1114      * Variables specifying the mode of the start rule.  Takes the following
1115      * values:
1116      * <dl>
1117      * <dt>{@code DOM_MODE}</dt>
1118      * <dd>
1119      * Exact day of week; e.g., March 1.
1120      * </dd>
1121      * <dt>{@code DOW_IN_MONTH_MODE}</dt>
1122      * <dd>
1123      * Day of week in month; e.g., last Sunday in March.
1124      * </dd>
1125      * <dt>{@code DOW_GE_DOM_MODE}</dt>
1126      * <dd>
1127      * Day of week after day of month; e.g., Sunday on or after March 15.
1128      * </dd>
1129      * <dt>{@code DOW_LE_DOM_MODE}</dt>
1130      * <dd>
1131      * Day of week before day of month; e.g., Sunday on or before March 15.
1132      * </dd>
1133      * </dl>
1134      * The setting of this field affects the interpretation of the
1135      * {@code startDay} field.
1136      * <p>If {@code useDaylight} is false, this value is ignored.
1137      * @serial
1138      * @since 1.1.4
1139      */
1140     private int startMode;
1141 
1142     /**
1143      * Variables specifying the mode of the end rule.  Takes the following
1144      * values:
1145      * <dl>
1146      * <dt>{@code DOM_MODE}</dt>
1147      * <dd>
1148      * Exact day of week; e.g., March 1.
1149      * </dd>
1150      * <dt>{@code DOW_IN_MONTH_MODE}</dt>
1151      * <dd>
1152      * Day of week in month; e.g., last Sunday in March.
1153      * </dd>
1154      * <dt>{@code DOW_GE_DOM_MODE}</dt>
1155      * <dd>
1156      * Day of week after day of month; e.g., Sunday on or after March 15.
1157      * </dd>
1158      * <dt>{@code DOW_LE_DOM_MODE}</dt>
1159      * <dd>
1160      * Day of week before day of month; e.g., Sunday on or before March 15.
1161      * </dd>
1162      * </dl>
1163      * The setting of this field affects the interpretation of the
1164      * {@code endDay} field.
1165      * <p>If {@code useDaylight} is false, this value is ignored.
1166      * @serial
1167      * @since 1.1.4
1168      */
1169     private int endMode;
1170 
1171     /**
1172      * A positive value indicating the amount of time saved during DST in
1173      * milliseconds.
1174      * Typically one hour (3600000); sometimes 30 minutes (1800000).
1175      * <p>If {@code useDaylight} is false, this value is ignored.
1176      * @serial
1177      * @since 1.1.4
1178      */
1179     private int dstSavings;
1180 
1181     private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
1182 
1183     /**
1184      * Cache values representing a single period of daylight saving
1185      * time. Cache.start is the start time (inclusive) of daylight
1186      * saving time and Cache.end is the end time (exclusive).
1187      *
1188      * Cache.year has a year value if both Cache.start and Cache.end are
1189      * in the same year. Cache.year is set to startYear - 1 if
1190      * Cache.start and Cache.end are in different years.
1191      * Cache.year is a long to support Integer.MIN_VALUE - 1 (JCK requirement).
1192      */
1193     private static final class Cache {
1194         final long year;
1195         final long start;
1196         final long end;
1197 
Cache(long year, long start, long end)1198         Cache(long year, long start, long end) {
1199             this.year = year;
1200             this.start = start;
1201             this.end = end;
1202         }
1203     }
1204 
1205     private transient volatile Cache cache;
1206 
1207     /**
1208      * Constants specifying values of startMode and endMode.
1209      */
1210     private static final int DOM_MODE          = 1; // Exact day of month, "Mar 1"
1211     private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
1212     private static final int DOW_GE_DOM_MODE   = 3; // Day of week after day of month, "Sun>=15"
1213     private static final int DOW_LE_DOM_MODE   = 4; // Day of week before day of month, "Sun<=21"
1214 
1215     /**
1216      * Constant for a mode of start or end time specified as wall clock
1217      * time.  Wall clock time is standard time for the onset rule, and
1218      * daylight time for the end rule.
1219      * @since 1.4
1220      */
1221     public static final int WALL_TIME = 0; // Zero for backward compatibility
1222 
1223     /**
1224      * Constant for a mode of start or end time specified as standard time.
1225      * @since 1.4
1226      */
1227     public static final int STANDARD_TIME = 1;
1228 
1229     /**
1230      * Constant for a mode of start or end time specified as UTC. European
1231      * Union rules are specified as UTC time, for example.
1232      * @since 1.4
1233      */
1234     public static final int UTC_TIME = 2;
1235 
1236     // Proclaim compatibility with 1.1
1237     @java.io.Serial
1238     static final long serialVersionUID = -403250971215465050L;
1239 
1240     // the internal serial version which says which version was written
1241     // - 0 (default) for version up to JDK 1.1.3
1242     // - 1 for version from JDK 1.1.4, which includes 3 new fields
1243     // - 2 for JDK 1.3, which includes 2 new fields
1244     static final int currentSerialVersion = 2;
1245 
1246     /**
1247      * The version of the serialized data on the stream.  Possible values:
1248      * <dl>
1249      * <dt><b>0</b> or not present on stream</dt>
1250      * <dd>
1251      * JDK 1.1.3 or earlier.
1252      * </dd>
1253      * <dt><b>1</b></dt>
1254      * <dd>
1255      * JDK 1.1.4 or later.  Includes three new fields: {@code startMode},
1256      * {@code endMode}, and {@code dstSavings}.
1257      * </dd>
1258      * <dt><b>2</b></dt>
1259      * <dd>
1260      * JDK 1.3 or later.  Includes two new fields: {@code startTimeMode}
1261      * and {@code endTimeMode}.
1262      * </dd>
1263      * </dl>
1264      * When streaming out this class, the most recent format
1265      * and the highest allowable {@code serialVersionOnStream}
1266      * is written.
1267      * @serial
1268      * @since 1.1.4
1269      */
1270     private int serialVersionOnStream = currentSerialVersion;
1271 
1272     // Maximum number of rules.
1273     private static final int MAX_RULE_NUM = 6;
1274 
invalidateCache()1275     private void invalidateCache() {
1276         cache = null;
1277     }
1278 
1279     //----------------------------------------------------------------------
1280     // Rule representation
1281     //
1282     // We represent the following flavors of rules:
1283     //       5        the fifth of the month
1284     //       lastSun  the last Sunday in the month
1285     //       lastMon  the last Monday in the month
1286     //       Sun>=8   first Sunday on or after the eighth
1287     //       Sun<=25  last Sunday on or before the 25th
1288     // This is further complicated by the fact that we need to remain
1289     // backward compatible with the 1.1 FCS.  Finally, we need to minimize
1290     // API changes.  In order to satisfy these requirements, we support
1291     // three representation systems, and we translate between them.
1292     //
1293     // INTERNAL REPRESENTATION
1294     // This is the format SimpleTimeZone objects take after construction or
1295     // streaming in is complete.  Rules are represented directly, using an
1296     // unencoded format.  We will discuss the start rule only below; the end
1297     // rule is analogous.
1298     //   startMode      Takes on enumerated values DAY_OF_MONTH,
1299     //                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
1300     //   startDay       The day of the month, or for DOW_IN_MONTH mode, a
1301     //                  value indicating which DOW, such as +1 for first,
1302     //                  +2 for second, -1 for last, etc.
1303     //   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
1304     //
1305     // ENCODED REPRESENTATION
1306     // This is the format accepted by the constructor and by setStartRule()
1307     // and setEndRule().  It uses various combinations of positive, negative,
1308     // and zero values to encode the different rules.  This representation
1309     // allows us to specify all the different rule flavors without altering
1310     // the API.
1311     //   MODE              startMonth    startDay    startDayOfWeek
1312     //   DOW_IN_MONTH_MODE >=0           !=0         >0
1313     //   DOM_MODE          >=0           >0          ==0
1314     //   DOW_GE_DOM_MODE   >=0           >0          <0
1315     //   DOW_LE_DOM_MODE   >=0           <0          <0
1316     //   (no DST)          don't care    ==0         don't care
1317     //
1318     // STREAMED REPRESENTATION
1319     // We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
1320     // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
1321     // flag useDaylight.  When we stream an object out, we translate into an
1322     // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
1323     // and used by 1.1 code.  Following that, we write out the full
1324     // representation separately so that contemporary code can recognize and
1325     // parse it.  The full representation is written in a "packed" format,
1326     // consisting of a version number, a length, and an array of bytes.  Future
1327     // versions of this class may specify different versions.  If they wish to
1328     // include additional data, they should do so by storing them after the
1329     // packed representation below.
1330     //----------------------------------------------------------------------
1331 
1332     /**
1333      * Given a set of encoded rules in startDay and startDayOfMonth, decode
1334      * them and set the startMode appropriately.  Do the same for endDay and
1335      * endDayOfMonth.  Upon entry, the day of week variables may be zero or
1336      * negative, in order to indicate special modes.  The day of month
1337      * variables may also be negative.  Upon exit, the mode variables will be
1338      * set, and the day of week and day of month variables will be positive.
1339      * This method also recognizes a startDay or endDay of zero as indicating
1340      * no DST.
1341      */
decodeRules()1342     private void decodeRules()
1343     {
1344         decodeStartRule();
1345         decodeEndRule();
1346     }
1347 
1348     /**
1349      * Decode the start rule and validate the parameters.  The parameters are
1350      * expected to be in encoded form, which represents the various rule modes
1351      * by negating or zeroing certain values.  Representation formats are:
1352      * <p>
1353      * <pre>
1354      *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
1355      *            ------------  -----  --------  --------  ----------
1356      * month       0..11        same    same      same     don't care
1357      * day        -5..5         1..31   1..31    -1..-31   0
1358      * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
1359      * time        0..ONEDAY    same    same      same     don't care
1360      * </pre>
1361      * The range for month does not include UNDECIMBER since this class is
1362      * really specific to GregorianCalendar, which does not use that month.
1363      * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
1364      * end rule is an exclusive limit point.  That is, the range of times that
1365      * are in DST include those >= the start and < the end.  For this reason,
1366      * it should be possible to specify an end of ONEDAY in order to include the
1367      * entire day.  Although this is equivalent to time 0 of the following day,
1368      * it's not always possible to specify that, for example, on December 31.
1369      * While arguably the start range should still be 0..ONEDAY-1, we keep
1370      * the start and end ranges the same for consistency.
1371      */
decodeStartRule()1372     private void decodeStartRule() {
1373         useDaylight = (startDay != 0) && (endDay != 0);
1374         if (startDay != 0) {
1375             if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
1376                 throw new IllegalArgumentException(
1377                         "Illegal start month " + startMonth);
1378             }
1379             if (startTime < 0 || startTime > millisPerDay) {
1380                 throw new IllegalArgumentException(
1381                         "Illegal start time " + startTime);
1382             }
1383             if (startDayOfWeek == 0) {
1384                 startMode = DOM_MODE;
1385             } else {
1386                 if (startDayOfWeek > 0) {
1387                     startMode = DOW_IN_MONTH_MODE;
1388                 } else {
1389                     startDayOfWeek = -startDayOfWeek;
1390                     if (startDay > 0) {
1391                         startMode = DOW_GE_DOM_MODE;
1392                     } else {
1393                         startDay = -startDay;
1394                         startMode = DOW_LE_DOM_MODE;
1395                     }
1396                 }
1397                 if (startDayOfWeek > Calendar.SATURDAY) {
1398                     throw new IllegalArgumentException(
1399                            "Illegal start day of week " + startDayOfWeek);
1400                 }
1401             }
1402             if (startMode == DOW_IN_MONTH_MODE) {
1403                 if (startDay < -5 || startDay > 5) {
1404                     throw new IllegalArgumentException(
1405                             "Illegal start day of week in month " + startDay);
1406                 }
1407             } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
1408                 throw new IllegalArgumentException(
1409                         "Illegal start day " + startDay);
1410             }
1411         }
1412     }
1413 
1414     /**
1415      * Decode the end rule and validate the parameters.  This method is exactly
1416      * analogous to decodeStartRule().
1417      * @see decodeStartRule
1418      */
decodeEndRule()1419     private void decodeEndRule() {
1420         useDaylight = (startDay != 0) && (endDay != 0);
1421         if (endDay != 0) {
1422             if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
1423                 throw new IllegalArgumentException(
1424                         "Illegal end month " + endMonth);
1425             }
1426             if (endTime < 0 || endTime > millisPerDay) {
1427                 throw new IllegalArgumentException(
1428                         "Illegal end time " + endTime);
1429             }
1430             if (endDayOfWeek == 0) {
1431                 endMode = DOM_MODE;
1432             } else {
1433                 if (endDayOfWeek > 0) {
1434                     endMode = DOW_IN_MONTH_MODE;
1435                 } else {
1436                     endDayOfWeek = -endDayOfWeek;
1437                     if (endDay > 0) {
1438                         endMode = DOW_GE_DOM_MODE;
1439                     } else {
1440                         endDay = -endDay;
1441                         endMode = DOW_LE_DOM_MODE;
1442                     }
1443                 }
1444                 if (endDayOfWeek > Calendar.SATURDAY) {
1445                     throw new IllegalArgumentException(
1446                            "Illegal end day of week " + endDayOfWeek);
1447                 }
1448             }
1449             if (endMode == DOW_IN_MONTH_MODE) {
1450                 if (endDay < -5 || endDay > 5) {
1451                     throw new IllegalArgumentException(
1452                             "Illegal end day of week in month " + endDay);
1453                 }
1454             } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) {
1455                 throw new IllegalArgumentException(
1456                         "Illegal end day " + endDay);
1457             }
1458         }
1459     }
1460 
1461     /**
1462      * Make rules compatible to 1.1 FCS code.  Since 1.1 FCS code only understands
1463      * day-of-week-in-month rules, we must modify other modes of rules to their
1464      * approximate equivalent in 1.1 FCS terms.  This method is used when streaming
1465      * out objects of this class.  After it is called, the rules will be modified,
1466      * with a possible loss of information.  startMode and endMode will NOT be
1467      * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
1468      * since the rule modification is only intended to be temporary.
1469      */
makeRulesCompatible()1470     private void makeRulesCompatible()
1471     {
1472         switch (startMode) {
1473         case DOM_MODE:
1474             startDay = 1 + (startDay / 7);
1475             startDayOfWeek = Calendar.SUNDAY;
1476             break;
1477 
1478         case DOW_GE_DOM_MODE:
1479             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1480             // that is, Sun>=1 == firstSun.
1481             if (startDay != 1) {
1482                 startDay = 1 + (startDay / 7);
1483             }
1484             break;
1485 
1486         case DOW_LE_DOM_MODE:
1487             if (startDay >= 30) {
1488                 startDay = -1;
1489             } else {
1490                 startDay = 1 + (startDay / 7);
1491             }
1492             break;
1493         }
1494 
1495         switch (endMode) {
1496         case DOM_MODE:
1497             endDay = 1 + (endDay / 7);
1498             endDayOfWeek = Calendar.SUNDAY;
1499             break;
1500 
1501         case DOW_GE_DOM_MODE:
1502             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1503             // that is, Sun>=1 == firstSun.
1504             if (endDay != 1) {
1505                 endDay = 1 + (endDay / 7);
1506             }
1507             break;
1508 
1509         case DOW_LE_DOM_MODE:
1510             if (endDay >= 30) {
1511                 endDay = -1;
1512             } else {
1513                 endDay = 1 + (endDay / 7);
1514             }
1515             break;
1516         }
1517 
1518         /*
1519          * Adjust the start and end times to wall time.  This works perfectly
1520          * well unless it pushes into the next or previous day.  If that
1521          * happens, we attempt to adjust the day rule somewhat crudely.  The day
1522          * rules have been forced into DOW_IN_MONTH mode already, so we change
1523          * the day of week to move forward or back by a day.  It's possible to
1524          * make a more refined adjustment of the original rules first, but in
1525          * most cases this extra effort will go to waste once we adjust the day
1526          * rules anyway.
1527          */
1528         switch (startTimeMode) {
1529             case UTC_TIME -> startTime += rawOffset;
1530         }
1531         while (startTime < 0) {
1532             startTime += millisPerDay;
1533             startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day
1534         }
1535         while (startTime >= millisPerDay) {
1536             startTime -= millisPerDay;
1537             startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day
1538         }
1539 
1540         switch (endTimeMode) {
1541             case UTC_TIME -> endTime += rawOffset + dstSavings;
1542             case STANDARD_TIME -> endTime += dstSavings;
1543         }
1544         while (endTime < 0) {
1545             endTime += millisPerDay;
1546             endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day
1547         }
1548         while (endTime >= millisPerDay) {
1549             endTime -= millisPerDay;
1550             endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day
1551         }
1552     }
1553 
1554     /**
1555      * Pack the start and end rules into an array of bytes.  Only pack
1556      * data which is not preserved by makeRulesCompatible.
1557      */
packRules()1558     private byte[] packRules()
1559     {
1560         byte[] rules = new byte[MAX_RULE_NUM];
1561         rules[0] = (byte)startDay;
1562         rules[1] = (byte)startDayOfWeek;
1563         rules[2] = (byte)endDay;
1564         rules[3] = (byte)endDayOfWeek;
1565 
1566         // As of serial version 2, include time modes
1567         rules[4] = (byte)startTimeMode;
1568         rules[5] = (byte)endTimeMode;
1569 
1570         return rules;
1571     }
1572 
1573     /**
1574      * Given an array of bytes produced by packRules, interpret them
1575      * as the start and end rules.
1576      */
unpackRules(byte[] rules)1577     private void unpackRules(byte[] rules)
1578     {
1579         startDay       = rules[0];
1580         startDayOfWeek = rules[1];
1581         endDay         = rules[2];
1582         endDayOfWeek   = rules[3];
1583 
1584         // As of serial version 2, include time modes
1585         if (rules.length >= MAX_RULE_NUM) {
1586             startTimeMode = rules[4];
1587             endTimeMode   = rules[5];
1588         }
1589     }
1590 
1591     /**
1592      * Pack the start and end times into an array of bytes.  This is required
1593      * as of serial version 2.
1594      */
packTimes()1595     private int[] packTimes() {
1596         int[] times = new int[2];
1597         times[0] = startTime;
1598         times[1] = endTime;
1599         return times;
1600     }
1601 
1602     /**
1603      * Unpack the start and end times from an array of bytes.  This is required
1604      * as of serial version 2.
1605      */
unpackTimes(int[] times)1606     private void unpackTimes(int[] times) {
1607         startTime = times[0];
1608         endTime = times[1];
1609     }
1610 
1611     /**
1612      * Save the state of this object to a stream (i.e., serialize it).
1613      *
1614      * @serialData We write out two formats, a JDK 1.1 compatible format, using
1615      * {@code DOW_IN_MONTH_MODE} rules, in the required section, followed
1616      * by the full rules, in packed format, in the optional section.  The
1617      * optional section will be ignored by JDK 1.1 code upon stream in.
1618      * <p> Contents of the optional section: The length of a byte array is
1619      * emitted (int); this is 4 as of this release. The byte array of the given
1620      * length is emitted. The contents of the byte array are the true values of
1621      * the fields {@code startDay}, {@code startDayOfWeek},
1622      * {@code endDay}, and {@code endDayOfWeek}.  The values of these
1623      * fields in the required section are approximate values suited to the rule
1624      * mode {@code DOW_IN_MONTH_MODE}, which is the only mode recognized by
1625      * JDK 1.1.
1626      */
1627     @java.io.Serial
writeObject(ObjectOutputStream stream)1628     private void writeObject(ObjectOutputStream stream)
1629          throws IOException
1630     {
1631         // Construct a binary rule
1632         byte[] rules = packRules();
1633         int[] times = packTimes();
1634 
1635         // Convert to 1.1 FCS rules.  This step may cause us to lose information.
1636         makeRulesCompatible();
1637 
1638         // Write out the 1.1 FCS rules
1639         stream.defaultWriteObject();
1640 
1641         // Write out the binary rules in the optional data area of the stream.
1642         stream.writeInt(rules.length);
1643         stream.write(rules);
1644         stream.writeObject(times);
1645 
1646         // Recover the original rules.  This recovers the information lost
1647         // by makeRulesCompatible.
1648         unpackRules(rules);
1649         unpackTimes(times);
1650     }
1651 
1652     /**
1653      * Reconstitute this object from a stream (i.e., deserialize it).
1654      *
1655      * We handle both JDK 1.1
1656      * binary formats and full formats with a packed byte array.
1657      */
1658     @java.io.Serial
readObject(ObjectInputStream stream)1659     private void readObject(ObjectInputStream stream)
1660          throws IOException, ClassNotFoundException
1661     {
1662         stream.defaultReadObject();
1663 
1664         if (serialVersionOnStream < 1) {
1665             // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
1666             // startDayOfWeek and endDayOfWeek were usually uninitialized.  We can't do
1667             // too much, so we assume SUNDAY, which actually works most of the time.
1668             if (startDayOfWeek == 0) {
1669                 startDayOfWeek = Calendar.SUNDAY;
1670             }
1671             if (endDayOfWeek == 0) {
1672                 endDayOfWeek = Calendar.SUNDAY;
1673             }
1674 
1675             // The variables dstSavings, startMode, and endMode are post-1.1, so they
1676             // won't be present if we're reading from a 1.1 stream.  Fix them up.
1677             startMode = endMode = DOW_IN_MONTH_MODE;
1678             dstSavings = millisPerHour;
1679         } else {
1680             // For 1.1.4, in addition to the 3 new instance variables, we also
1681             // store the actual rules (which have not be made compatible with 1.1)
1682             // in the optional area.  Read them in here and parse them.
1683             int length = stream.readInt();
1684             if (length <= MAX_RULE_NUM) {
1685                 byte[] rules = new byte[length];
1686                 stream.readFully(rules);
1687                 unpackRules(rules);
1688             } else {
1689                 throw new InvalidObjectException("Too many rules: " + length);
1690             }
1691         }
1692 
1693         if (serialVersionOnStream >= 2) {
1694             int[] times = (int[]) stream.readObject();
1695             unpackTimes(times);
1696         }
1697 
1698         serialVersionOnStream = currentSerialVersion;
1699     }
1700 }
1701