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