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