KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > SimpleTimeZone


1 /*
2  * @(#)SimpleTimeZone.java 1.49 04/01/12
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 /*
9  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
10  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
11  *
12  * The original version of this source code and documentation is copyrighted
13  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
14  * materials are provided under terms of a License Agreement between Taligent
15  * and Sun. This technology is protected by multiple US and International
16  * patents. This notice and attribution to Taligent may not be removed.
17  * Taligent is a registered trademark of Taligent, Inc.
18  *
19  */

20
21 package java.util;
22
23 import java.io.ObjectInputStream JavaDoc;
24 import java.io.ObjectOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import sun.util.calendar.CalendarSystem;
27 import sun.util.calendar.CalendarUtils;
28 import sun.util.calendar.BaseCalendar;
29 import sun.util.calendar.Gregorian;
30
31 /**
32  * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
33  * that represents a time zone for use with a Gregorian calendar.
34  * The class holds an offset from GMT, called <em>raw offset</em>, and start
35  * and end rules for a daylight saving time schedule. Since it only holds
36  * single values for each, it cannot handle historical changes in the offset
37  * from GMT and the daylight saving schedule, except that the {@link
38  * #setStartYear setStartYear} method can specify the year when the daylight
39  * saving time schedule starts in effect.
40  * <p>
41  * To construct a <code>SimpleTimeZone</code> with a daylight saving time
42  * schedule, the schedule can be described with a set of rules,
43  * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time
44  * starts or ends is specified by a combination of <em>month</em>,
45  * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em>
46  * value is represented by a Calendar {@link Calendar#MONTH MONTH} field
47  * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is
48  * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value,
49  * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations
50  * are as follows.
51  *
52  * <ul>
53  * <li><b>Exact day of month</b><br>
54  * To specify an exact day of month, set the <em>month</em> and
55  * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For
56  * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH
57  * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li>
58  *
59  * <li><b>Day of week on or after day of month</b><br>
60  * To specify a day of week on or after an exact day of month, set the
61  * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on
62  * or after which the rule is applied, and <em>day-of-week</em> to a negative {@link
63  * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the
64  * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL},
65  * <em>day-of-month</em> to 8, and <em>day-of-week</em> to <code>-</code>{@link
66  * Calendar#SUNDAY SUNDAY}.</li>
67  *
68  * <li><b>Day of week on or before day of month</b><br>
69  * To specify a day of the week on or before an exact day of the month, set
70  * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For
71  * example, to specify the last Wednesday on or before the 21st of March, set
72  * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21
73  * and <em>day-of-week</em> is <code>-</code>{@link Calendar#WEDNESDAY WEDNESDAY}. </li>
74  *
75  * <li><b>Last day-of-week of month</b><br>
76  * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a
77  * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to
78  * -1. For example, to specify the last Sunday of October, set <em>month</em>
79  * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link
80  * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1. </li>
81  *
82  * </ul>
83  * The time of the day at which daylight saving time starts or ends is
84  * specified by a millisecond value within the day. There are three kinds of
85  * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link
86  * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight
87  * saving time ends
88  * at 2:00 am in the wall clock time, it can be specified by 7200000
89  * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time
90  * for an <em>end-rule</em> means the same thing as the daylight time.
91  * <p>
92  * The following are examples of parameters for constructing time zone objects.
93  * <pre><code>
94  * // Base GMT offset: -8:00
95  * // DST starts: at 2:00am in standard time
96  * // on the first Sunday in April
97  * // DST ends: at 2:00am in daylight time
98  * // on the last Sunday in October
99  * // Save: 1 hour
100  * SimpleTimeZone(-28800000,
101  * "America/Los_Angeles",
102  * Calendar.APRIL, 1, -Calendar.SUNDAY,
103  * 7200000,
104  * Calendar.OCTOBER, -1, Calendar.SUNDAY,
105  * 7200000,
106  * 3600000)
107  *
108  * // Base GMT offset: +1:00
109  * // DST starts: at 1:00am in UTC time
110  * // on the last Sunday in March
111  * // DST ends: at 1:00am in UTC time
112  * // on the last Sunday in October
113  * // Save: 1 hour
114  * SimpleTimeZone(3600000,
115  * "Europe/Paris",
116  * Calendar.MARCH, -1, Calendar.SUNDAY,
117  * 3600000, SimpleTimeZone.UTC_TIME,
118  * Calendar.OCTOBER, -1, Calendar.SUNDAY,
119  * 3600000, SimpleTimeZone.UTC_TIME,
120  * 3600000)
121  * </code></pre>
122  * These parameter rules are also applicable to the set rule methods, such as
123  * <code>setStartRule</code>.
124  *
125  * @since 1.1
126  * @see Calendar
127  * @see GregorianCalendar
128  * @see TimeZone
129  * @version 1.49 01/12/04
130  * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
131  */

