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.
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.
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).
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.
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
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
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.
41 import java.io.ObjectInputStream;
42 import java.io.ObjectOutputStream;
43 import java.io.IOException;
44 import java.util.Date.BaseCalendar;
47 * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
48 * that represents a time zone for use with a Gregorian calendar.
49 * The class holds an offset from GMT, called <em>raw offset</em>, and start
50 * and end rules for a daylight saving time schedule. Since it only holds
51 * single values for each, it cannot handle historical changes in the offset
52 * from GMT and the daylight saving schedule, except that the {@link
53 * #setStartYear setStartYear} method can specify the year when the daylight
54 * saving time schedule starts in effect.
56 * To construct a <code>SimpleTimeZone</code> with a daylight saving time
57 * schedule, the schedule can be described with a set of rules,
58 * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time
59 * starts or ends is specified by a combination of <em>month</em>,
60 * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em>
61 * value is represented by a Calendar {@link Calendar#MONTH MONTH} field
62 * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is
63 * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value,
64 * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations
68 * <li><b>Exact day of month</b><br>
69 * To specify an exact day of month, set the <em>month</em> and
70 * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For
71 * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH
72 * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li>
74 * <li><b>Day of week on or after day of month</b><br>
75 * To specify a day of week on or after an exact day of month, set the
76 * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on
77 * or after which the rule is applied, and <em>day-of-week</em> to a negative {@link
78 * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the
79 * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL},
80 * <em>day-of-month</em> to 8, and <em>day-of-week</em> to <code>-</code>{@link
81 * Calendar#SUNDAY SUNDAY}.</li>
83 * <li><b>Day of week on or before day of month</b><br>
84 * To specify a day of the week on or before an exact day of the month, set
85 * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For
86 * example, to specify the last Wednesday on or before the 21st of March, set
87 * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21
88 * and <em>day-of-week</em> is <code>-</code>{@link Calendar#WEDNESDAY WEDNESDAY}. </li>
90 * <li><b>Last day-of-week of month</b><br>
91 * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a
92 * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to
93 * -1. For example, to specify the last Sunday of October, set <em>month</em>
94 * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link
95 * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1. </li>
98 * The time of the day at which daylight saving time starts or ends is
99 * specified by a millisecond value within the day. There are three kinds of
100 * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link
101 * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight
103 * at 2:00 am in the wall clock time, it can be specified by 7200000
104 * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time
105 * for an <em>end-rule</em> means the same thing as the daylight time.
107 * The following are examples of parameters for constructing time zone objects.
109 * // Base GMT offset: -8:00
110 * // DST starts: at 2:00am in standard time
111 * // on the first Sunday in April
112 * // DST ends: at 2:00am in daylight time
113 * // on the last Sunday in October
115 * SimpleTimeZone(-28800000,
116 * "America/Los_Angeles",
117 * Calendar.APRIL, 1, -Calendar.SUNDAY,
119 * Calendar.OCTOBER, -1, Calendar.SUNDAY,
123 * // Base GMT offset: +1:00
124 * // DST starts: at 1:00am in UTC time
125 * // on the last Sunday in March
126 * // DST ends: at 1:00am in UTC time
127 * // on the last Sunday in October
129 * SimpleTimeZone(3600000,
131 * Calendar.MARCH, -1, Calendar.SUNDAY,
132 * 3600000, SimpleTimeZone.UTC_TIME,
133 * Calendar.OCTOBER, -1, Calendar.SUNDAY,
134 * 3600000, SimpleTimeZone.UTC_TIME,
137 * These parameter rules are also applicable to the set rule methods, such as
138 * <code>setStartRule</code>.
142 * @see GregorianCalendar
144 * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
147 public class SimpleTimeZone extends TimeZone {
149 * Constructs a SimpleTimeZone with the given base time zone offset from GMT
150 * and time zone ID with no daylight saving time schedule.
152 * @param rawOffset The base time zone offset in milliseconds to GMT.
153 * @param ID The time zone name that is given to this instance.
155 public SimpleTimeZone(int rawOffset, String ID)
157 this.rawOffset = rawOffset;
159 dstSavings = millisPerHour; // In case user sets rules later
163 * Constructs a SimpleTimeZone with the given base time zone offset from
164 * GMT, time zone ID, and rules for starting and ending the daylight
166 * Both <code>startTime</code> and <code>endTime</code> are specified to be
167 * represented in the wall clock time. The amount of daylight saving is
168 * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is
171 * SimpleTimeZone(rawOffset,
177 * SimpleTimeZone.{@link #WALL_TIME},
182 * SimpleTimeZone.{@link #WALL_TIME},
186 * @param rawOffset The given base time zone offset from GMT.
187 * @param ID The time zone ID which is given to this object.
188 * @param startMonth The daylight saving time starting month. Month is
189 * a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0
191 * @param startDay The day of the month on which the daylight saving time starts.
192 * See the class description for the special cases of this parameter.
193 * @param startDayOfWeek The daylight saving time starting day-of-week.
194 * See the class description for the special cases of this parameter.
195 * @param startTime The daylight saving time starting time in local wall clock
196 * time (in milliseconds within the day), which is local
197 * standard time in this case.
198 * @param endMonth The daylight saving time ending month. Month is
199 * a {@link Calendar#MONTH MONTH} field
200 * value (0-based. e.g., 9 for October).
201 * @param endDay The day of the month on which the daylight saving time ends.
202 * See the class description for the special cases of this parameter.
203 * @param endDayOfWeek The daylight saving time ending day-of-week.
204 * See the class description for the special cases of this parameter.
205 * @param endTime The daylight saving ending time in local wall clock time,
206 * (in milliseconds within the day) which is local daylight
208 * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
209 * parameters are out of range for the start or end rule
211 public SimpleTimeZone(int rawOffset, String ID,
212 int startMonth, int startDay, int startDayOfWeek, int startTime,
213 int endMonth, int endDay, int endDayOfWeek, int endTime)
216 startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
217 endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
222 * Constructs a SimpleTimeZone with the given base time zone offset from
223 * GMT, time zone ID, and rules for starting and ending the daylight
225 * Both <code>startTime</code> and <code>endTime</code> are assumed to be
226 * represented in the wall clock time. This constructor is equivalent to:
228 * SimpleTimeZone(rawOffset,
234 * SimpleTimeZone.{@link #WALL_TIME},
239 * SimpleTimeZone.{@link #WALL_TIME},
243 * @param rawOffset The given base time zone offset from GMT.
244 * @param ID The time zone ID which is given to this object.
245 * @param startMonth The daylight saving time starting month. Month is
246 * a {@link Calendar#MONTH MONTH} field
247 * value (0-based. e.g., 0 for January).
248 * @param startDay The day of the month on which the daylight saving time starts.
249 * See the class description for the special cases of this parameter.
250 * @param startDayOfWeek The daylight saving time starting day-of-week.
251 * See the class description for the special cases of this parameter.
252 * @param startTime The daylight saving time starting time in local wall clock
253 * time, which is local standard time in this case.
254 * @param endMonth The daylight saving time ending month. Month is
255 * a {@link Calendar#MONTH MONTH} field
256 * value (0-based. e.g., 9 for October).
257 * @param endDay The day of the month on which the daylight saving time ends.
258 * See the class description for the special cases of this parameter.
259 * @param endDayOfWeek The daylight saving time ending day-of-week.
260 * See the class description for the special cases of this parameter.
261 * @param endTime The daylight saving ending time in local wall clock time,
262 * which is local daylight time in this case.
263 * @param dstSavings The amount of time in milliseconds saved during
264 * daylight saving time.
265 * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
266 * parameters are out of range for the start or end rule
269 public SimpleTimeZone(int rawOffset, String ID,
270 int startMonth, int startDay, int startDayOfWeek, int startTime,
271 int endMonth, int endDay, int endDayOfWeek, int endTime,
275 startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
276 endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
281 * Constructs a SimpleTimeZone with the given base time zone offset from
282 * GMT, time zone ID, and rules for starting and ending the daylight
284 * This constructor takes the full set of the start and end rules
285 * parameters, including modes of <code>startTime</code> and
286 * <code>endTime</code>. The mode specifies either {@link #WALL_TIME wall
287 * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC
290 * @param rawOffset The given base time zone offset from GMT.
291 * @param ID The time zone ID which is given to this object.
292 * @param startMonth The daylight saving time starting month. Month is
293 * a {@link Calendar#MONTH MONTH} field
294 * value (0-based. e.g., 0 for January).
295 * @param startDay The day of the month on which the daylight saving time starts.
296 * See the class description for the special cases of this parameter.
297 * @param startDayOfWeek The daylight saving time starting day-of-week.
298 * See the class description for the special cases of this parameter.
299 * @param startTime The daylight saving time starting time in the time mode
300 * specified by <code>startTimeMode</code>.
301 * @param startTimeMode The mode of the start time specified by startTime.
302 * @param endMonth The daylight saving time ending month. Month is
303 * a {@link Calendar#MONTH MONTH} field
304 * value (0-based. e.g., 9 for October).
305 * @param endDay The day of the month on which the daylight saving time ends.
306 * See the class description for the special cases of this parameter.
307 * @param endDayOfWeek The daylight saving time ending day-of-week.
308 * See the class description for the special cases of this parameter.
309 * @param endTime The daylight saving ending time in time time mode
310 * specified by <code>endTimeMode</code>.
311 * @param endTimeMode The mode of the end time specified by endTime
312 * @param dstSavings The amount of time in milliseconds saved during
313 * daylight saving time.
315 * @exception IllegalArgumentException if the month, day, dayOfWeek, time more, or
316 * time parameters are out of range for the start or end rule, or if a time mode
320 * @see #STANDARD_TIME
325 public SimpleTimeZone(int rawOffset, String ID,
326 int startMonth, int startDay, int startDayOfWeek,
327 int startTime, int startTimeMode,
328 int endMonth, int endDay, int endDayOfWeek,
329 int endTime, int endTimeMode,
333 this.rawOffset = rawOffset;
334 this.startMonth = startMonth;
335 this.startDay = startDay;
336 this.startDayOfWeek = startDayOfWeek;
337 this.startTime = startTime;
338 this.startTimeMode = startTimeMode;
339 this.endMonth = endMonth;
340 this.endDay = endDay;
341 this.endDayOfWeek = endDayOfWeek;
342 this.endTime = endTime;
343 this.endTimeMode = endTimeMode;
344 this.dstSavings = dstSavings;
346 // this.useDaylight is set by decodeRules
348 if (dstSavings <= 0) {
349 throw new IllegalArgumentException("Illegal daylight saving value: " + dstSavings);
354 * Sets the daylight saving time starting year.
356 * @param year The daylight saving starting year.
358 public void setStartYear(int year)
365 * Sets the daylight saving time start rule. For example, if daylight saving
366 * time starts on the first Sunday in April at 2 am in local wall clock
367 * time, you can set the start rule by calling:
368 * <pre><code>setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);</code></pre>
370 * @param startMonth The daylight saving time starting month. Month is
371 * a {@link Calendar#MONTH MONTH} field
372 * value (0-based. e.g., 0 for January).
373 * @param startDay The day of the month on which the daylight saving time starts.
374 * See the class description for the special cases of this parameter.
375 * @param startDayOfWeek The daylight saving time starting day-of-week.
376 * See the class description for the special cases of this parameter.
377 * @param startTime The daylight saving time starting time in local wall clock
378 * time, which is local standard time in this case.
379 * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
380 * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
382 public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)
384 this.startMonth = startMonth;
385 this.startDay = startDay;
386 this.startDayOfWeek = startDayOfWeek;
387 this.startTime = startTime;
388 startTimeMode = WALL_TIME;
394 * Sets the daylight saving time start rule to a fixed date within a month.
395 * This method is equivalent to:
396 * <pre><code>setStartRule(startMonth, startDay, 0, startTime)</code></pre>
398 * @param startMonth The daylight saving time starting month. Month is
399 * a {@link Calendar#MONTH MONTH} field
400 * value (0-based. e.g., 0 for January).
401 * @param startDay The day of the month on which the daylight saving time starts.
402 * @param startTime The daylight saving time starting time in local wall clock
403 * time, which is local standard time in this case.
404 * See the class description for the special cases of this parameter.
405 * @exception IllegalArgumentException if the <code>startMonth</code>,
406 * <code>startDayOfMonth</code>, or <code>startTime</code> parameters are out of range
409 public void setStartRule(int startMonth, int startDay, int startTime) {
410 setStartRule(startMonth, startDay, 0, startTime);
414 * Sets the daylight saving time start rule to a weekday before or after the given date within
415 * a month, e.g., the first Monday on or after the 8th.
417 * @param startMonth The daylight saving time starting month. Month is
418 * a {@link Calendar#MONTH MONTH} field
419 * value (0-based. e.g., 0 for January).
420 * @param startDay The day of the month on which the daylight saving time starts.
421 * @param startDayOfWeek The daylight saving time starting day-of-week.
422 * @param startTime The daylight saving time starting time in local wall clock
423 * time, which is local standard time in this case.
424 * @param after If true, this rule selects the first <code>dayOfWeek</code> on or
425 * <em>after</em> <code>dayOfMonth</code>. If false, this rule
426 * selects the last <code>dayOfWeek</code> on or <em>before</em>
427 * <code>dayOfMonth</code>.
428 * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
429 * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
432 public void setStartRule(int startMonth, int startDay, int startDayOfWeek,
433 int startTime, boolean after)
435 // TODO: this method doesn't check the initial values of dayOfMonth or dayOfWeek.
437 setStartRule(startMonth, startDay, -startDayOfWeek, startTime);
439 setStartRule(startMonth, -startDay, -startDayOfWeek, startTime);
444 * Sets the daylight saving time end rule. For example, if daylight saving time
445 * ends on the last Sunday in October at 2 am in wall clock time,
446 * you can set the end rule by calling:
447 * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
449 * @param endMonth The daylight saving time ending month. Month is
450 * a {@link Calendar#MONTH MONTH} field
451 * value (0-based. e.g., 9 for October).
452 * @param endDay The day of the month on which the daylight saving time ends.
453 * See the class description for the special cases of this parameter.
454 * @param endDayOfWeek The daylight saving time ending day-of-week.
455 * See the class description for the special cases of this parameter.
456 * @param endTime The daylight saving ending time in local wall clock time,
457 * (in milliseconds within the day) which is local daylight
459 * @exception IllegalArgumentException if the <code>endMonth</code>, <code>endDay</code>,
460 * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
462 public void setEndRule(int endMonth, int endDay, int endDayOfWeek,
465 this.endMonth = endMonth;
466 this.endDay = endDay;
467 this.endDayOfWeek = endDayOfWeek;
468 this.endTime = endTime;
469 this.endTimeMode = WALL_TIME;
475 * Sets the daylight saving time end rule to a fixed date within a month.
476 * This method is equivalent to:
477 * <pre><code>setEndRule(endMonth, endDay, 0, endTime)</code></pre>
479 * @param endMonth The daylight saving time ending month. Month is
480 * a {@link Calendar#MONTH MONTH} field
481 * value (0-based. e.g., 9 for October).
482 * @param endDay The day of the month on which the daylight saving time ends.
483 * @param endTime The daylight saving ending time in local wall clock time,
484 * (in milliseconds within the day) which is local daylight
486 * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
487 * or <code>endTime</code> parameters are out of range
490 public void setEndRule(int endMonth, int endDay, int endTime)
492 setEndRule(endMonth, endDay, 0, endTime);
496 * Sets the daylight saving time end rule to a weekday before or after the given date within
497 * a month, e.g., the first Monday on or after the 8th.
499 * @param endMonth The daylight saving time ending month. Month is
500 * a {@link Calendar#MONTH MONTH} field
501 * value (0-based. e.g., 9 for October).
502 * @param endDay The day of the month on which the daylight saving time ends.
503 * @param endDayOfWeek The daylight saving time ending day-of-week.
504 * @param endTime The daylight saving ending time in local wall clock time,
505 * (in milliseconds within the day) which is local daylight
507 * @param after If true, this rule selects the first <code>endDayOfWeek</code> on
508 * or <em>after</em> <code>endDay</code>. If false, this rule
509 * selects the last <code>endDayOfWeek</code> on or before
510 * <code>endDay</code> of the month.
511 * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
512 * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
515 public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)
518 setEndRule(endMonth, endDay, -endDayOfWeek, endTime);
520 setEndRule(endMonth, -endDay, -endDayOfWeek, endTime);
525 * Returns the offset of this time zone from UTC at the given
526 * time. If daylight saving time is in effect at the given time,
527 * the offset value is adjusted with the amount of daylight
530 * @param date the time at which the time zone offset is found
531 * @return the amount of time in milliseconds to add to UTC to get
535 public int getOffset(long date) {
536 return getOffsets(date, null);
540 * @see TimeZone#getOffsets
542 int getOffsets(long date, int[] offsets) {
543 int offset = rawOffset;
547 synchronized (this) {
548 if (cacheStart != 0) {
549 if (date >= cacheStart && date < cacheEnd) {
550 offset += dstSavings;
555 BaseCalendar cal = gcal;
556 // date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
557 // gcal : (BaseCalendar) CalendarSystem.forName("julian");
558 BaseCalendar.Datum cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
559 // Get the year in local time
560 cal.getCalendarDate(date + rawOffset, cdate);
561 int year = cdate.getNormalizedYear();
562 if (year >= startYear) {
563 // Clear time elements for the transition calculations
564 cdate.setTimeOfDay(0, 0, 0, 0);
565 offset = getOffset(cal, cdate, year, date);
569 if (offsets != null) {
570 offsets[0] = rawOffset;
571 offsets[1] = offset - rawOffset;
577 * Returns the difference in milliseconds between local time and
578 * UTC, taking into account both the raw offset and the effect of
579 * daylight saving, for the specified date and time. This method
580 * assumes that the start and end month are distinct. It also
581 * uses a default {@link GregorianCalendar} object as its
582 * underlying calendar, such as for determining leap years. Do
583 * not use the result of this method with a calendar other than a
584 * default <code>GregorianCalendar</code>.
586 * <p><em>Note: In general, clients should use
587 * <code>Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)</code>
588 * instead of calling this method.</em>
590 * @param era The era of the given date.
591 * @param year The year in the given date.
592 * @param month The month in the given date. Month is 0-based. e.g.,
594 * @param day The day-in-month of the given date.
595 * @param dayOfWeek The day-of-week of the given date.
596 * @param millis The milliseconds in day in <em>standard</em> local time.
597 * @return The milliseconds to add to UTC to get local time.
598 * @exception IllegalArgumentException the <code>era</code>,
599 * <code>month</code>, <code>day</code>, <code>dayOfWeek</code>,
600 * or <code>millis</code> parameters are out of range
602 public int getOffset(int era, int year, int month, int day, int dayOfWeek,
605 if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
606 throw new IllegalArgumentException("Illegal era " + era);
610 if (era == GregorianCalendar.BC) {
611 // adjust y with the GregorianCalendar-style year numbering.
615 // If the year isn't representable with the 64-bit long
616 // integer in milliseconds, convert the year to an
617 // equivalent year. This is required to pass some JCK test cases
618 // which are actually useless though because the specified years
619 // can't be supported by the Java time system.
620 if (y >= 292278994) {
622 } else if (y <= -292269054) {
623 // y %= 28 also produces an equivalent year, but positive
624 // year numbers would be convenient to use the UNIX cal
626 y = (int) (long) y % 28;
629 // convert year to its 1-based month value
632 // First, calculate time as a Gregorian date.
633 BaseCalendar cal = gcal;
634 BaseCalendar.Datum cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
635 cdate.setDate(y, m, day);
636 long time = cal.getTime(cdate); // normalize cdate
637 time += millis - rawOffset; // UTC time
639 // If the time value represents a time before the default
640 // Gregorian cutover, recalculate time using the Julian
641 // calendar system. For the Julian calendar system, the
642 // normalized year numbering is ..., -2 (BCE 2), -1 (BCE 1),
643 // 1, 2 ... which is different from the GregorianCalendar
644 // style year numbering (..., -1, 0 (BCE 1), 1, 2, ...).
645 // if (time < GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER) {
646 // cal = (BaseCalendar) CalendarSystem.forName("julian");
647 // cdate = (BaseCalendar.Datum) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
648 // cdate.setNormalizedDate(y, m, day);
649 // time = cal.getTime(cdate) + millis - rawOffset;
652 if ((cdate.getNormalizedYear() != y)
653 || (cdate.getMonth() != m)
654 || (cdate.getDayOfMonth() != day)
655 // The validation should be cdate.getDayOfWeek() ==
656 // dayOfWeek. However, we don't check dayOfWeek for
658 || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
659 || (millis < 0 || millis >= (24*60*60*1000))) {
660 throw new IllegalArgumentException();
663 if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
667 return getOffset(cal, cdate, y, time);
670 private int getOffset(BaseCalendar cal, BaseCalendar.Datum cdate, int year, long time) {
671 synchronized (this) {
672 if (cacheStart != 0) {
673 if (time >= cacheStart && time < cacheEnd) {
674 return rawOffset + dstSavings;
676 if (year == cacheYear) {
682 long start = getStart(cal, cdate, year);
683 long end = getEnd(cal, cdate, year);
684 int offset = rawOffset;
686 if (time >= start && time < end) {
687 offset += dstSavings;
689 synchronized (this) {
696 // TODO: support Gregorian cutover. The previous year
697 // may be in the other calendar system.
698 start = getStart(cal, cdate, year - 1);
700 offset += dstSavings;
702 } else if (time >= start) {
703 // TODO: support Gregorian cutover. The next year
704 // may be in the other calendar system.
705 end = getEnd(cal, cdate, year + 1);
707 offset += dstSavings;
711 synchronized (this) {
712 // The start and end transitions are in multiple years.
713 cacheYear = (long) startYear - 1;
722 private long getStart(BaseCalendar cal, BaseCalendar.Datum cdate, int year) {
723 int time = startTime;
724 if (startTimeMode != UTC_TIME) {
727 return getTransition(cal, cdate, startMode, year, startMonth, startDay,
728 startDayOfWeek, time);
731 private long getEnd(BaseCalendar cal, BaseCalendar.Datum cdate, int year) {
733 if (endTimeMode != UTC_TIME) {
736 if (endTimeMode == WALL_TIME) {
739 return getTransition(cal, cdate, endMode, year, endMonth, endDay,
743 private long getTransition(BaseCalendar cal, BaseCalendar.Datum cdate,
744 int mode, int year, int month, int dayOfMonth,
745 int dayOfWeek, int timeOfDay) {
746 cdate.setNormalizedYear(year);
747 cdate.setMonth(month + 1);
750 cdate.setDayOfMonth(dayOfMonth);
753 case DOW_IN_MONTH_MODE:
754 cdate.setDayOfMonth(1);
755 if (dayOfMonth < 0) {
756 cdate.setDayOfMonth(cal.getMonthLength(cdate));
758 cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(dayOfMonth, dayOfWeek, cdate);
761 case DOW_GE_DOM_MODE:
762 cdate.setDayOfMonth(dayOfMonth);
763 cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(1, dayOfWeek, cdate);
766 case DOW_LE_DOM_MODE:
767 cdate.setDayOfMonth(dayOfMonth);
768 cdate = (BaseCalendar.Datum) cal.getNthDayOfWeek(-1, dayOfWeek, cdate);
771 return cal.getTime(cdate) + timeOfDay;
775 * Gets the GMT offset for this time zone.
776 * @return the GMT offset value in milliseconds
779 public int getRawOffset()
781 // The given date will be taken into account while
782 // we have the historical time zone data in place.
787 * Sets the base time zone offset to GMT.
788 * This is the offset to add to UTC to get local time.
791 public void setRawOffset(int offsetMillis)
793 this.rawOffset = offsetMillis;
797 * Sets the amount of time in milliseconds that the clock is advanced
798 * during daylight saving time.
799 * @param millisSavedDuringDST the number of milliseconds the time is
800 * advanced with respect to standard time when the daylight saving time rules
801 * are in effect. A positive number, typically one hour (3600000).
802 * @see #getDSTSavings
805 public void setDSTSavings(int millisSavedDuringDST) {
806 if (millisSavedDuringDST <= 0) {
807 throw new IllegalArgumentException("Illegal daylight saving value: "
808 + millisSavedDuringDST);
810 dstSavings = millisSavedDuringDST;
814 * Returns the amount of time in milliseconds that the clock is
815 * advanced during daylight saving time.
817 * @return the number of milliseconds the time is advanced with
818 * respect to standard time when the daylight saving rules are in
819 * effect, or 0 (zero) if this time zone doesn't observe daylight
822 * @see #setDSTSavings
825 public int getDSTSavings() {
826 return useDaylight ? dstSavings : 0;
830 * Queries if this time zone uses daylight saving time.
831 * @return true if this time zone uses daylight saving time;
834 public boolean useDaylightTime()
840 * Returns {@code true} if this {@code SimpleTimeZone} observes
841 * Daylight Saving Time. This method is equivalent to {@link
842 * #useDaylightTime()}.
844 * @return {@code true} if this {@code SimpleTimeZone} observes
845 * Daylight Saving Time; {@code false} otherwise.
849 public boolean observesDaylightTime() {
850 return useDaylightTime();
854 * Queries if the given date is in daylight saving time.
855 * @return true if daylight saving time is in effective at the
856 * given date; false otherwise.
858 public boolean inDaylightTime(Date date)
860 return (getOffset(date.getTime()) != rawOffset);
864 * Returns a clone of this <code>SimpleTimeZone</code> instance.
865 * @return a clone of this instance.
867 public Object clone()
869 return super.clone();
873 * Generates the hash code for the SimpleDateFormat object.
874 * @return the hash code for this object
876 public synchronized int hashCode()
878 return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
879 endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
883 * Compares the equality of two <code>SimpleTimeZone</code> objects.
885 * @param obj The <code>SimpleTimeZone</code> object to be compared with.
886 * @return True if the given <code>obj</code> is the same as this
887 * <code>SimpleTimeZone</code> object; false otherwise.
889 public boolean equals(Object obj)
894 if (!(obj instanceof SimpleTimeZone)) {
898 SimpleTimeZone that = (SimpleTimeZone) obj;
900 return getID().equals(that.getID()) &&
905 * Returns <code>true</code> if this zone has the same rules and offset as another zone.
906 * @param other the TimeZone object to be compared with
907 * @return <code>true</code> if the given zone is a SimpleTimeZone and has the
908 * same rules and offset as this one
911 public boolean hasSameRules(TimeZone other) {
915 if (!(other instanceof SimpleTimeZone)) {
918 SimpleTimeZone that = (SimpleTimeZone) other;
919 return rawOffset == that.rawOffset &&
920 useDaylight == that.useDaylight &&
922 // Only check rules if using DST
923 || (dstSavings == that.dstSavings &&
924 startMode == that.startMode &&
925 startMonth == that.startMonth &&
926 startDay == that.startDay &&
927 startDayOfWeek == that.startDayOfWeek &&
928 startTime == that.startTime &&
929 startTimeMode == that.startTimeMode &&
930 endMode == that.endMode &&
931 endMonth == that.endMonth &&
932 endDay == that.endDay &&
933 endDayOfWeek == that.endDayOfWeek &&
934 endTime == that.endTime &&
935 endTimeMode == that.endTimeMode &&
936 startYear == that.startYear));
940 * Returns a string representation of this time zone.
941 * @return a string representation of this time zone.
943 public String toString() {
944 return getClass().getName() +
946 ",offset=" + rawOffset +
947 ",dstSavings=" + dstSavings +
948 ",useDaylight=" + useDaylight +
949 ",startYear=" + startYear +
950 ",startMode=" + startMode +
951 ",startMonth=" + startMonth +
952 ",startDay=" + startDay +
953 ",startDayOfWeek=" + startDayOfWeek +
954 ",startTime=" + startTime +
955 ",startTimeMode=" + startTimeMode +
956 ",endMode=" + endMode +
957 ",endMonth=" + endMonth +
958 ",endDay=" + endDay +
959 ",endDayOfWeek=" + endDayOfWeek +
960 ",endTime=" + endTime +
961 ",endTimeMode=" + endTimeMode + ']';
964 // =======================privates===============================
967 * The month in which daylight saving time starts. This value must be
968 * between <code>Calendar.JANUARY</code> and
969 * <code>Calendar.DECEMBER</code> inclusive. This value must not equal
970 * <code>endMonth</code>.
971 * <p>If <code>useDaylight</code> is false, this value is ignored.
974 private int startMonth;
977 * This field has two possible interpretations:
979 * <dt><code>startMode == DOW_IN_MONTH</code></dt>
981 * <code>startDay</code> indicates the day of the month of
982 * <code>startMonth</code> on which daylight
983 * saving time starts, from 1 to 28, 30, or 31, depending on the
984 * <code>startMonth</code>.
986 * <dt><code>startMode != DOW_IN_MONTH</code></dt>
988 * <code>startDay</code> indicates which <code>startDayOfWeek</code> in the
989 * month <code>startMonth</code> daylight
990 * saving time starts on. For example, a value of +1 and a
991 * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
992 * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the
993 * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
996 * <p>If <code>useDaylight</code> is false, this value is ignored.
999 private int startDay;
1002 * The day of the week on which daylight saving time starts. This value
1003 * must be between <code>Calendar.SUNDAY</code> and
1004 * <code>Calendar.SATURDAY</code> inclusive.
1005 * <p>If <code>useDaylight</code> is false or
1006 * <code>startMode == DAY_OF_MONTH</code>, this value is ignored.
1009 private int startDayOfWeek;
1012 * The time in milliseconds after midnight at which daylight saving
1013 * time starts. This value is expressed as wall time, standard time,
1014 * or UTC time, depending on the setting of <code>startTimeMode</code>.
1015 * <p>If <code>useDaylight</code> is false, this value is ignored.
1018 private int startTime;
1021 * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
1025 private int startTimeMode;
1028 * The month in which daylight saving time ends. This value must be
1029 * between <code>Calendar.JANUARY</code> and
1030 * <code>Calendar.UNDECIMBER</code>. This value must not equal
1031 * <code>startMonth</code>.
1032 * <p>If <code>useDaylight</code> is false, this value is ignored.
1035 private int endMonth;
1038 * This field has two possible interpretations:
1040 * <dt><code>endMode == DOW_IN_MONTH</code></dt>
1042 * <code>endDay</code> indicates the day of the month of
1043 * <code>endMonth</code> on which daylight
1044 * saving time ends, from 1 to 28, 30, or 31, depending on the
1045 * <code>endMonth</code>.
1047 * <dt><code>endMode != DOW_IN_MONTH</code></dt>
1049 * <code>endDay</code> indicates which <code>endDayOfWeek</code> in th
1050 * month <code>endMonth</code> daylight
1051 * saving time ends on. For example, a value of +1 and a
1052 * <code>endDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
1053 * first Sunday of <code>endMonth</code>. Likewise, +2 would indicate the
1054 * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
1057 * <p>If <code>useDaylight</code> is false, this value is ignored.
1063 * The day of the week on which daylight saving time ends. This value
1064 * must be between <code>Calendar.SUNDAY</code> and
1065 * <code>Calendar.SATURDAY</code> inclusive.
1066 * <p>If <code>useDaylight</code> is false or
1067 * <code>endMode == DAY_OF_MONTH</code>, this value is ignored.
1070 private int endDayOfWeek;
1073 * The time in milliseconds after midnight at which daylight saving
1074 * time ends. This value is expressed as wall time, standard time,
1075 * or UTC time, depending on the setting of <code>endTimeMode</code>.
1076 * <p>If <code>useDaylight</code> is false, this value is ignored.
1079 private int endTime;
1082 * The format of endTime, either <code>WALL_TIME</code>,
1083 * <code>STANDARD_TIME</code>, or <code>UTC_TIME</code>.
1087 private int endTimeMode;
1090 * The year in which daylight saving time is first observed. This is an {@link GregorianCalendar#AD AD}
1091 * value. If this value is less than 1 then daylight saving time is observed
1092 * for all <code>AD</code> years.
1093 * <p>If <code>useDaylight</code> is false, this value is ignored.
1096 private int startYear;
1099 * The offset in milliseconds between this zone and GMT. Negative offsets
1100 * are to the west of Greenwich. To obtain local <em>standard</em> time,
1101 * add the offset to GMT time. To obtain local wall time it may also be
1102 * necessary to add <code>dstSavings</code>.
1105 private int rawOffset;
1108 * A boolean value which is true if and only if this zone uses daylight
1109 * saving time. If this value is false, several other fields are ignored.
1112 private boolean useDaylight=false; // indicate if this time zone uses DST
1114 private static final int millisPerHour = 60*60*1000;
1115 private static final int millisPerDay = 24*millisPerHour;
1118 * This field was serialized in JDK 1.1, so we have to keep it that way
1119 * to maintain serialization compatibility. However, there's no need to
1120 * recreate the array each time we create a new time zone.
1121 * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
1122 * 31, 31, 30, 31, 30, 31}. This is ignored as of the Java 2 platform v1.2, however, it must
1123 * be streamed out for compatibility with JDK 1.1.
1125 private final byte monthLength[] = staticMonthLength;
1126 private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
1127 private final static byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
1130 * Variables specifying the mode of the start rule. Takes the following
1133 * <dt><code>DOM_MODE</code></dt>
1135 * Exact day of week; e.g., March 1.
1137 * <dt><code>DOW_IN_MONTH_MODE</code></dt>
1139 * Day of week in month; e.g., last Sunday in March.
1141 * <dt><code>DOW_GE_DOM_MODE</code></dt>
1143 * Day of week after day of month; e.g., Sunday on or after March 15.
1145 * <dt><code>DOW_LE_DOM_MODE</code></dt>
1147 * Day of week before day of month; e.g., Sunday on or before March 15.
1150 * The setting of this field affects the interpretation of the
1151 * <code>startDay</code> field.
1152 * <p>If <code>useDaylight</code> is false, this value is ignored.
1156 private int startMode;
1159 * Variables specifying the mode of the end rule. Takes the following
1162 * <dt><code>DOM_MODE</code></dt>
1164 * Exact day of week; e.g., March 1.
1166 * <dt><code>DOW_IN_MONTH_MODE</code></dt>
1168 * Day of week in month; e.g., last Sunday in March.
1170 * <dt><code>DOW_GE_DOM_MODE</code></dt>
1172 * Day of week after day of month; e.g., Sunday on or after March 15.
1174 * <dt><code>DOW_LE_DOM_MODE</code></dt>
1176 * Day of week before day of month; e.g., Sunday on or before March 15.
1179 * The setting of this field affects the interpretation of the
1180 * <code>endDay</code> field.
1181 * <p>If <code>useDaylight</code> is false, this value is ignored.
1185 private int endMode;
1188 * A positive value indicating the amount of time saved during DST in
1190 * Typically one hour (3600000); sometimes 30 minutes (1800000).
1191 * <p>If <code>useDaylight</code> is false, this value is ignored.
1195 private int dstSavings;
1197 private static final BaseCalendar gcal = new BaseCalendar();//CalendarSystem.getGregorianCalendar();
1200 * Cache values representing a single period of daylight saving
1201 * time. When the cache values are valid, cacheStart is the start
1202 * time (inclusive) of daylight saving time and cacheEnd is the
1203 * end time (exclusive).
1205 * cacheYear has a year value if both cacheStart and cacheEnd are
1206 * in the same year. cacheYear is set to startYear - 1 if
1207 * cacheStart and cacheEnd are in different years. cacheStart is 0
1208 * if the cache values are void. cacheYear is a long to support
1209 * Integer.MIN_VALUE - 1 (JCK requirement).
1211 private transient long cacheYear;
1212 private transient long cacheStart;
1213 private transient long cacheEnd;
1216 * Constants specifying values of startMode and endMode.
1218 private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
1219 private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
1220 private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
1221 private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
1224 * Constant for a mode of start or end time specified as wall clock
1225 * time. Wall clock time is standard time for the onset rule, and
1226 * daylight time for the end rule.
1229 public static final int WALL_TIME = 0; // Zero for backward compatibility
1232 * Constant for a mode of start or end time specified as standard time.
1235 public static final int STANDARD_TIME = 1;
1238 * Constant for a mode of start or end time specified as UTC. European
1239 * Union rules are specified as UTC time, for example.
1242 public static final int UTC_TIME = 2;
1244 // Proclaim compatibility with 1.1
1245 static final long serialVersionUID = -403250971215465050L;
1247 // the internal serial version which says which version was written
1248 // - 0 (default) for version up to JDK 1.1.3
1249 // - 1 for version from JDK 1.1.4, which includes 3 new fields
1250 // - 2 for JDK 1.3, which includes 2 new fields
1251 static final int currentSerialVersion = 2;
1254 * The version of the serialized data on the stream. Possible values:
1256 * <dt><b>0</b> or not present on stream</dt>
1258 * JDK 1.1.3 or earlier.
1262 * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
1263 * <code>endMode</code>, and <code>dstSavings</code>.
1267 * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code>
1268 * and <code>endTimeMode</code>.
1271 * When streaming out this class, the most recent format
1272 * and the highest allowable <code>serialVersionOnStream</code>
1277 private int serialVersionOnStream = currentSerialVersion;
1279 synchronized private void invalidateCache() {
1280 cacheYear = startYear - 1;
1281 cacheStart = cacheEnd = 0;
1284 //----------------------------------------------------------------------
1285 // Rule representation
1287 // We represent the following flavors of rules:
1288 // 5 the fifth of the month
1289 // lastSun the last Sunday in the month
1290 // lastMon the last Monday in the month
1291 // Sun>=8 first Sunday on or after the eighth
1292 // Sun<=25 last Sunday on or before the 25th
1293 // This is further complicated by the fact that we need to remain
1294 // backward compatible with the 1.1 FCS. Finally, we need to minimize
1295 // API changes. In order to satisfy these requirements, we support
1296 // three representation systems, and we translate between them.
1298 // INTERNAL REPRESENTATION
1299 // This is the format SimpleTimeZone objects take after construction or
1300 // streaming in is complete. Rules are represented directly, using an
1301 // unencoded format. We will discuss the start rule only below; the end
1302 // rule is analogous.
1303 // startMode Takes on enumerated values DAY_OF_MONTH,
1304 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
1305 // startDay The day of the month, or for DOW_IN_MONTH mode, a
1306 // value indicating which DOW, such as +1 for first,
1307 // +2 for second, -1 for last, etc.
1308 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
1310 // ENCODED REPRESENTATION
1311 // This is the format accepted by the constructor and by setStartRule()
1312 // and setEndRule(). It uses various combinations of positive, negative,
1313 // and zero values to encode the different rules. This representation
1314 // allows us to specify all the different rule flavors without altering
1316 // MODE startMonth startDay startDayOfWeek
1317 // DOW_IN_MONTH_MODE >=0 !=0 >0
1318 // DOM_MODE >=0 >0 ==0
1319 // DOW_GE_DOM_MODE >=0 >0 <0
1320 // DOW_LE_DOM_MODE >=0 <0 <0
1321 // (no DST) don't care ==0 don't care
1323 // STREAMED REPRESENTATION
1324 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
1325 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
1326 // flag useDaylight. When we stream an object out, we translate into an
1327 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
1328 // and used by 1.1 code. Following that, we write out the full
1329 // representation separately so that contemporary code can recognize and
1330 // parse it. The full representation is written in a "packed" format,
1331 // consisting of a version number, a length, and an array of bytes. Future
1332 // versions of this class may specify different versions. If they wish to
1333 // include additional data, they should do so by storing them after the
1334 // packed representation below.
1335 //----------------------------------------------------------------------
1338 * Given a set of encoded rules in startDay and startDayOfMonth, decode
1339 * them and set the startMode appropriately. Do the same for endDay and
1340 * endDayOfMonth. Upon entry, the day of week variables may be zero or
1341 * negative, in order to indicate special modes. The day of month
1342 * variables may also be negative. Upon exit, the mode variables will be
1343 * set, and the day of week and day of month variables will be positive.
1344 * This method also recognizes a startDay or endDay of zero as indicating
1347 private void decodeRules()
1354 * Decode the start rule and validate the parameters. The parameters are
1355 * expected to be in encoded form, which represents the various rule modes
1356 * by negating or zeroing certain values. Representation formats are:
1359 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
1360 * ------------ ----- -------- -------- ----------
1361 * month 0..11 same same same don't care
1362 * day -5..5 1..31 1..31 -1..-31 0
1363 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
1364 * time 0..ONEDAY same same same don't care
1366 * The range for month does not include UNDECIMBER since this class is
1367 * really specific to GregorianCalendar, which does not use that month.
1368 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
1369 * end rule is an exclusive limit point. That is, the range of times that
1370 * are in DST include those >= the start and < the end. For this reason,
1371 * it should be possible to specify an end of ONEDAY in order to include the
1372 * entire day. Although this is equivalent to time 0 of the following day,
1373 * it's not always possible to specify that, for example, on December 31.
1374 * While arguably the start range should still be 0..ONEDAY-1, we keep
1375 * the start and end ranges the same for consistency.
1377 private void decodeStartRule() {
1378 useDaylight = (startDay != 0) && (endDay != 0);
1379 if (startDay != 0) {
1380 if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
1381 throw new IllegalArgumentException(
1382 "Illegal start month " + startMonth);
1384 if (startTime < 0 || startTime > millisPerDay) {
1385 throw new IllegalArgumentException(
1386 "Illegal start time " + startTime);
1388 if (startDayOfWeek == 0) {
1389 startMode = DOM_MODE;
1391 if (startDayOfWeek > 0) {
1392 startMode = DOW_IN_MONTH_MODE;
1394 startDayOfWeek = -startDayOfWeek;
1396 startMode = DOW_GE_DOM_MODE;
1398 startDay = -startDay;
1399 startMode = DOW_LE_DOM_MODE;
1402 if (startDayOfWeek > Calendar.SATURDAY) {
1403 throw new IllegalArgumentException(
1404 "Illegal start day of week " + startDayOfWeek);
1407 if (startMode == DOW_IN_MONTH_MODE) {
1408 if (startDay < -5 || startDay > 5) {
1409 throw new IllegalArgumentException(
1410 "Illegal start day of week in month " + startDay);
1412 } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
1413 throw new IllegalArgumentException(
1414 "Illegal start day " + startDay);
1420 * Decode the end rule and validate the parameters. This method is exactly
1421 * analogous to decodeStartRule().
1422 * @see decodeStartRule
1424 private void decodeEndRule() {
1425 useDaylight = (startDay != 0) && (endDay != 0);
1427 if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
1428 throw new IllegalArgumentException(
1429 "Illegal end month " + endMonth);
1431 if (endTime < 0 || endTime > millisPerDay) {
1432 throw new IllegalArgumentException(
1433 "Illegal end time " + endTime);
1435 if (endDayOfWeek == 0) {
1438 if (endDayOfWeek > 0) {
1439 endMode = DOW_IN_MONTH_MODE;
1441 endDayOfWeek = -endDayOfWeek;
1443 endMode = DOW_GE_DOM_MODE;
1446 endMode = DOW_LE_DOM_MODE;
1449 if (endDayOfWeek > Calendar.SATURDAY) {
1450 throw new IllegalArgumentException(
1451 "Illegal end day of week " + endDayOfWeek);
1454 if (endMode == DOW_IN_MONTH_MODE) {
1455 if (endDay < -5 || endDay > 5) {
1456 throw new IllegalArgumentException(
1457 "Illegal end day of week in month " + endDay);
1459 } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) {
1460 throw new IllegalArgumentException(
1461 "Illegal end day " + endDay);
1467 * Make rules compatible to 1.1 FCS code. Since 1.1 FCS code only understands
1468 * day-of-week-in-month rules, we must modify other modes of rules to their
1469 * approximate equivalent in 1.1 FCS terms. This method is used when streaming
1470 * out objects of this class. After it is called, the rules will be modified,
1471 * with a possible loss of information. startMode and endMode will NOT be
1472 * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
1473 * since the rule modification is only intended to be temporary.
1475 private void makeRulesCompatible()
1477 switch (startMode) {
1479 startDay = 1 + (startDay / 7);
1480 startDayOfWeek = Calendar.SUNDAY;
1483 case DOW_GE_DOM_MODE:
1484 // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1485 // that is, Sun>=1 == firstSun.
1486 if (startDay != 1) {
1487 startDay = 1 + (startDay / 7);
1491 case DOW_LE_DOM_MODE:
1492 if (startDay >= 30) {
1495 startDay = 1 + (startDay / 7);
1502 endDay = 1 + (endDay / 7);
1503 endDayOfWeek = Calendar.SUNDAY;
1506 case DOW_GE_DOM_MODE:
1507 // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
1508 // that is, Sun>=1 == firstSun.
1510 endDay = 1 + (endDay / 7);
1514 case DOW_LE_DOM_MODE:
1518 endDay = 1 + (endDay / 7);
1524 * Adjust the start and end times to wall time. This works perfectly
1525 * well unless it pushes into the next or previous day. If that
1526 * happens, we attempt to adjust the day rule somewhat crudely. The day
1527 * rules have been forced into DOW_IN_MONTH mode already, so we change
1528 * the day of week to move forward or back by a day. It's possible to
1529 * make a more refined adjustment of the original rules first, but in
1530 * most cases this extra effort will go to waste once we adjust the day
1533 switch (startTimeMode) {
1535 startTime += rawOffset;
1538 while (startTime < 0) {
1539 startTime += millisPerDay;
1540 startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day
1542 while (startTime >= millisPerDay) {
1543 startTime -= millisPerDay;
1544 startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day
1547 switch (endTimeMode) {
1549 endTime += rawOffset + dstSavings;
1552 endTime += dstSavings;
1554 while (endTime < 0) {
1555 endTime += millisPerDay;
1556 endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day
1558 while (endTime >= millisPerDay) {
1559 endTime -= millisPerDay;
1560 endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day
1565 * Pack the start and end rules into an array of bytes. Only pack
1566 * data which is not preserved by makeRulesCompatible.
1568 private byte[] packRules()
1570 byte[] rules = new byte[6];
1571 rules[0] = (byte)startDay;
1572 rules[1] = (byte)startDayOfWeek;
1573 rules[2] = (byte)endDay;
1574 rules[3] = (byte)endDayOfWeek;
1576 // As of serial version 2, include time modes
1577 rules[4] = (byte)startTimeMode;
1578 rules[5] = (byte)endTimeMode;
1584 * Given an array of bytes produced by packRules, interpret them
1585 * as the start and end rules.
1587 private void unpackRules(byte[] rules)
1589 startDay = rules[0];
1590 startDayOfWeek = rules[1];
1592 endDayOfWeek = rules[3];
1594 // As of serial version 2, include time modes
1595 if (rules.length >= 6) {
1596 startTimeMode = rules[4];
1597 endTimeMode = rules[5];
1602 * Pack the start and end times into an array of bytes. This is required
1603 * as of serial version 2.
1605 private int[] packTimes() {
1606 int[] times = new int[2];
1607 times[0] = startTime;
1613 * Unpack the start and end times from an array of bytes. This is required
1614 * as of serial version 2.
1616 private void unpackTimes(int[] times) {
1617 startTime = times[0];
1622 * Save the state of this object to a stream (i.e., serialize it).
1624 * @serialData We write out two formats, a JDK 1.1 compatible format, using
1625 * <code>DOW_IN_MONTH_MODE</code> rules, in the required section, followed
1626 * by the full rules, in packed format, in the optional section. The
1627 * optional section will be ignored by JDK 1.1 code upon stream in.
1628 * <p> Contents of the optional section: The length of a byte array is
1629 * emitted (int); this is 4 as of this release. The byte array of the given
1630 * length is emitted. The contents of the byte array are the true values of
1631 * the fields <code>startDay</code>, <code>startDayOfWeek</code>,
1632 * <code>endDay</code>, and <code>endDayOfWeek</code>. The values of these
1633 * fields in the required section are approximate values suited to the rule
1634 * mode <code>DOW_IN_MONTH_MODE</code>, which is the only mode recognized by
1637 private void writeObject(ObjectOutputStream stream)
1640 // Construct a binary rule
1641 byte[] rules = packRules();
1642 int[] times = packTimes();
1644 // Convert to 1.1 FCS rules. This step may cause us to lose information.
1645 makeRulesCompatible();
1647 // Write out the 1.1 FCS rules
1648 stream.defaultWriteObject();
1650 // Write out the binary rules in the optional data area of the stream.
1651 stream.writeInt(rules.length);
1652 stream.write(rules);
1653 stream.writeObject(times);
1655 // Recover the original rules. This recovers the information lost
1656 // by makeRulesCompatible.
1662 * Reconstitute this object from a stream (i.e., deserialize it).
1664 * We handle both JDK 1.1
1665 * binary formats and full formats with a packed byte array.
1667 private void readObject(ObjectInputStream stream)
1668 throws IOException, ClassNotFoundException
1670 stream.defaultReadObject();
1672 if (serialVersionOnStream < 1) {
1673 // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
1674 // startDayOfWeek and endDayOfWeek were usually uninitialized. We can't do
1675 // too much, so we assume SUNDAY, which actually works most of the time.
1676 if (startDayOfWeek == 0) {
1677 startDayOfWeek = Calendar.SUNDAY;
1679 if (endDayOfWeek == 0) {
1680 endDayOfWeek = Calendar.SUNDAY;
1683 // The variables dstSavings, startMode, and endMode are post-1.1, so they
1684 // won't be present if we're reading from a 1.1 stream. Fix them up.
1685 startMode = endMode = DOW_IN_MONTH_MODE;
1686 dstSavings = millisPerHour;
1688 // For 1.1.4, in addition to the 3 new instance variables, we also
1689 // store the actual rules (which have not be made compatible with 1.1)
1690 // in the optional area. Read them in here and parse them.
1691 int length = stream.readInt();
1692 byte[] rules = new byte[length];
1693 stream.readFully(rules);
1697 if (serialVersionOnStream >= 2) {
1698 int[] times = (int[]) stream.readObject();
1702 serialVersionOnStream = currentSerialVersion;
1705 static final class GregorianCalendar {
1706 public static final int BC = 0;
1709 * Value of the {@link #ERA} field indicating the period before the
1710 * common era, the same value as {@link #BC}.
1714 static final int BCE = 0;
1717 * Value of the <code>ERA</code> field indicating the common era (Anno
1718 * Domini), also known as CE. The sequence of years at the transition
1719 * from <code>BC</code> to <code>AD</code> is ..., 2 BC, 1 BC, 1 AD, 2
1724 public static final int AD = 1;
1726 // The default value of gregorianCutover.
1727 static final long DEFAULT_GREGORIAN_CUTOVER = -12219292800000L;
1729 * Value of the {@link #ERA} field indicating
1730 * the common era, the same value as {@link #AD}.
1734 static final int CE = 1;