• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.util;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.ObjectOutputStream;
23 import java.io.ObjectStreamField;
24 
25 /**
26  * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone}
27  * that represents a time zone for use with a Gregorian calendar. This class
28  * does not handle historical changes.
29  * <p>
30  * Use a negative value for {@code dayOfWeekInMonth} to indicate that
31  * {@code SimpleTimeZone} should count from the end of the month
32  * backwards. For example, Daylight Savings Time ends at the last
33  * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
34  *
35  * @see Calendar
36  * @see GregorianCalendar
37  * @see TimeZone
38  */
39 public class SimpleTimeZone extends TimeZone {
40 
41     private static final long serialVersionUID = -403250971215465050L;
42 
43     private int rawOffset;
44 
45     private int startYear, startMonth, startDay, startDayOfWeek, startTime;
46 
47     private int endMonth, endDay, endDayOfWeek, endTime;
48 
49     private int startMode, endMode;
50 
51     private static final int DOM_MODE = 1, DOW_IN_MONTH_MODE = 2,
52             DOW_GE_DOM_MODE = 3, DOW_LE_DOM_MODE = 4;
53 
54     /**
55      * The constant for representing a start or end time in GMT time mode.
56      */
57     public static final int UTC_TIME = 2;
58 
59     /**
60      * The constant for representing a start or end time in standard local time mode,
61      * based on timezone's raw offset from GMT; does not include Daylight
62      * savings.
63      */
64     public static final int STANDARD_TIME = 1;
65 
66     /**
67      * The constant for representing a start or end time in local wall clock time
68      * mode, based on timezone's adjusted offset from GMT; includes
69      * Daylight savings.
70      */
71     public static final int WALL_TIME = 0;
72 
73     private boolean useDaylight;
74 
75     private int dstSavings = 3600000;
76 
77     /**
78      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT
79      * and time zone ID. Timezone IDs can be obtained from
80      * {@code TimeZone.getAvailableIDs}. Normally you should use {@code TimeZone.getDefault} to
81      * construct a {@code TimeZone}.
82      *
83      * @param offset
84      *            the given base time zone offset to GMT.
85      * @param name
86      *            the time zone ID which is obtained from
87      *            {@code TimeZone.getAvailableIDs}.
88      */
SimpleTimeZone(int offset, final String name)89     public SimpleTimeZone(int offset, final String name) {
90         setID(name);
91         rawOffset = offset;
92     }
93 
94     /**
95      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT,
96      * time zone ID, and times to start and end the daylight savings time. Timezone IDs can
97      * be obtained from {@code TimeZone.getAvailableIDs}. Normally you should use
98      * {@code TimeZone.getDefault} to create a {@code TimeZone}. For a time zone that does not
99      * use daylight saving time, do not use this constructor; instead you should
100      * use {@code SimpleTimeZone(rawOffset, ID)}.
101      * <p>
102      * By default, this constructor specifies day-of-week-in-month rules. That
103      * is, if the {@code startDay} is 1, and the {@code startDayOfWeek} is {@code SUNDAY}, then this
104      * indicates the first Sunday in the {@code startMonth}. A {@code startDay} of -1 likewise
105      * indicates the last Sunday. However, by using negative or zero values for
106      * certain parameters, other types of rules can be specified.
107      * <p>
108      * Day of month: To specify an exact day of the month, such as March 1, set
109      * {@code startDayOfWeek} to zero.
110      * <p>
111      * Day of week after day of month: To specify the first day of the week
112      * occurring on or after an exact day of the month, make the day of the week
113      * negative. For example, if {@code startDay} is 5 and {@code startDayOfWeek} is {@code -MONDAY},
114      * this indicates the first Monday on or after the 5th day of the
115      * {@code startMonth}.
116      * <p>
117      * Day of week before day of month: To specify the last day of the week
118      * occurring on or before an exact day of the month, make the day of the
119      * week and the day of the month negative. For example, if {@code startDay} is {@code -21}
120      * and {@code startDayOfWeek} is {@code -WEDNESDAY}, this indicates the last Wednesday on or
121      * before the 21st of the {@code startMonth}.
122      * <p>
123      * The above examples refer to the {@code startMonth}, {@code startDay}, and {@code startDayOfWeek};
124      * the same applies for the {@code endMonth}, {@code endDay}, and {@code endDayOfWeek}.
125      * <p>
126      * The daylight savings time difference is set to the default value: one hour.
127      *
128      * @param offset
129      *            the given base time zone offset to GMT.
130      * @param name
131      *            the time zone ID which is obtained from
132      *            {@code TimeZone.getAvailableIDs}.
133      * @param startMonth
134      *            the daylight savings starting month. The month indexing is 0-based. eg, 0
135      *            for January.
136      * @param startDay
137      *            the daylight savings starting day-of-week-in-month. Please see
138      *            the member description for an example.
139      * @param startDayOfWeek
140      *            the daylight savings starting day-of-week. Please see the
141      *            member description for an example.
142      * @param startTime
143      *            the daylight savings starting time in local wall time, which
144      *            is standard time in this case. Please see the member
145      *            description for an example.
146      * @param endMonth
147      *            the daylight savings ending month. The month indexing is 0-based. eg, 0 for
148      *            January.
149      * @param endDay
150      *            the daylight savings ending day-of-week-in-month. Please see
151      *            the member description for an example.
152      * @param endDayOfWeek
153      *            the daylight savings ending day-of-week. Please see the member
154      *            description for an example.
155      * @param endTime
156      *            the daylight savings ending time in local wall time, which is
157      *            daylight time in this case. Please see the member description
158      *            for an example.
159      * @throws IllegalArgumentException
160      *             if the month, day, dayOfWeek, or time parameters are out of
161      *             range for the start or end rule.
162      */
SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)163     public SimpleTimeZone(int offset, String name, int startMonth,
164             int startDay, int startDayOfWeek, int startTime, int endMonth,
165             int endDay, int endDayOfWeek, int endTime) {
166         this(offset, name, startMonth, startDay, startDayOfWeek, startTime,
167                 endMonth, endDay, endDayOfWeek, endTime, 3600000);
168     }
169 
170     /**
171      * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT,
172      * time zone ID, times to start and end the daylight savings time, and
173      * the daylight savings time difference in milliseconds.
174      *
175      * @param offset
176      *            the given base time zone offset to GMT.
177      * @param name
178      *            the time zone ID which is obtained from
179      *            {@code TimeZone.getAvailableIDs}.
180      * @param startMonth
181      *            the daylight savings starting month. Month is 0-based. eg, 0
182      *            for January.
183      * @param startDay
184      *            the daylight savings starting day-of-week-in-month. Please see
185      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
186      * @param startDayOfWeek
187      *            the daylight savings starting day-of-week. Please see the
188      *            description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
189      * @param startTime
190      *            The daylight savings starting time in local wall time, which
191      *            is standard time in this case. Please see the description of
192      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
193      * @param endMonth
194      *            the daylight savings ending month. Month is 0-based. eg, 0 for
195      *            January.
196      * @param endDay
197      *            the daylight savings ending day-of-week-in-month. Please see
198      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
199      * @param endDayOfWeek
200      *            the daylight savings ending day-of-week. Please see the description of
201      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
202      * @param endTime
203      *            the daylight savings ending time in local wall time, which is
204      *            daylight time in this case. Please see the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)}
205      *            for an example.
206      * @param daylightSavings
207      *            the daylight savings time difference in milliseconds.
208      * @throws IllegalArgumentException
209      *                if the month, day, dayOfWeek, or time parameters are out of
210      *                range for the start or end rule.
211      */
SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int daylightSavings)212     public SimpleTimeZone(int offset, String name, int startMonth,
213             int startDay, int startDayOfWeek, int startTime, int endMonth,
214             int endDay, int endDayOfWeek, int endTime, int daylightSavings) {
215         this(offset, name);
216         if (daylightSavings <= 0) {
217             throw new IllegalArgumentException("Invalid daylightSavings: " + daylightSavings);
218         }
219         dstSavings = daylightSavings;
220         // TODO: do we need to set useDaylight is dstSavings != 0?
221 
222         setStartRule(startMonth, startDay, startDayOfWeek, startTime);
223         setEndRule(endMonth, endDay, endDayOfWeek, endTime);
224     }
225 
226     /**
227      * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT,
228      * time zone ID, times to start and end the daylight savings time including a
229      * mode specifier, the daylight savings time difference in milliseconds.
230      * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or
231      * {@link #UTC_TIME}.
232      *
233      * @param offset
234      *            the given base time zone offset to GMT.
235      * @param name
236      *            the time zone ID which is obtained from
237      *            {@code TimeZone.getAvailableIDs}.
238      * @param startMonth
239      *            the daylight savings starting month. The month indexing is 0-based. eg, 0
240      *            for January.
241      * @param startDay
242      *            the daylight savings starting day-of-week-in-month. Please see
243      *            the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
244      * @param startDayOfWeek
245      *            the daylight savings starting day-of-week. Please see the
246      *            description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
247      * @param startTime
248      *            the time of day in milliseconds on which daylight savings
249      *            time starts, based on the {@code startTimeMode}.
250      * @param startTimeMode
251      *            the mode (UTC, standard, or wall time) of the start time
252      *            value.
253      * @param endDay
254      *            the day of the week on which daylight savings time ends.
255      * @param endMonth
256      *            the daylight savings ending month. The month indexing is 0-based. eg, 0 for
257      *            January.
258      * @param endDayOfWeek
259      *            the daylight savings ending day-of-week. Please see the description of
260      *            {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example.
261      * @param endTime
262      *            the time of day in milliseconds on which daylight savings
263      *            time ends, based on the {@code endTimeMode}.
264      * @param endTimeMode
265      *            the mode (UTC, standard, or wall time) of the end time value.
266      * @param daylightSavings
267      *            the daylight savings time difference in milliseconds.
268      * @throws IllegalArgumentException
269      *             if the month, day, dayOfWeek, or time parameters are out of
270      *             range for the start or end rule.
271      */
SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, int endMonth, int endDay, int endDayOfWeek, int endTime, int endTimeMode, int daylightSavings)272     public SimpleTimeZone(int offset, String name, int startMonth,
273             int startDay, int startDayOfWeek, int startTime, int startTimeMode,
274             int endMonth, int endDay, int endDayOfWeek, int endTime,
275             int endTimeMode, int daylightSavings) {
276 
277         this(offset, name, startMonth, startDay, startDayOfWeek, startTime,
278                 endMonth, endDay, endDayOfWeek, endTime, daylightSavings);
279         startMode = startTimeMode;
280         endMode = endTimeMode;
281     }
282 
283     /**
284      * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight
285      * savings time rules as this SimpleTimeZone.
286      *
287      * @return a shallow copy of this {@code SimpleTimeZone}.
288      * @see java.lang.Cloneable
289      */
290     @Override
clone()291     public Object clone() {
292         SimpleTimeZone zone = (SimpleTimeZone) super.clone();
293         return zone;
294     }
295 
296     /**
297      * Compares the specified object to this {@code SimpleTimeZone} and returns whether they
298      * are equal. The object must be an instance of {@code SimpleTimeZone} and have the
299      * same internal data.
300      *
301      * @param object
302      *            the object to compare with this object.
303      * @return {@code true} if the specified object is equal to this
304      *         {@code SimpleTimeZone}, {@code false} otherwise.
305      * @see #hashCode
306      */
307     @Override
equals(Object object)308     public boolean equals(Object object) {
309         if (!(object instanceof SimpleTimeZone)) {
310             return false;
311         }
312         SimpleTimeZone tz = (SimpleTimeZone) object;
313         return getID().equals(tz.getID())
314                 && rawOffset == tz.rawOffset
315                 && useDaylight == tz.useDaylight
316                 && (!useDaylight || (startYear == tz.startYear
317                         && startMonth == tz.startMonth
318                         && startDay == tz.startDay && startMode == tz.startMode
319                         && startDayOfWeek == tz.startDayOfWeek
320                         && startTime == tz.startTime && endMonth == tz.endMonth
321                         && endDay == tz.endDay
322                         && endDayOfWeek == tz.endDayOfWeek
323                         && endTime == tz.endTime && endMode == tz.endMode && dstSavings == tz.dstSavings));
324     }
325 
326     @Override
getDSTSavings()327     public int getDSTSavings() {
328         if (!useDaylight) {
329             return 0;
330         }
331         return dstSavings;
332     }
333 
334     @Override
getOffset(int era, int year, int month, int day, int dayOfWeek, int time)335     public int getOffset(int era, int year, int month, int day, int dayOfWeek, int time) {
336         if (era != GregorianCalendar.BC && era != GregorianCalendar.AD) {
337             throw new IllegalArgumentException("Invalid era: " + era);
338         }
339         checkRange(month, dayOfWeek, time);
340         if (month != Calendar.FEBRUARY || day != 29 || !isLeapYear(year)) {
341             checkDay(month, day);
342         }
343 
344         if (!useDaylightTime() || era != GregorianCalendar.AD || year < startYear) {
345             return rawOffset;
346         }
347         if (endMonth < startMonth) {
348             if (month > endMonth && month < startMonth) {
349                 return rawOffset;
350             }
351         } else {
352             if (month < startMonth || month > endMonth) {
353                 return rawOffset;
354             }
355         }
356 
357         int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day);
358         if (month == startMonth) {
359             switch (startMode) {
360                 case DOM_MODE:
361                     ruleDay = startDay;
362                     break;
363                 case DOW_IN_MONTH_MODE:
364                     if (startDay >= 0) {
365                         ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1
366                                 + (startDay - 1) * 7;
367                     } else {
368                         daysInMonth = GregorianCalendar.DaysInMonth[startMonth];
369                         if (startMonth == Calendar.FEBRUARY && isLeapYear(
370                                 year)) {
371                             daysInMonth += 1;
372                         }
373                         ruleDay = daysInMonth
374                                 + 1
375                                 + mod7(startDayOfWeek
376                                 - (firstDayOfMonth + daysInMonth))
377                                 + startDay * 7;
378                     }
379                     break;
380                 case DOW_GE_DOM_MODE:
381                     ruleDay = startDay
382                             + mod7(startDayOfWeek
383                             - (firstDayOfMonth + startDay - 1));
384                     break;
385                 case DOW_LE_DOM_MODE:
386                     ruleDay = startDay
387                             + mod7(startDayOfWeek
388                             - (firstDayOfMonth + startDay - 1));
389                     if (ruleDay != startDay) {
390                         ruleDay -= 7;
391                     }
392                     break;
393             }
394             if (ruleDay > day || ruleDay == day && time < startTime) {
395                 return rawOffset;
396             }
397         }
398 
399         int ruleTime = endTime - dstSavings;
400         int nextMonth = (month + 1) % 12;
401         if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) {
402             switch (endMode) {
403                 case DOM_MODE:
404                     ruleDay = endDay;
405                     break;
406                 case DOW_IN_MONTH_MODE:
407                     if (endDay >= 0) {
408                         ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1
409                                 + (endDay - 1) * 7;
410                     } else {
411                         daysInMonth = GregorianCalendar.DaysInMonth[endMonth];
412                         if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) {
413                             daysInMonth++;
414                         }
415                         ruleDay = daysInMonth
416                                 + 1
417                                 + mod7(endDayOfWeek
418                                 - (firstDayOfMonth + daysInMonth)) + endDay
419                                 * 7;
420                     }
421                     break;
422                 case DOW_GE_DOM_MODE:
423                     ruleDay = endDay
424                             + mod7(
425                             endDayOfWeek - (firstDayOfMonth + endDay - 1));
426                     break;
427                 case DOW_LE_DOM_MODE:
428                     ruleDay = endDay
429                             + mod7(
430                             endDayOfWeek - (firstDayOfMonth + endDay - 1));
431                     if (ruleDay != endDay) {
432                         ruleDay -= 7;
433                     }
434                     break;
435             }
436 
437             int ruleMonth = endMonth;
438             if (ruleTime < 0) {
439                 int changeDays = 1 - (ruleTime / 86400000);
440                 ruleTime = (ruleTime % 86400000) + 86400000;
441                 ruleDay -= changeDays;
442                 if (ruleDay <= 0) {
443                     if (--ruleMonth < Calendar.JANUARY) {
444                         ruleMonth = Calendar.DECEMBER;
445                     }
446                     ruleDay += GregorianCalendar.DaysInMonth[ruleMonth];
447                     if (ruleMonth == Calendar.FEBRUARY && isLeapYear(year)) {
448                         ruleDay++;
449                     }
450                 }
451             }
452 
453             if (month == ruleMonth) {
454                 if (ruleDay < day || ruleDay == day && time >= ruleTime) {
455                     return rawOffset;
456                 }
457             } else if (nextMonth != ruleMonth) {
458                 return rawOffset;
459             }
460         }
461         return rawOffset + dstSavings;
462     }
463 
464     @Override
getOffset(long time)465     public int getOffset(long time) {
466         // Simplified variant of the ICU4J code.
467         if (!useDaylightTime()) {
468             return rawOffset;
469         }
470         int[] fields = Grego.timeToFields(time + rawOffset, null);
471         return getOffset(GregorianCalendar.AD, fields[0], fields[1], fields[2],
472                 fields[3], fields[5]);
473     }
474 
475     @Override
getRawOffset()476     public int getRawOffset() {
477         return rawOffset;
478     }
479 
480     /**
481      * Returns an integer hash code for the receiver. Objects which are equal
482      * return the same value for this method.
483      *
484      * @return the receiver's hash.
485      * @see #equals
486      */
487     @Override
hashCode()488     public synchronized int hashCode() {
489         int hashCode = getID().hashCode() + rawOffset;
490         if (useDaylight) {
491             hashCode += startYear + startMonth + startDay + startDayOfWeek
492                     + startTime + startMode + endMonth + endDay + endDayOfWeek
493                     + endTime + endMode + dstSavings;
494         }
495         return hashCode;
496     }
497 
498     @Override
hasSameRules(TimeZone zone)499     public boolean hasSameRules(TimeZone zone) {
500         if (!(zone instanceof SimpleTimeZone)) {
501             return false;
502         }
503         SimpleTimeZone tz = (SimpleTimeZone) zone;
504         if (useDaylight != tz.useDaylight) {
505             return false;
506         }
507         if (!useDaylight) {
508             return rawOffset == tz.rawOffset;
509         }
510         return rawOffset == tz.rawOffset && dstSavings == tz.dstSavings
511                 && startYear == tz.startYear && startMonth == tz.startMonth
512                 && startDay == tz.startDay && startMode == tz.startMode
513                 && startDayOfWeek == tz.startDayOfWeek
514                 && startTime == tz.startTime && endMonth == tz.endMonth
515                 && endDay == tz.endDay && endDayOfWeek == tz.endDayOfWeek
516                 && endTime == tz.endTime && endMode == tz.endMode;
517     }
518 
inDaylightTime(Date time)519     @Override public boolean inDaylightTime(Date time) {
520         return useDaylightTime() && getOffset(time.getTime()) != getRawOffset();
521     }
522 
isLeapYear(int year)523     private boolean isLeapYear(int year) {
524         if (year > 1582) {
525             return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
526         }
527         return year % 4 == 0;
528     }
529 
mod7(int num1)530     private int mod7(int num1) {
531         int rem = num1 % 7;
532         return (num1 < 0 && rem < 0) ? 7 + rem : rem;
533     }
534 
535     /**
536      * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}.
537      *
538      * @param milliseconds
539      *            the daylight savings offset in milliseconds.
540      */
setDSTSavings(int milliseconds)541     public void setDSTSavings(int milliseconds) {
542         if (milliseconds > 0) {
543             dstSavings = milliseconds;
544         } else {
545             throw new IllegalArgumentException();
546         }
547     }
548 
checkRange(int month, int dayOfWeek, int time)549     private void checkRange(int month, int dayOfWeek, int time) {
550         if (month < Calendar.JANUARY || month > Calendar.DECEMBER) {
551             throw new IllegalArgumentException("Invalid month: " + month);
552         }
553         if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) {
554             throw new IllegalArgumentException("Invalid day of week: " + dayOfWeek);
555         }
556         if (time < 0 || time >= 24 * 3600000) {
557             throw new IllegalArgumentException("Invalid time: " + time);
558         }
559     }
560 
checkDay(int month, int day)561     private void checkDay(int month, int day) {
562         if (day <= 0 || day > GregorianCalendar.DaysInMonth[month]) {
563             throw new IllegalArgumentException("Invalid day of month: " + day);
564         }
565     }
566 
setEndMode()567     private void setEndMode() {
568         if (endDayOfWeek == 0) {
569             endMode = DOM_MODE;
570         } else if (endDayOfWeek < 0) {
571             endDayOfWeek = -endDayOfWeek;
572             if (endDay < 0) {
573                 endDay = -endDay;
574                 endMode = DOW_LE_DOM_MODE;
575             } else {
576                 endMode = DOW_GE_DOM_MODE;
577             }
578         } else {
579             endMode = DOW_IN_MONTH_MODE;
580         }
581         useDaylight = startDay != 0 && endDay != 0;
582         if (endDay != 0) {
583             checkRange(endMonth, endMode == DOM_MODE ? 1 : endDayOfWeek,
584                     endTime);
585             if (endMode != DOW_IN_MONTH_MODE) {
586                 checkDay(endMonth, endDay);
587             } else {
588                 if (endDay < -5 || endDay > 5) {
589                     throw new IllegalArgumentException("Day of week in month: " + endDay);
590                 }
591             }
592         }
593         if (endMode != DOM_MODE) {
594             endDayOfWeek--;
595         }
596     }
597 
598     /**
599      * Sets the rule which specifies the end of daylight savings time.
600      *
601      * @param month
602      *            the {@code Calendar} month in which daylight savings time ends.
603      * @param dayOfMonth
604      *            the {@code Calendar} day of the month on which daylight savings time
605      *            ends.
606      * @param time
607      *            the time of day in milliseconds standard time on which
608      *            daylight savings time ends.
609      */
setEndRule(int month, int dayOfMonth, int time)610     public void setEndRule(int month, int dayOfMonth, int time) {
611         endMonth = month;
612         endDay = dayOfMonth;
613         endDayOfWeek = 0; // Initialize this value for hasSameRules()
614         endTime = time;
615         setEndMode();
616     }
617 
618     /**
619      * Sets the rule which specifies the end of daylight savings time.
620      *
621      * @param month
622      *            the {@code Calendar} month in which daylight savings time ends.
623      * @param day
624      *            the occurrence of the day of the week on which daylight
625      *            savings time ends.
626      * @param dayOfWeek
627      *            the {@code Calendar} day of the week on which daylight savings time
628      *            ends.
629      * @param time
630      *            the time of day in milliseconds standard time on which
631      *            daylight savings time ends.
632      */
setEndRule(int month, int day, int dayOfWeek, int time)633     public void setEndRule(int month, int day, int dayOfWeek, int time) {
634         endMonth = month;
635         endDay = day;
636         endDayOfWeek = dayOfWeek;
637         endTime = time;
638         setEndMode();
639     }
640 
641     /**
642      * Sets the rule which specifies the end of daylight savings time.
643      *
644      * @param month
645      *            the {@code Calendar} month in which daylight savings time ends.
646      * @param day
647      *            the {@code Calendar} day of the month.
648      * @param dayOfWeek
649      *            the {@code Calendar} day of the week on which daylight savings time
650      *            ends.
651      * @param time
652      *            the time of day in milliseconds on which daylight savings time
653      *            ends.
654      * @param after
655      *            selects the day after or before the day of month.
656      */
setEndRule(int month, int day, int dayOfWeek, int time, boolean after)657     public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) {
658         endMonth = month;
659         endDay = after ? day : -day;
660         endDayOfWeek = -dayOfWeek;
661         endTime = time;
662         setEndMode();
663     }
664 
665     /**
666      * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}.
667      *
668      * @param offset
669      *            the offset from GMT of standard time in milliseconds.
670      */
671     @Override
setRawOffset(int offset)672     public void setRawOffset(int offset) {
673         rawOffset = offset;
674     }
675 
setStartMode()676     private void setStartMode() {
677         if (startDayOfWeek == 0) {
678             startMode = DOM_MODE;
679         } else if (startDayOfWeek < 0) {
680             startDayOfWeek = -startDayOfWeek;
681             if (startDay < 0) {
682                 startDay = -startDay;
683                 startMode = DOW_LE_DOM_MODE;
684             } else {
685                 startMode = DOW_GE_DOM_MODE;
686             }
687         } else {
688             startMode = DOW_IN_MONTH_MODE;
689         }
690         useDaylight = startDay != 0 && endDay != 0;
691         if (startDay != 0) {
692             checkRange(startMonth, startMode == DOM_MODE ? 1 : startDayOfWeek,
693                     startTime);
694             if (startMode != DOW_IN_MONTH_MODE) {
695                 checkDay(startMonth, startDay);
696             } else {
697                 if (startDay < -5 || startDay > 5) {
698                     throw new IllegalArgumentException("Day of week in month: " + startDay);
699                 }
700             }
701         }
702         if (startMode != DOM_MODE) {
703             startDayOfWeek--;
704         }
705     }
706 
707     /**
708      * Sets the rule which specifies the start of daylight savings time.
709      *
710      * @param month
711      *            the {@code Calendar} month in which daylight savings time starts.
712      * @param dayOfMonth
713      *            the {@code Calendar} day of the month on which daylight savings time
714      *            starts.
715      * @param time
716      *            the time of day in milliseconds on which daylight savings time
717      *            starts.
718      */
setStartRule(int month, int dayOfMonth, int time)719     public void setStartRule(int month, int dayOfMonth, int time) {
720         startMonth = month;
721         startDay = dayOfMonth;
722         startDayOfWeek = 0; // Initialize this value for hasSameRules()
723         startTime = time;
724         setStartMode();
725     }
726 
727     /**
728      * Sets the rule which specifies the start of daylight savings time.
729      *
730      * @param month
731      *            the {@code Calendar} month in which daylight savings time starts.
732      * @param day
733      *            the occurrence of the day of the week on which daylight
734      *            savings time starts.
735      * @param dayOfWeek
736      *            the {@code Calendar} day of the week on which daylight savings time
737      *            starts.
738      * @param time
739      *            the time of day in milliseconds on which daylight savings time
740      *            starts.
741      */
setStartRule(int month, int day, int dayOfWeek, int time)742     public void setStartRule(int month, int day, int dayOfWeek, int time) {
743         startMonth = month;
744         startDay = day;
745         startDayOfWeek = dayOfWeek;
746         startTime = time;
747         setStartMode();
748     }
749 
750     /**
751      * Sets the rule which specifies the start of daylight savings time.
752      *
753      * @param month
754      *            the {@code Calendar} month in which daylight savings time starts.
755      * @param day
756      *            the {@code Calendar} day of the month.
757      * @param dayOfWeek
758      *            the {@code Calendar} day of the week on which daylight savings time
759      *            starts.
760      * @param time
761      *            the time of day in milliseconds on which daylight savings time
762      *            starts.
763      * @param after
764      *            selects the day after or before the day of month.
765      */
setStartRule(int month, int day, int dayOfWeek, int time, boolean after)766     public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) {
767         startMonth = month;
768         startDay = after ? day : -day;
769         startDayOfWeek = -dayOfWeek;
770         startTime = time;
771         setStartMode();
772     }
773 
774     /**
775      * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}.
776      * Years before this start year will always be in standard time.
777      *
778      * @param year
779      *            the starting year.
780      */
setStartYear(int year)781     public void setStartYear(int year) {
782         startYear = year;
783         useDaylight = true;
784     }
785 
786     /**
787      * Returns the string representation of this {@code SimpleTimeZone}.
788      *
789      * @return the string representation of this {@code SimpleTimeZone}.
790      */
791     @Override
toString()792     public String toString() {
793         return getClass().getName()
794                 + "[id="
795                 + getID()
796                 + ",offset="
797                 + rawOffset
798                 + ",dstSavings="
799                 + dstSavings
800                 + ",useDaylight="
801                 + useDaylight
802                 + ",startYear="
803                 + startYear
804                 + ",startMode="
805                 + startMode
806                 + ",startMonth="
807                 + startMonth
808                 + ",startDay="
809                 + startDay
810                 + ",startDayOfWeek="
811                 + (useDaylight && (startMode != DOM_MODE) ? startDayOfWeek + 1
812                         : 0) + ",startTime=" + startTime + ",endMode="
813                 + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay
814                 + ",endDayOfWeek="
815                 + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0)
816                 + ",endTime=" + endTime + "]";
817     }
818 
819     @Override
useDaylightTime()820     public boolean useDaylightTime() {
821         return useDaylight;
822     }
823 
824     private static final ObjectStreamField[] serialPersistentFields = {
825         new ObjectStreamField("dstSavings", int.class),
826         new ObjectStreamField("endDay", int.class),
827         new ObjectStreamField("endDayOfWeek", int.class),
828         new ObjectStreamField("endMode", int.class),
829         new ObjectStreamField("endMonth", int.class),
830         new ObjectStreamField("endTime", int.class),
831         new ObjectStreamField("monthLength", byte[].class),
832         new ObjectStreamField("rawOffset", int.class),
833         new ObjectStreamField("serialVersionOnStream", int.class),
834         new ObjectStreamField("startDay", int.class),
835         new ObjectStreamField("startDayOfWeek", int.class),
836         new ObjectStreamField("startMode", int.class),
837         new ObjectStreamField("startMonth", int.class),
838         new ObjectStreamField("startTime", int.class),
839         new ObjectStreamField("startYear", int.class),
840         new ObjectStreamField("useDaylight", boolean.class),
841     };
842 
writeObject(ObjectOutputStream stream)843     private void writeObject(ObjectOutputStream stream) throws IOException {
844         int sEndDay = endDay, sEndDayOfWeek = endDayOfWeek + 1, sStartDay = startDay, sStartDayOfWeek = startDayOfWeek + 1;
845         if (useDaylight
846                 && (startMode != DOW_IN_MONTH_MODE || endMode != DOW_IN_MONTH_MODE)) {
847             Calendar cal = new GregorianCalendar(this);
848             if (endMode != DOW_IN_MONTH_MODE) {
849                 cal.set(Calendar.MONTH, endMonth);
850                 cal.set(Calendar.DATE, endDay);
851                 sEndDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
852                 if (endMode == DOM_MODE) {
853                     sEndDayOfWeek = cal.getFirstDayOfWeek();
854                 }
855             }
856             if (startMode != DOW_IN_MONTH_MODE) {
857                 cal.set(Calendar.MONTH, startMonth);
858                 cal.set(Calendar.DATE, startDay);
859                 sStartDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
860                 if (startMode == DOM_MODE) {
861                     sStartDayOfWeek = cal.getFirstDayOfWeek();
862                 }
863             }
864         }
865         ObjectOutputStream.PutField fields = stream.putFields();
866         fields.put("dstSavings", dstSavings);
867         fields.put("endDay", sEndDay);
868         fields.put("endDayOfWeek", sEndDayOfWeek);
869         fields.put("endMode", endMode);
870         fields.put("endMonth", endMonth);
871         fields.put("endTime", endTime);
872         fields.put("monthLength", GregorianCalendar.DaysInMonth);
873         fields.put("rawOffset", rawOffset);
874         fields.put("serialVersionOnStream", 1);
875         fields.put("startDay", sStartDay);
876         fields.put("startDayOfWeek", sStartDayOfWeek);
877         fields.put("startMode", startMode);
878         fields.put("startMonth", startMonth);
879         fields.put("startTime", startTime);
880         fields.put("startYear", startYear);
881         fields.put("useDaylight", useDaylight);
882         stream.writeFields();
883         stream.writeInt(4);
884         byte[] values = new byte[4];
885         values[0] = (byte) startDay;
886         values[1] = (byte) (startMode == DOM_MODE ? 0 : startDayOfWeek + 1);
887         values[2] = (byte) endDay;
888         values[3] = (byte) (endMode == DOM_MODE ? 0 : endDayOfWeek + 1);
889         stream.write(values);
890     }
891 
readObject(ObjectInputStream stream)892     private void readObject(ObjectInputStream stream) throws IOException,
893             ClassNotFoundException {
894         ObjectInputStream.GetField fields = stream.readFields();
895         rawOffset = fields.get("rawOffset", 0);
896         useDaylight = fields.get("useDaylight", false);
897         if (useDaylight) {
898             endMonth = fields.get("endMonth", 0);
899             endTime = fields.get("endTime", 0);
900             startMonth = fields.get("startMonth", 0);
901             startTime = fields.get("startTime", 0);
902             startYear = fields.get("startYear", 0);
903         }
904         if (fields.get("serialVersionOnStream", 0) == 0) {
905             if (useDaylight) {
906                 startMode = endMode = DOW_IN_MONTH_MODE;
907                 endDay = fields.get("endDay", 0);
908                 endDayOfWeek = fields.get("endDayOfWeek", 0) - 1;
909                 startDay = fields.get("startDay", 0);
910                 startDayOfWeek = fields.get("startDayOfWeek", 0) - 1;
911             }
912         } else {
913             dstSavings = fields.get("dstSavings", 0);
914             if (useDaylight) {
915                 endMode = fields.get("endMode", 0);
916                 startMode = fields.get("startMode", 0);
917                 int length = stream.readInt();
918                 byte[] values = new byte[length];
919                 stream.readFully(values);
920                 if (length >= 4) {
921                     startDay = values[0];
922                     startDayOfWeek = values[1];
923                     if (startMode != DOM_MODE) {
924                         startDayOfWeek--;
925                     }
926                     endDay = values[2];
927                     endDayOfWeek = values[3];
928                     if (endMode != DOM_MODE) {
929                         endDayOfWeek--;
930                     }
931                 }
932             }
933         }
934     }
935 
936 }
937