132
133 public class SimpleTimeZone extends TimeZone JavaDoc {
134     /**
135      * Constructs a SimpleTimeZone with the given base time zone offset from GMT
136      * and time zone ID with no daylight saving time schedule.
137      *
138      * @param rawOffset The base time zone offset in milliseconds to GMT.
139      * @param ID The time zone name that is given to this instance.
140      */

141     public SimpleTimeZone(int rawOffset, String JavaDoc ID)
142     {
143         this.rawOffset = rawOffset;
144         setID (ID);
145         dstSavings = millisPerHour; // In case user sets rules later
146
}
147
148     /**
149      * Constructs a SimpleTimeZone with the given base time zone offset from
150      * GMT, time zone ID, and rules for starting and ending the daylight
151      * time.
152      * Both <code>startTime</code> and <code>endTime</code> are specified to be
153      * represented in the wall clock time. The amount of daylight saving is
154      * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is
155      * equivalent to:
156      * <pre><code>
157      * SimpleTimeZone(rawOffset,
158      * ID,
159      * startMonth,
160      * startDay,
161      * startDayOfWeek,
162      * startTime,
163      * SimpleTimeZone.{@link #WALL_TIME},
164      * endMonth,
165      * endDay,
166      * endDayOfWeek,
167      * endTime,
168      * SimpleTimeZone.{@link #WALL_TIME},
169      * 3600000)
170      * </code></pre>
171      *
172      * @param rawOffset The given base time zone offset from GMT.
173      * @param ID The time zone ID which is given to this object.
174      * @param startMonth The daylight saving time starting month. Month is
175      * a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0
176      * for January).
177      * @param startDay The day of the month on which the daylight saving time starts.
178      * See the class description for the special cases of this parameter.
179      * @param startDayOfWeek The daylight saving time starting day-of-week.
180      * See the class description for the special cases of this parameter.
181      * @param startTime The daylight saving time starting time in local wall clock
182      * time (in milliseconds within the day), which is local
183      * standard time in this case.
184      * @param endMonth The daylight saving time ending month. Month is
185      * a {@link Calendar#MONTH MONTH} field
186      * value (0-based. e.g., 9 for October).
187      * @param endDay The day of the month on which the daylight saving time ends.
188      * See the class description for the special cases of this parameter.
189      * @param endDayOfWeek The daylight saving time ending day-of-week.
190      * See the class description for the special cases of this parameter.
191      * @param endTime The daylight saving ending time in local wall clock time,
192      * (in milliseconds within the day) which is local daylight
193      * time in this case.
194      * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
195      * parameters are out of range for the start or end rule
196      */

197     public SimpleTimeZone(int rawOffset, String JavaDoc ID,
198                           int startMonth, int startDay, int startDayOfWeek, int startTime,
199                           int endMonth, int endDay, int endDayOfWeek, int endTime)
200     {
201         this(rawOffset, ID,
202              startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
203              endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
204              millisPerHour);
205     }
206
207     /**
208      * Constructs a SimpleTimeZone with the given base time zone offset from
209      * GMT, time zone ID, and rules for starting and ending the daylight
210      * time.
211      * Both <code>startTime</code> and <code>endTime</code> are assumed to be
212      * represented in the wall clock time. This constructor is equivalent to:
213      * <pre><code>
214      * SimpleTimeZone(rawOffset,
215      * ID,
216      * startMonth,
217      * startDay,
218      * startDayOfWeek,
219      * startTime,
220      * SimpleTimeZone.{@link #WALL_TIME},
221      * endMonth,
222      * endDay,
223      * endDayOfWeek,
224      * endTime,
225      * SimpleTimeZone.{@link #WALL_TIME},
226      * dstSavings)
227      * </code></pre>
228      *
229      * @param rawOffset The given base time zone offset from GMT.
230      * @param ID The time zone ID which is given to this object.
231      * @param startMonth The daylight saving time starting month. Month is
232      * a {@link Calendar#MONTH MONTH} field
233      * value (0-based. e.g., 0 for January).
234      * @param startDay The day of the month on which the daylight saving time starts.
235      * See the class description for the special cases of this parameter.
236      * @param startDayOfWeek The daylight saving time starting day-of-week.
237      * See the class description for the special cases of this parameter.
238      * @param startTime The daylight saving time starting time in local wall clock
239      * time, which is local standard time in this case.
240      * @param endMonth The daylight saving time ending month. Month is
241      * a {@link Calendar#MONTH MONTH} field
242      * value (0-based. e.g., 9 for October).
243      * @param endDay The day of the month on which the daylight saving time ends.
244      * See the class description for the special cases of this parameter.
245      * @param endDayOfWeek The daylight saving time ending day-of-week.
246      * See the class description for the special cases of this parameter.
247      * @param endTime The daylight saving ending time in local wall clock time,
248      * which is local daylight time in this case.
249      * @param dstSavings The amount of time in milliseconds saved during
250      * daylight saving time.
251      * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
252      * parameters are out of range for the start or end rule
253      * @since 1.2
254      */

255     public SimpleTimeZone(int rawOffset, String JavaDoc ID,
256                           int startMonth, int startDay, int startDayOfWeek, int startTime,
257                           int endMonth, int endDay, int endDayOfWeek, int endTime,
258                           int dstSavings)
259     {
260         this(rawOffset, ID,
261              startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
262              endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
263              dstSavings);
264     }
265
266     /**
267      * Constructs a SimpleTimeZone with the given base time zone offset from
268      * GMT, time zone ID, and rules for starting and ending the daylight
269      * time.
270      * This constructor takes the full set of the start and end rules
271      * parameters, including modes of <code>startTime</code> and
272      * <code>endTime</code>. The mode specifies either {@link #WALL_TIME wall
273      * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC
274      * time}.
275      *
276      * @param rawOffset The given base time zone offset from GMT.
277      * @param ID The time zone ID which is given to this object.
278      * @param startMonth The daylight saving time starting month. Month is
279      * a {@link Calendar#MONTH MONTH} field
280      * value (0-based. e.g., 0 for January).
281      * @param startDay The day of the month on which the daylight saving time starts.
282      * See the class description for the special cases of this parameter.
283      * @param startDayOfWeek The daylight saving time starting day-of-week.
284      * See the class description for the special cases of this parameter.
285      * @param startTime The daylight saving time starting time in the time mode
286      * specified by <code>startTimeMode</code>.
287      * @param startTimeMode The mode of the start time specified by startTime.
288      * @param endMonth The daylight saving time ending month. Month is
289      * a {@link Calendar#MONTH MONTH} field
290      * value (0-based. e.g., 9 for October).
291      * @param endDay The day of the month on which the daylight saving time ends.
292      * See the class description for the special cases of this parameter.
293      * @param endDayOfWeek The daylight saving time ending day-of-week.
294      * See the class description for the special cases of this parameter.
295      * @param endTime The daylight saving ending time in time time mode
296      * specified by <code>endTimeMode</code>.
297      * @param endTimeMode The mode of the end time specified by endTime
298      * @param dstSavings The amount of time in milliseconds saved during
299      * daylight saving time.
300      *
301      * @exception IllegalArgumentException if the month, day, dayOfWeek, time more, or
302      * time parameters are out of range for the start or end rule, or if a time mode
303      * value is invalid.
304      *
305      * @see #WALL_TIME
306      * @see #STANDARD_TIME
307      * @see #UTC_TIME
308      *
309      * @since 1.4
310      */

311     public SimpleTimeZone(int rawOffset, String JavaDoc ID,
312               int startMonth, int startDay, int startDayOfWeek,
313               int startTime, int startTimeMode,
314               int endMonth, int endDay, int endDayOfWeek,
315               int endTime, int endTimeMode,
316               int dstSavings) {
317
318         setID(ID);
319         this.rawOffset = rawOffset;
320         this.startMonth = startMonth;
321         this.startDay = startDay;
322         this.startDayOfWeek = startDayOfWeek;
323         this.startTime = startTime;
324         this.startTimeMode = startTimeMode;
325         this.endMonth = endMonth;
326         this.endDay = endDay;
327         this.endDayOfWeek = endDayOfWeek;
328         this.endTime = endTime;
329         this.endTimeMode = endTimeMode;
330         this.dstSavings = dstSavings;
331
332         // this.useDaylight is set by decodeRules
333
decodeRules();
334         if (dstSavings <= 0) {
335             throw new IllegalArgumentException JavaDoc("Illegal daylight saving value: " + dstSavings);
336         }
337     }
338
339     /**
340      * Sets the daylight saving time starting year.
341      *
342      * @param year The daylight saving starting year.
343      */

344     public void setStartYear(int year)
345     {
346         startYear = year;
347     invalidateCache();
348     }
349
350     /**
351      * Sets the daylight saving time start rule. For example, if daylight saving
352      * time starts on the first Sunday in April at 2 am in local wall clock
353      * time, you can set the start rule by calling:
354      * <pre><code>setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);</code></pre>
355      *
356      * @param startMonth The daylight saving time starting month. Month is
357      * a {@link Calendar#MONTH MONTH} field
358      * value (0-based. e.g., 0 for January).
359      * @param startDay The day of the month on which the daylight saving time starts.
360      * See the class description for the special cases of this parameter.
361      * @param startDayOfWeek The daylight saving time starting day-of-week.
362      * See the class description for the special cases of this parameter.
363      * @param startTime The daylight saving time starting time in local wall clock
364      * time, which is local standard time in this case.
365      * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
366      * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
367      */

368     public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)
369     {
370         this.startMonth = startMonth;
371         this.startDay = startDay;
372         this.startDayOfWeek = startDayOfWeek;
373         this.startTime = startTime;
374         startTimeMode = WALL_TIME;
375         decodeStartRule();
376     invalidateCache();
377     }
378
379     /**
380      * Sets the daylight saving time start rule to a fixed date within a month.
381      * This method is equivalent to:
382      * <pre><code>setStartRule(startMonth, startDay, 0, startTime)</code></pre>
383      *
384      * @param startMonth The daylight saving time starting month. Month is
385      * a {@link Calendar#MONTH MONTH} field
386      * value (0-based. e.g., 0 for January).
387      * @param startDay The day of the month on which the daylight saving time starts.
388      * @param startTime The daylight saving time starting time in local wall clock
389      * time, which is local standard time in this case.
390      * See the class description for the special cases of this parameter.
391      * @exception IllegalArgumentException if the <code>startMonth</code>,
392      * <code>startDayOfMonth</code>, or <code>startTime</code> parameters are out of range
393      * @since 1.2
394      */

