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