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