395     public void setStartRule(int startMonth, int startDay, int startTime) {
396         setStartRule(startMonth, startDay, 0, startTime);
397     }
398
399     /**
400      * Sets the daylight saving time start rule to a weekday before or after the given date within
401      * a month, e.g., the first Monday on or after the 8th.
402      *
403      * @param startMonth The daylight saving time starting month. Month is
404      * a {@link Calendar#MONTH MONTH} field
405      * value (0-based. e.g., 0 for January).
406      * @param startDay The day of the month on which the daylight saving time starts.
407      * @param startDayOfWeek The daylight saving time starting day-of-week.
408      * @param startTime The daylight saving time starting time in local wall clock
409      * time, which is local standard time in this case.
410      * @param after If true, this rule selects the first <code>dayOfWeek</code> on or
411      * <em>after</em> <code>dayOfMonth</code>. If false, this rule
412      * selects the last <code>dayOfWeek</code> on or <em>before</em>
413      * <code>dayOfMonth</code>.
414      * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
415      * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
416      * @since 1.2
417      */

418     public void setStartRule(int startMonth, int startDay, int startDayOfWeek,
419                  int startTime, boolean after)
420     {
421     // TODO: this method doesn't check the initial values of dayOfMonth or dayOfWeek.
422
if (after) {
423             setStartRule(startMonth, startDay, -startDayOfWeek, startTime);
424         } else {
425             setStartRule(startMonth, -startDay, -startDayOfWeek, startTime);
426     }
427     }
428
429     /**
430      * Sets the daylight saving time end rule. For example, if daylight saving time
431      * ends on the last Sunday in October at 2 am in wall clock time,
432      * you can set the end rule by calling:
433      * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
434      *
435      * @param endMonth The daylight saving time ending month. Month is
436      * a {@link Calendar#MONTH MONTH} field
437      * value (0-based. e.g., 9 for October).
438      * @param endDay The day of the month on which the daylight saving time ends.
439      * See the class description for the special cases of this parameter.
440      * @param endDayOfWeek The daylight saving time ending day-of-week.
441      * See the class description for the special cases of this parameter.
442      * @param endTime The daylight saving ending time in local wall clock time,
443      * (in milliseconds within the day) which is local daylight
444      * time in this case.
445      * @exception IllegalArgumentException if the <code>endMonth</code>, <code>endDay</code>,
446      * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
447      */

448     public void setEndRule(int endMonth, int endDay, int endDayOfWeek,
449                            int endTime)
450     {
451         this.endMonth = endMonth;
452         this.endDay = endDay;
453         this.endDayOfWeek = endDayOfWeek;
454         this.endTime = endTime;
455         this.endTimeMode = WALL_TIME;
456         decodeEndRule();
457     invalidateCache();
458     }
459
460     /**
461      * Sets the daylight saving time end rule to a fixed date within a month.
462      * This method is equivalent to:
463      * <pre><code>setEndRule(endMonth, endDay, 0, endTime)</code></pre>
464      *
465      * @param endMonth The daylight saving time ending month. Month is
466      * a {@link Calendar#MONTH MONTH} field
467      * value (0-based. e.g., 9 for October).
468      * @param endDay The day of the month on which the daylight saving time ends.
469      * @param endTime The daylight saving ending time in local wall clock time,
470      * (in milliseconds within the day) which is local daylight
471      * time in this case.
472      * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
473      * or <code>endTime</code> parameters are out of range
474      * @since 1.2
475      */

476     public void setEndRule(int endMonth, int endDay, int endTime)
477     {
478         setEndRule(endMonth, endDay, 0, endTime);
479     }
480
481     /**
482      * Sets the daylight saving time end rule to a weekday before or after the given date within
483      * a month, e.g., the first Monday on or after the 8th.
484      *
485      * @param endMonth The daylight saving time ending month. Month is
486      * a {@link Calendar#MONTH MONTH} field
487      * value (0-based. e.g., 9 for October).
488      * @param endDay The day of the month on which the daylight saving time ends.
489      * @param endDayOfWeek The daylight saving time ending day-of-week.
490      * @param endTime The daylight saving ending time in local wall clock time,
491      * (in milliseconds within the day) which is local daylight
492      * time in this case.
493      * @param after If true, this rule selects the first <code>endDayOfWeek</code> on
494      * or <em>after</em> <code>endDay</code>. If false, this rule
495      * selects the last <code>endDayOfWeek</code> on or before
496      * <code>endDay</code> of the month.
497      * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
498      * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
499      * @since 1.2
500      */

501     public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)
502     {
503         if (after) {
504             setEndRule(endMonth, endDay, -endDayOfWeek, endTime);
505         } else {
506             setEndRule(endMonth, -endDay, -endDayOfWeek, endTime);
507     }
508     }
509
510     /**
511      * Returns the offset of this time zone from UTC at the given
512      * time. If daylight saving time is in effect at the given time,
513      * the offset value is adjusted with the amount of daylight
514      * saving.
515      *
516      * @param date the time at which the time zone offset is found
517      * @return the amount of time in milliseconds to add to UTC to get
518      * local time.
519      * @since 1.4
520      */

521     public int getOffset(long date) {
522     return getOffsets(date, null);
523     }
524
525     /**
526      * @see TimeZone#getOffsets
527      */

528     int getOffsets(long date, int[] offsets) {
529     int offset = rawOffset;
530
531       computeOffset:
532     if (useDaylight) {
533         synchronized (this) {
534         if (cacheStart != 0) {
535             if (date >= cacheStart && date < cacheEnd) {
536             offset += dstSavings;
537             break computeOffset;
538             }
539         }
540         }
541         BaseCalendar cal = date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
542         gcal : (BaseCalendar) CalendarSystem.forName("julian");
543         BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
544         // Get the year in local time
545
cal.getCalendarDate(date + rawOffset, cdate);
546         int year = cdate.getNormalizedYear();
547         if (year >= startYear) {
548         // Clear time elements for the transition calculations
549
cdate.setTimeOfDay(0, 0, 0, 0);
550         offset = getOffset(cal, cdate, year, date);
551         }
552     }
553
554     if (offsets != null) {
555         offsets[0] = rawOffset;
556         offsets[1] = offset - rawOffset;
557     }
558     return offset;
559     }
560
561    /**
562      * Returns the difference in milliseconds between local time and
563      * UTC, taking into account both the raw offset and the effect of
564      * daylight saving, for the specified date and time. This method
565      * assumes that the start and end month are distinct. It also
566      * uses a default {@link GregorianCalendar} object as its
567      * underlying calendar, such as for determining leap years. Do
568      * not use the result of this method with a calendar other than a
569      * default <code>GregorianCalendar</code>.
570      *
571      * <p><em>Note: In general, clients should use
572      * <code>Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)</code>
573      * instead of calling this method.</em>
574      *
575      * @param era The era of the given date.
576      * @param year The year in the given date.
577      * @param month The month in the given date. Month is 0-based. e.g.,
578      * 0 for January.
579      * @param day The day-in-month of the given date.
580      * @param dayOfWeek The day-of-week of the given date.
581      * @param millis The milliseconds in day in <em>standard</em> local time.
582      * @return The milliseconds to add to UTC to get local time.
583      * @exception IllegalArgumentException the <code>era</code>,
584      * <code>month</code>, <code>day</code>, <code>dayOfWeek</code>,
585      * or <code>millis</code> parameters are out of range
586      */

587     public int getOffset(int era, int year, int month, int day, int dayOfWeek,
588                          int millis)
589     {
590     if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
591             throw new IllegalArgumentException JavaDoc("Illegal era " + era);
592         }
593
594     int y = year;
595     if (era == GregorianCalendar.BC) {
596         // adjust y with the GregorianCalendar-style year numbering.
597
y = 1 - y;
598     }
599
600     // If the year isn't representable with the 64-bit long
601
// integer in milliseconds, convert the year to an
602
// equivalent year. This is required to pass some JCK test cases
603
// which are actually useless though because the specified years
604
// can't be supported by the Java time system.
605
if (y >= 292278994) {
606         y = 2800 + y % 2800;
607     } else if (y <= -292269054) {
608         // y %= 28 also produces an equivalent year, but positive
609
// year numbers would be convenient to use the UNIX cal
610
// command.
611
y = (int) CalendarUtils.mod((long) y, 28);
612     }
613
614     // convert year to its 1-based month value
615
int m = month + 1;
616
617     // First, calculate time as a Gregorian date.
618
BaseCalendar cal = gcal;
619     BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
620     cdate.setDate(y, m, day);
621     long time = cal.getTime(cdate); // normalize cdate
622
time += millis - rawOffset; // UTC time
623

624     // If the time value represents a time before the default
625
// Gregorian cutover, recalculate time using the Julian
626
// calendar system. For the Julian calendar system, the
627
// normalized year numbering is ..., -2 (BCE 2), -1 (BCE 1),
628
// 1, 2 ... which is different from the GregorianCalendar
629
// style year numbering (..., -1, 0 (BCE 1), 1, 2, ...).
630
if (time < GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER) {
631         cal = (BaseCalendar) CalendarSystem.forName("julian");
632         cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
633         cdate.setNormalizedDate(y, m, day);
634         time = cal.getTime(cdate) + millis - rawOffset;
635     }
636
637     if ((cdate.getNormalizedYear() != y)
638         || (cdate.getMonth() != m)
639         || (cdate.getDayOfMonth() != day)
640         // The validation should be cdate.getDayOfWeek() ==
641
// dayOfWeek. However, we don't check dayOfWeek for
642
// compatibility.
643
|| (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
644         || (millis < 0 || millis >= (24*60*60*1000))) {
645             throw new IllegalArgumentException JavaDoc();
646     }
647
648         if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
649         return rawOffset;
650     }
651
652     return getOffset(cal, cdate, y, time);
653     }
654
655     private int getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time) {
656     synchronized (this) {
657         if (cacheStart != 0) {
658         if (time >= cacheStart && time < cacheEnd) {
659             return rawOffset + dstSavings;
660         }
661         if (year == cacheYear) {
662             return rawOffset;
663         }
664         }
665     }
666
667     long start = getStart(cal, cdate, year);
668     long end = getEnd(cal, cdate, year);
669     int offset = rawOffset;
670     if (start <= end) {
671         if (time >= start && time < end) {
672         offset += dstSavings;
673         }
674         synchronized (this) {
675         cacheYear = year;
676         cacheStart = start;
677         cacheEnd = end;
678         }
679     } else {
680         if (time < end) {
681         // TODO: support Gregorian cutover. The previous year
682
// may be in the other calendar system.
683
start = getStart(cal, cdate, year - 1);
684         if (time >= start) {
685             offset += dstSavings;
686         }
687         } else if (time >= start) {
688         // TODO: support Gregorian cutover. The next year
689
// may be in the other calendar system.
690
end = getEnd(cal, cdate, year + 1);
691         if (time < end) {
692             offset += dstSavings;
693         }
694         }
695         if (start <= end) {
696         synchronized (this) {
697             // The start and end transitions are in multiple years.
698
cacheYear = (long) startYear - 1;
699             cacheStart = start;
700             cacheEnd = end;
701         }
702         }
703     }
704     return offset;
705     }
706
707     private long getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
708     int time = startTime;
709     if (startTimeMode != UTC_TIME) {
710         time -= rawOffset;
711     }
712     return getTransition(cal, cdate, startMode, year, startMonth, startDay,
713                  startDayOfWeek, time);
714     }
715
716     private long getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
717     int time = endTime;
718     if (startTimeMode != UTC_TIME) {
719         time -= rawOffset;
720     }
721     if (startTimeMode == WALL_TIME) {
722         time -= dstSavings;
723     }
724     return getTransition(cal, cdate, endMode, year, endMonth, endDay,
725                     endDayOfWeek, time);
726     }
727
728     private long getTransition(BaseCalendar cal, BaseCalendar.Date cdate,
729                    int mode, int year, int month, int dayOfMonth,
730                    int dayOfWeek, int timeOfDay) {
731     cdate.setNormalizedYear(year);
732     cdate.setMonth(month + 1);
733     switch (mode) {
734     case DOM_MODE:
735         cdate.setDayOfMonth(dayOfMonth);
736         break;
737
738     case DOW_IN_MONTH_MODE:
739         cdate.setDayOfMonth(1);
740         if (dayOfMonth < 0) {
741         cdate.setDayOfMonth(cal.getMonthLength(cdate));
742         }
743         cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(dayOfMonth, dayOfWeek, cdate);
744         break;
745
746     case DOW_GE_DOM_MODE:
747         cdate.setDayOfMonth(dayOfMonth);
748         cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(1, dayOfWeek, cdate);
749         break;
750
751     case DOW_LE_DOM_MODE:
752         cdate.setDayOfMonth(dayOfMonth);
753         cdate = (BaseCalendar.Date) cal.getNthDayOfWeek(-1, dayOfWeek, cdate);
754         break;
755     }
756     return cal.getTime(cdate) + timeOfDay;
757     }
758
759     /**
760      * Gets the GMT offset for this time zone.
761      * @return the GMT offset value in milliseconds
762      * @see #setRawOffset
763      */

764     public int getRawOffset()
765     {
766         // The given date will be taken into account while
767
// we have the historical time zone data in place.
768
return rawOffset;
769     }
770
771     /**
772      * Sets the base time zone offset to GMT.
773      * This is the offset to add to UTC to get local time.
774      * @see #getRawOffset
775      */

776     public void setRawOffset(int offsetMillis)
777     {
778         this.rawOffset = offsetMillis;
779     }
780
781     /**
782      * Sets the amount of time in milliseconds that the clock is advanced
783      * during daylight saving time.
784      * @param millisSavedDuringDST the number of milliseconds the time is
785      * advanced with respect to standard time when the daylight saving time rules
786      * are in effect. A positive number, typically one hour (3600000).
787      * @see #getDSTSavings
788      * @since 1.2
789      */

790     public void setDSTSavings(int millisSavedDuringDST) {
791         if (millisSavedDuringDST <= 0) {
792             throw new IllegalArgumentException JavaDoc("Illegal daylight saving value: "
793                            + millisSavedDuringDST);
794         }
795         dstSavings = millisSavedDuringDST;
796     }
797
798     /**
799      * Returns the amount of time in milliseconds that the clock is
800      * advanced during daylight saving time.
801      *
802      * @return the number of milliseconds the time is advanced with
803      * respect to standard time when the daylight saving rules are in
804      * effect, or 0 (zero) if this time zone doesn't observe daylight
805      * saving time.
806      *
807      * @see #setDSTSavings
808      * @since 1.2
809      */

810     public int getDSTSavings() {
811     if (useDaylight) {
812         return dstSavings;
813     }
814     return 0;
815     }
816
817     /**
818      * Queries if this time zone uses daylight saving time.
819      * @return true if this time zone uses daylight saving time;
820      * false otherwise.
821      */

822     public boolean useDaylightTime()
823     {
824         return useDaylight;
825     }
826
827     /**
828      * Queries if the given date is in daylight saving time.
829      * @return true if daylight saving time is in effective at the
830      * given date; false otherwise.
831      */

832     public boolean inDaylightTime(Date JavaDoc date)
833     {
834     return (getOffset(date.getTime()) != rawOffset);
835     }
836
837     /**
838      * Returns a clone of this <code>SimpleTimeZone</code> instance.
839      * @return a clone of this instance.
840      */

841     public Object JavaDoc clone()
842     {
843     return super.clone();
844     }
845
846     /**
847      * Generates the hash code for the SimpleDateFormat object.
848      * @return the hash code for this object
849      */

850     public synchronized int hashCode()
851     {
852         return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
853             endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
854     }
855
856     /**
857      * Compares the equality of two <code>SimpleTimeZone</code> objects.
858      *
859      * @param obj The <code>SimpleTimeZone</code> object to be compared with.
860      * @return True if the given <code>obj</code> is the same as this
861      * <code>SimpleTimeZone</code> object; false otherwise.
862      */

863     public boolean equals(Object JavaDoc obj)
864     {
865         if (this == obj) {
866             return true;
867     }
868         if (!(obj instanceof SimpleTimeZone JavaDoc)) {
869             return false;
870     }
871
872         SimpleTimeZone JavaDoc that = (SimpleTimeZone JavaDoc) obj;
873
874         return getID().equals(that.getID()) &&
875             hasSameRules(that);
876     }
877
878     /**
879      * Returns <code>true</code> if this zone has the same rules and offset as another zone.
880      * @param other the TimeZone object to be compared with
881      * @return <code>true</code> if the given zone is a SimpleTimeZone and has the
882      * same rules and offset as this one
883      * @since 1.2
884      */

885     public boolean hasSameRules(TimeZone JavaDoc other) {
886         if (this == other) {
887         return true;
888     }
889         if (!(other instanceof SimpleTimeZone JavaDoc)) {
890         return false;
891     }
892         SimpleTimeZone JavaDoc that = (SimpleTimeZone JavaDoc) other;
893         return rawOffset == that.rawOffset &&
894             useDaylight == that.useDaylight &&
895             (!useDaylight
896              // Only check rules if using DST
897
|| (dstSavings == that.dstSavings &&
898                  startMode == that.startMode &&
899                  startMonth == that.startMonth &&
900                  startDay == that.startDay &&
901                  startDayOfWeek == that.startDayOfWeek &&
902                  startTime == that.startTime &&
903                  startTimeMode == that.startTimeMode &&
904                  endMode == that.endMode &&
905                  endMonth == that.endMonth &&
906                  endDay == that.endDay &&
907                  endDayOfWeek == that.endDayOfWeek &&
908                  endTime == that.endTime &&
909                  endTimeMode == that.endTimeMode &&
910                  startYear == that.startYear));
911     }
912
913     /**
914      * Returns a string representation of this time zone.
915      * @return a string representation of this time zone.
916      */

917     public String JavaDoc toString() {
918         return getClass().getName() +
919             "[id=" + getID() +
920             ",offset=" + rawOffset +
921             ",dstSavings=" + dstSavings +
922             ",useDaylight=" + useDaylight +
923             ",startYear=" + startYear +
924             ",startMode=" + startMode +
925             ",startMonth=" + startMonth +
926             ",startDay=" + startDay +
927             ",startDayOfWeek=" + startDayOfWeek +
928             ",startTime=" + startTime +
929             ",startTimeMode=" + startTimeMode +
930             ",endMode=" + endMode +
931             ",endMonth=" + endMonth +
932             ",endDay=" + endDay +
933             ",endDayOfWeek=" + endDayOfWeek +
934             ",endTime=" + endTime +
935             ",endTimeMode=" + endTimeMode + ']';
936     }
937
938     // =======================privates===============================
939

940     /**
941      * The month in which daylight saving time starts. This value must be
942      * between <code>Calendar.JANUARY</code> and
943      * <code>Calendar.DECEMBER</code> inclusive. This value must not equal
944      * <code>endMonth</code>.
945      * <p>If <code>useDaylight</code> is false, this value is ignored.
946      * @serial
947      */

948     private int startMonth;
949
950     /**
951      * This field has two possible interpretations:
952      * <dl>
953      * <dt><code>startMode == DOW_IN_MONTH</code></dt>
954      * <dd>
955      * <code>startDay</code> indicates the day of the month of
956      * <code>startMonth</code> on which daylight
957      * saving time starts, from 1 to 28, 30, or 31, depending on the
958      * <code>startMonth</code>.
959      * </dd>
960      * <dt><code>startMode != DOW_IN_MONTH</code></dt>
961      * <dd>
962      * <code>startDay</code> indicates which <code>startDayOfWeek</code> in th
963      * month <code>startMonth</code> daylight
964      * saving time starts on. For example, a value of +1 and a
965      * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
966      * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the
967      * second Sunday, and -1 the last Sunday. A value o