home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / SimpleTimeZone.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  33.6 KB  |  836 lines

  1. /*
  2.  * @(#)SimpleTimeZone.java    1.20 98/03/18
  3.  *
  4.  * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  5.  * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  6.  *
  7.  * Portions copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
  8.  *
  9.  *   The original version of this source code and documentation is copyrighted
  10.  * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  11.  * materials are provided under terms of a License Agreement between Taligent
  12.  * and Sun. This technology is protected by multiple US and International
  13.  * patents. This notice and attribution to Taligent may not be removed.
  14.  *   Taligent is a registered trademark of Taligent, Inc.
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software
  17.  * and its documentation for NON-COMMERCIAL purposes and without
  18.  * fee is hereby granted provided that this copyright notice
  19.  * appears in all copies. Please refer to the file "copyright.html"
  20.  * for further important copyright and licensing information.
  21.  *
  22.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  23.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  24.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  25.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  26.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  27.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  28.  *
  29.  */
  30.  
  31. package java.util;
  32.  
  33. import java.io.ObjectInputStream;
  34. import java.io.ObjectOutputStream;
  35. import java.io.IOException;
  36.  
  37. /**
  38.  * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
  39.  * that represents a time zone for use with a Gregorian calendar. This
  40.  * class does not handle historical changes.
  41.  *
  42.  * <P>
  43.  * Use a negative value for <code>dayOfWeekInMonth</code> to indicate that
  44.  * <code>SimpleTimeZone</code> should count from the end of the month backwards.
  45.  * For example, Daylight Savings Time ends at the last
  46.  * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
  47.  *
  48.  * @see      Calendar
  49.  * @see      GregorianCalendar
  50.  * @see      TimeZone
  51.  * @version  1.20 03/18/98
  52.  * @author   David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
  53.  */
  54. public class SimpleTimeZone extends TimeZone {
  55.     /**
  56.      * Constructs a SimpleTimeZone with the given base time zone offset from GMT
  57.      * and time zone ID. Timezone IDs can be obtained from
  58.      * TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to
  59.      * construct a TimeZone.
  60.      *
  61.      * @param rawOffset  The given base time zone offset to GMT.
  62.      * @param ID         The time zone ID which is obtained from
  63.      *                   TimeZone.getAvailableIDs.
  64.      */
  65.     public SimpleTimeZone(int rawOffset, String ID)
  66.     {
  67.         this.rawOffset = rawOffset;
  68.         setID (ID);
  69.     dstSavings = millisPerHour; // In case user sets rules later
  70.     }
  71.  
  72.     /**
  73.      * Construct a SimpleTimeZone with the given base time zone offset from
  74.      * GMT, time zone ID, time to start and end the daylight time. Timezone IDs
  75.      * can be obtained from TimeZone.getAvailableIDs. Normally you should use
  76.      * TimeZone.getDefault to create a TimeZone. For a time zone that does not
  77.      * use daylight saving time, do not use this constructor; instead you should
  78.      * use SimpleTimeZone(rawOffset, ID).
  79.      *
  80.      * By default, this constructor specifies day-of-week-in-month rules. That
  81.      * is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this
  82.      * indicates the first Sunday in the startMonth. A startDay of -1 likewise
  83.      * indicates the last Sunday. However, by using negative or zero values for
  84.      * certain parameters, other types of rules can be specified.
  85.      *
  86.      * Day of month. To specify an exact day of the month, such as March 1, set
  87.      * startDayOfWeek to zero.
  88.      *
  89.      * Day of week after day of month. To specify the first day of the week
  90.      * occurring on or after an exact day of the month, make the day of the week
  91.      * negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY,
  92.      * this indicates the first Monday on or after the 5th day of the
  93.      * startMonth.
  94.      *
  95.      * Day of week before day of month. To specify the last day of the week
  96.      * occurring on or before an exact day of the month, make the day of the
  97.      * week and the day of the month negative. For example, if startDay is -21
  98.      * and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or
  99.      * before the 21st of the startMonth.
  100.      *
  101.      * The above examples refer to the startMonth, startDay, and startDayOfWeek;
  102.      * the same applies for the endMonth, endDay, and endDayOfWeek.
  103.      *
  104.      * @param rawOffset       The given base time zone offset to GMT.
  105.      * @param ID              The time zone ID which is obtained from
  106.      *                        TimeZone.getAvailableIDs.
  107.      * @param startMonth      The daylight savings starting month. Month is
  108.      *                        0-based. eg, 0 for January.
  109.      * @param startDay        The daylight savings starting
  110.      *                        day-of-week-in-month. Please see the member
  111.      *                        description for an example.
  112.      * @param startDayOfWeek  The daylight savings starting day-of-week. Please
  113.      *                        see the member description for an example.
  114.      * @param startTime       The daylight savings starting time. Please see the
  115.      *                        member description for an example.
  116.      * @param endMonth        The daylight savings ending month. Month is
  117.      *                        0-based. eg, 0 for January.
  118.      * @param endDay          The daylight savings ending day-of-week-in-month.
  119.      *                        Please see the member description for an example.
  120.      * @param endDayOfWeek    The daylight savings ending day-of-week. Please
  121.      *                        see the member description for an example.
  122.      * @param endTime         The daylight savings ending time. Please see the
  123.      *                        member description for an example.
  124.      */
  125.     public SimpleTimeZone(int rawOffset, String ID,
  126.                           int startMonth, int startDay, int startDayOfWeek, int startTime,
  127.                           int endMonth, int endDay, int endDayOfWeek, int endTime)
  128.     {
  129.         this(rawOffset, ID,
  130.              startMonth, startDay, startDayOfWeek, startTime,
  131.              endMonth, endDay, endDayOfWeek, endTime,
  132.              millisPerHour);
  133.     }
  134.  
  135.     /**
  136.      * Constructor.  This constructor is identical to the 10-argument
  137.      * constructor, but also takes a dstSavings parameter.
  138.      * @param dstSavings   The amount of time in ms saved during DST.
  139.      */
  140. // function made package private pending API-change approval
  141.     /*public*/ SimpleTimeZone(int rawOffset, String ID,
  142.                           int startMonth, int startDay, int startDayOfWeek, int startTime,
  143.                           int endMonth, int endDay, int endDayOfWeek, int endTime,
  144.                           int dstSavings)
  145.     {
  146.         setID(ID);
  147.         this.rawOffset      = rawOffset;
  148.         this.startMonth     = startMonth;
  149.         this.startDay       = startDay;
  150.         this.startDayOfWeek = startDayOfWeek;
  151.         this.startTime      = startTime;
  152.         this.endMonth       = endMonth;
  153.         this.endDay         = endDay;
  154.         this.endDayOfWeek   = endDayOfWeek;
  155.         this.endTime        = endTime;
  156.         this.dstSavings     = dstSavings;
  157.         // this.useDaylight    = true; // Set by decodeRules
  158.         decodeRules();
  159.     }
  160.  
  161.     /**
  162.      * Sets the daylight savings starting year.
  163.      *
  164.      * @param year  The daylight savings starting year.
  165.      */
  166.     public void setStartYear(int year)
  167.     {
  168.         startYear = year;
  169.     }
  170.  
  171.     /**
  172.      * Sets the daylight savings starting rule. For example, Daylight Savings
  173.      * Time starts at the first Sunday in April, at 2 AM in standard time.
  174.      * Therefore, you can set the start rule by calling:
  175.      * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
  176.      *
  177.      * @param month             The daylight savings starting month. Month is
  178.      *                          0-based. eg, 0 for January.
  179.      * @param dayOfWeekInMonth  The daylight savings starting
  180.      *                          day-of-week-in-month. Please see the member
  181.      *                          description for an example.
  182.      * @param dayOfWeek         The daylight savings starting day-of-week.
  183.      *                          Please see the member description for an
  184.      *                          example.
  185.      * @param time              The daylight savings starting time. Please see
  186.      *                          the member description for an example.
  187.      */
  188.     public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  189.                              int time)
  190.     {
  191.         startMonth = month;
  192.         startDay = dayOfWeekInMonth;
  193.         startDayOfWeek = dayOfWeek;
  194.         startTime = time;
  195.         // useDaylight = true; // Set by decodeRules
  196.         decodeStartRule();
  197.     }
  198.  
  199.     /**
  200.      * Sets the DST start rule to a fixed date within a month.
  201.      *
  202.      * @param month         The month in which this rule occurs (0-based).
  203.      * @param dayOfMonth    The date in that month (1-based).
  204.      * @param time          The time of that day (number of millis after midnight)
  205.      *                          when DST takes effect.
  206.      */
  207. // function made package private pending API-change approval
  208.     /*public*/ void setStartRule(int month, int dayOfMonth, int time)
  209.     {
  210.         setStartRule(month, dayOfMonth, 0, time);
  211.     }
  212.  
  213.     /**
  214.      * Sets the DST start rule to a weekday before or after a give date within
  215.      * a month, e.g., the first Monday on or after the 8th.
  216.      *
  217.      * @param month         The month in which this rule occurs (0-based).
  218.      * @param dayOfMonth    A date within that month (1-based).
  219.      * @param dayOfWeek     The day of the week on which this rule occurs.
  220.      * @param time          The time of that day (number of millis after midnight)
  221.      *                          when DST takes effect.
  222.      * @param after         If true, this rule selects the first dayOfWeek on
  223.      *                      or after dayOfMonth.  If false, this rule selects
  224.      *                      the last dayOfWeek on or before dayOfMonth.
  225.      */
  226. // function made package private pending API-change approval
  227.     /*public*/ void setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)
  228.     {
  229.         if (after)
  230.             setStartRule(month, dayOfMonth, -dayOfWeek, time);
  231.         else
  232.             setStartRule(month, -dayOfMonth, -dayOfWeek, time);
  233.     }
  234.  
  235.     /**
  236.      * Sets the daylight savings ending rule. For example, Daylight Savings Time
  237.      * ends at the last (-1) Sunday in October, at 2 AM in standard time.
  238.      * Therefore, you can set the end rule by calling:
  239.      * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
  240.      *
  241.      * @param month             The daylight savings ending month. Month is
  242.      *                          0-based. eg, 0 for January.
  243.      * @param dayOfWeekInMonth  The daylight savings ending
  244.      *                          day-of-week-in-month. Please see the member
  245.      *                          description for an example.
  246.      * @param dayOfWeek         The daylight savings ending day-of-week. Please
  247.      *                          see the member description for an example.
  248.      * @param time              The daylight savings ending time. Please see the
  249.      *                          member description for an example.
  250.      */
  251.     public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  252.                            int time)
  253.     {
  254.         endMonth = month;
  255.         endDay = dayOfWeekInMonth;
  256.         endDayOfWeek = dayOfWeek;
  257.         endTime = time;
  258.         // useDaylight = true; // Set by decodeRules
  259.         decodeEndRule();
  260.     }
  261.  
  262.     /**
  263.      * Sets the DST end rule to a fixed date within a month.
  264.      *
  265.      * @param month         The month in which this rule occurs (0-based).
  266.      * @param dayOfMonth    The date in that month (1-based).
  267.      * @param time          The time of that day (number of millis after midnight)
  268.      *                          when DST ends.
  269.      */
  270. // function made package private pending API-change approval
  271.     /*public*/ void setEndRule(int month, int dayOfMonth, int time)
  272.     {
  273.         setEndRule(month, dayOfMonth, 0, time);
  274.     }
  275.  
  276.     /**
  277.      * Sets the DST end rule to a weekday before or after a give date within
  278.      * a month, e.g., the first Monday on or after the 8th.
  279.      *
  280.      * @param month         The month in which this rule occurs (0-based).
  281.      * @param dayOfMonth    A date within that month (1-based).
  282.      * @param dayOfWeek     The day of the week on which this rule occurs.
  283.      * @param time          The time of that day (number of millis after midnight)
  284.      *                          when DST ends.
  285.      * @param after         If true, this rule selects the first dayOfWeek on
  286.      *                      or after dayOfMonth.  If false, this rule selects
  287.      *                      the last dayOfWeek on or before dayOfMonth.
  288.      */
  289. // function made package private pending API-change approval
  290.     /*public*/ void setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)
  291.     {
  292.         if (after)
  293.             setEndRule(month, dayOfMonth, -dayOfWeek, time);
  294.         else
  295.             setEndRule(month, -dayOfMonth, -dayOfWeek, time);
  296.     }
  297.  
  298.     /**
  299.      * Overrides TimeZone
  300.      * Gets offset, for current date, modified in case of
  301.      * daylight savings. This is the offset to add *to* UTC to get local time.
  302.      * Gets the time zone offset, for current date, modified in case of daylight
  303.      * savings. This is the offset to add *to* UTC to get local time. Assume
  304.      * that the start and end month are distinct, and that no rule refers to the
  305.      * end of February (e.g., last Sunday in February).
  306.      *
  307.      * @param era           The era of the given date.
  308.      * @param year          The year in the given date.
  309.      * @param month         The month in the given date. Month is 0-based. e.g.,
  310.      *                      0 for January.
  311.      * @param day           The day-in-month of the given date.
  312.      * @param dayOfWeek     The day-of-week of the given date.
  313.      * @param milliseconds  The millis in day in <em>standard</em> local time.
  314.      * @return              The offset to add *to* GMT to get local time.
  315.      */
  316.     public int getOffset(int era, int year, int month, int day, int dayOfWeek,
  317.                          int millis)
  318.     {
  319.         int result = rawOffset;
  320.  
  321.         // Bail out if we are before the onset of daylight savings time
  322.         if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result;
  323.  
  324.         // Check for southern hemisphere.  We assume that the start and end
  325.         // month are different.
  326.         boolean southern = (startMonth > endMonth);
  327.  
  328.         // Compare the date to the starting and ending rules.  For the ending
  329.         // rule comparison, we add the dstSavings to the millis passed in to convert
  330.         // them from standard to wall time.  +1 = date>rule, -1 = date<rule, 0 =
  331.         // date==rule.
  332.         int startCompare = compareToRule(month, day, dayOfWeek, millis,
  333.                                          startMode, startMonth, startDayOfWeek,
  334.                                          startDay, startTime);
  335.         int endCompare = compareToRule(month, day, dayOfWeek, millis + dstSavings,
  336.                                        endMode, endMonth, endDayOfWeek,
  337.                                        endDay, endTime);
  338.  
  339.         // Check for both the northern and southern hemisphere cases.  We
  340.         // assume that in the northern hemisphere, the start rule is before the
  341.         // end rule within the calendar year, and vice versa for the southern
  342.         // hemisphere.
  343.         if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
  344.             (southern && (startCompare >= 0 || endCompare < 0)))
  345.             result += dstSavings;
  346.  
  347.         return result;
  348.     }
  349.  
  350.     /**
  351.      * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
  352.      * on whether the date is after, equal to, or before the rule date. The
  353.      * millis are compared directly against the ruleMillis, so any
  354.      * standard-daylight adjustments must be handled by the caller. Assume that
  355.      * no rule references the end of February (e.g., last Sunday in February).
  356.      *
  357.      * @return  1 if the date is after the rule date, -1 if the date is before
  358.      *          the rule date, or 0 if the date is equal to the rule date.
  359.      */
  360.     private static int compareToRule(int month, int dayOfMonth, int dayOfWeek, int millis,
  361.                                      int ruleMode, int ruleMonth, int ruleDayOfWeek,
  362.                                      int ruleDay, int ruleMillis)
  363.     {
  364.         if (month < ruleMonth) return -1;
  365.         else if (month > ruleMonth) return 1;
  366.  
  367.         int ruleDayOfMonth = 0;
  368.         switch (ruleMode)
  369.         {
  370.         case DOM_MODE:
  371.             ruleDayOfMonth = ruleDay;
  372.             break;
  373.         case DOW_IN_MONTH_MODE:
  374.             // In this case ruleDay is the day-of-week-in-month
  375.             if (ruleDay > 0)
  376.                 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
  377.                     (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
  378.             else // Assume ruleDay < 0 here
  379.             {
  380.                 int monthLen = staticMonthLength[month];
  381.  
  382.                 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
  383.                     (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
  384.             }
  385.             break;
  386.         case DOW_GE_DOM_MODE:
  387.             ruleDayOfMonth = ruleDay +
  388.                 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
  389.             break;
  390.         case DOW_LE_DOM_MODE:
  391.             ruleDayOfMonth = ruleDay -
  392.                 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
  393.             // Note at this point ruleDayOfMonth may be <1, although it will
  394.             // be >=1 for well-formed rules.
  395.             break;
  396.         }
  397.  
  398.         if (dayOfMonth < ruleDayOfMonth) return -1;
  399.         else if (dayOfMonth > ruleDayOfMonth) return 1;
  400.  
  401.         if (millis < ruleMillis) return -1;
  402.         else if (millis > ruleMillis) return 1;
  403.         else return 0;
  404.     }
  405.  
  406.     /**
  407.      * Overrides TimeZone
  408.      * Gets the GMT offset for this time zone.
  409.      */
  410.     public int getRawOffset()
  411.     {
  412.         // The given date will be taken into account while
  413.         // we have the historical time zone data in place.
  414.         return rawOffset;
  415.     }
  416.  
  417.     /**
  418.      * Overrides TimeZone
  419.      * Sets the base time zone offset to GMT.
  420.      * This is the offset to add *to* UTC to get local time.
  421.      * Please see TimeZone.setRawOffset for descriptions on the parameter.
  422.      */
  423.     public void setRawOffset(int offsetMillis)
  424.     {
  425.         this.rawOffset = offsetMillis;
  426.     }
  427.  
  428.     /**
  429.      * Sets the amount of time in ms that the clock is advanced during DST.
  430.      */
  431. // function made package private pending API-change approval
  432.     /*public*/ void setDSTSavings(int millisSavedDuringDST) {
  433.         dstSavings = millisSavedDuringDST;
  434.     }
  435.  
  436.     /**
  437.      * Returns the amount of time in ms that the clock is advanced during DST.
  438.      */
  439. // function made package private pending API-change approval
  440.     /*public*/ int getDSTSavings() {
  441.         return dstSavings;
  442.     }
  443.  
  444.     /**
  445.      * Overrides TimeZone
  446.      * Queries if this time zone uses Daylight Savings Time.
  447.      */
  448.     public boolean useDaylightTime()
  449.     {
  450.         return useDaylight;
  451.     }
  452.  
  453.     /**
  454.      * Overrides TimeZone
  455.      * Queries if the given date is in Daylight Savings Time.
  456.      */
  457.     public boolean inDaylightTime(Date date)
  458.     {
  459.         GregorianCalendar gc = new GregorianCalendar(this);
  460.         gc.setTime(date);
  461.         return gc.inDaylightTime();
  462.     }
  463.  
  464.     /**
  465.      * Overrides Cloneable
  466.      */
  467.     public Object clone()
  468.     {
  469.         return super.clone();
  470.         // other fields are bit-copied
  471.     }
  472.  
  473.     /**
  474.      * Override hashCode.
  475.      * Generates the hash code for the SimpleDateFormat object
  476.      */
  477.     public synchronized int hashCode()
  478.     {
  479.         return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
  480.             endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
  481.     }
  482.  
  483.     /**
  484.      * Compares the equality of two SimpleTimeZone objects.
  485.      *
  486.      * @param obj  The SimpleTimeZone object to be compared with.
  487.      * @return     True if the given obj is the same as this SimpleTimeZone
  488.      *             object; false otherwise.
  489.      */
  490.     public boolean equals(Object obj)
  491.     {
  492.         if (this == obj)
  493.             return true;
  494.         if (!(obj instanceof SimpleTimeZone))
  495.             return false;
  496.  
  497.         SimpleTimeZone that = (SimpleTimeZone) obj;
  498.  
  499.         return this.getID().equals(that.getID()) &&
  500.             rawOffset == that.rawOffset &&
  501.             useDaylight == that.useDaylight &&
  502.             dstSavings == that.dstSavings &&
  503.             startMode == that.startMode &&
  504.             startMonth == that.startMonth &&
  505.             startDay == that.startDay &&
  506.             startDayOfWeek == that.startDayOfWeek &&
  507.             startTime == that.startTime &&
  508.             endMode == that.endMode &&
  509.             endMonth == that.endMonth &&
  510.             endDay == that.endDay &&
  511.             endDayOfWeek == that.endDayOfWeek &&
  512.             endTime == that.endTime &&
  513.             startYear == that.startYear;
  514.     }
  515.  
  516.     /**
  517.      * Return a string representation of this time zone.
  518.      * @return  a string representation of this time zone.
  519.      */
  520.     public String toString() {
  521.         return getClass().getName() +
  522.             "[id=" + getID() +
  523.             ",offset=" + rawOffset +
  524.             ",dstSavings=" + dstSavings +
  525.             ",useDaylight=" + useDaylight +
  526.             ",startYear=" + startYear +
  527.             ",startMode=" + startMode +
  528.             ",startMonth=" + startMonth +
  529.             ",startDay=" + startDay +
  530.             ",startDayOfWeek=" + startDayOfWeek +
  531.             ",startTime=" + startTime +
  532.             ",endMode=" + endMode +
  533.             ",endMonth=" + endMonth +
  534.             ",endDay=" + endDay +
  535.             ",endDayOfWeek=" + endDayOfWeek +
  536.             ",endTime=" + endTime + ']';
  537.     }
  538.  
  539.     // =======================privates===============================
  540.  
  541.     private int startMonth, startDay, startDayOfWeek, startTime;
  542.     private int endMonth, endDay, endDayOfWeek, endTime;
  543.     private int startYear;
  544.     private int rawOffset;
  545.     private boolean useDaylight=false; // indicate if this time zone uses DST
  546.     private static final int millisPerHour = 60*60*1000;
  547.     // WARNING: assumes that no rule is measured from the end of February,
  548.     // since we don't handle leap years. Could handle assuming always
  549.     // Gregorian, since we know they didn't have daylight time when
  550.     // Gregorian calendar started.
  551.     // monthLength was non-static in JDK 1.1, so we have to keep it that way
  552.     // to maintain serialization compatibility. However, there's no need to
  553.     // recreate the array each time we create a new time zone.
  554.     private final byte monthLength[] = staticMonthLength;
  555.     private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  556.  
  557.     /**
  558.      * Variables specifying the mode of the start and end rules.
  559.      */
  560.     private int startMode, endMode; // fields new in JDK 1.1.4
  561.  
  562.     /**
  563.      * A positive value indicating the amount of time saved during DST in ms.
  564.      * Typically one hour; sometimes 30 minutes.
  565.      */
  566.     private int dstSavings; // field new in JDK 1.1.4
  567.  
  568.     /**
  569.      * Constants specifying values of startMode and endMode.
  570.      */
  571.     private static final int DOM_MODE          = 1; // Exact day of month, "Mar 1"
  572.     private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
  573.     private static final int DOW_GE_DOM_MODE   = 3; // Day of week after day of month, "Sun>=15"
  574.     private static final int DOW_LE_DOM_MODE   = 4; // Day of week before day of month, "Sun<=21"
  575.  
  576.     // Proclaim compatibility with 1.1
  577.     static final long serialVersionUID = -403250971215465050L;
  578.  
  579.     // the internal serial version which says which version was written
  580.     // - 0 (default) for version up to JDK 1.1.3
  581.     // - 1 for version from JDK 1.1.4, which includes 3 new fields
  582.     static final int currentSerialVersion = 1;
  583.     private int serialVersionOnStream = currentSerialVersion;
  584.  
  585.     //----------------------------------------------------------------------
  586.     // Rule representation
  587.     //
  588.     // We represent the following flavors of rules:
  589.     //       5        the fifth of the month
  590.     //       lastSun  the last Sunday in the month
  591.     //       lastMon  the last Monday in the month
  592.     //       Sun>=8   first Sunday on or after the eighth
  593.     //       Sun<=25  last Sunday on or before the 25th
  594.     // This is further complicated by the fact that we need to remain
  595.     // backward compatible with the 1.1 FCS.  Finally, we need to minimize
  596.     // API changes.  In order to satisfy these requirements, we support
  597.     // three representation systems, and we translate between them.
  598.     //
  599.     // INTERNAL REPRESENTATION
  600.     // This is the format SimpleTimeZone objects take after construction or
  601.     // streaming in is complete.  Rules are represented directly, using an
  602.     // unencoded format.  We will discuss the start rule only below; the end
  603.     // rule is analogous.
  604.     //   startMode      Takes on enumerated values DAY_OF_MONTH,
  605.     //                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
  606.     //   startDay       The day of the month, or for DOW_IN_MONTH mode, a
  607.     //                  value indicating which DOW, such as +1 for first,
  608.     //                  +2 for second, -1 for last, etc.
  609.     //   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
  610.     //
  611.     // ENCODED REPRESENTATION
  612.     // This is the format accepted by the constructor and by setStartRule()
  613.     // and setEndRule().  It uses various combinations of positive, negative,
  614.     // and zero values to encode the different rules.  This representation
  615.     // allows us to specify all the different rule flavors without altering
  616.     // the API.
  617.     //   MODE              startMonth    startDay    startDayOfWeek
  618.     //   DOW_IN_MONTH_MODE >=0           !=0         >0
  619.     //   DOM_MODE          >=0           >0          ==0
  620.     //   DOW_GE_DOM_MODE   >=0           >0          <0
  621.     //   DOW_LE_DOM_MODE   >=0           <0          <0
  622.     //   (no DST)          don't care    ==0         don't care
  623.     //
  624.     // STREAMED REPRESENTATION
  625.     // We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
  626.     // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
  627.     // flag useDaylight.  When we stream an object out, we translate into an
  628.     // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
  629.     // and used by 1.1 code.  Following that, we write out the full
  630.     // representation separately so that contemporary code can recognize and
  631.     // parse it.  The full representation is written in a "packed" format,
  632.     // consisting of a version number, a length, and an array of bytes.  Future
  633.     // versions of this class may specify different versions.  If they wish to
  634.     // include additional data, they should do so by storing them after the
  635.     // packed representation below.
  636.     //----------------------------------------------------------------------
  637.  
  638.     /**
  639.      * Given a set of encoded rules in startDay and startDayOfMonth, decode
  640.      * them and set the startMode appropriately.  Do the same for endDay and
  641.      * endDayOfMonth.  Upon entry, the day of week variables may be zero or
  642.      * negative, in order to indicate special modes.  The day of month
  643.      * variables may also be negative.  Upon exit, the mode variables will be
  644.      * set, and the day of week and day of month variables will be positive.
  645.      * This method also recognizes a startDay or endDay of zero as indicating
  646.      * no DST.
  647.      */
  648.     private void decodeRules()
  649.     {
  650.         decodeStartRule();
  651.         decodeEndRule();
  652.     }
  653.  
  654.     private void decodeStartRule()
  655.     {
  656.         useDaylight = (startDay != 0) && (endDay != 0);
  657.         if (startDayOfWeek == 0)
  658.             startMode = DOM_MODE;
  659.         else if (startDayOfWeek > 0)
  660.             startMode = DOW_IN_MONTH_MODE;
  661.         else
  662.         {
  663.             startDayOfWeek = -startDayOfWeek;
  664.             if (startDay > 0) startMode = DOW_GE_DOM_MODE;
  665.             else
  666.             {
  667.             startDay = -startDay;
  668.             startMode = DOW_LE_DOM_MODE;
  669.             }
  670.         }
  671.     }
  672.  
  673.     private void decodeEndRule()
  674.     {
  675.         useDaylight = (startDay != 0) && (endDay != 0);
  676.         if (endDayOfWeek == 0)
  677.             endMode = DOM_MODE;
  678.         else if (endDayOfWeek > 0)
  679.             endMode = DOW_IN_MONTH_MODE;
  680.         else
  681.         {
  682.             endDayOfWeek = -endDayOfWeek;
  683.             if (endDay > 0) endMode = DOW_GE_DOM_MODE;
  684.             else
  685.             {
  686.             endDay = -endDay;
  687.             endMode = DOW_LE_DOM_MODE;
  688.             }
  689.         }
  690.     }
  691.  
  692.     /**
  693.      * Make rules compatible to 1.1 FCS code.  Since 1.1 FCS code only understands
  694.      * day-of-week-in-month rules, we must modify other modes of rules to their
  695.      * approximate equivalent in 1.1 FCS terms.  This method is used when streaming
  696.      * out objects of this class.  After it is called, the rules will be modified,
  697.      * with a possible loss of information.  startMode and endMode will NOT be
  698.      * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
  699.      * since the rule modification is only intended to be temporary.
  700.      */
  701.     private void makeRulesCompatible()
  702.     {
  703.         switch (startMode)
  704.         {
  705.         case DOM_MODE:
  706.             startDay = 1 + (startDay / 7);
  707.             startDayOfWeek = Calendar.SUNDAY;
  708.             break;
  709.         case DOW_GE_DOM_MODE:
  710.             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  711.             // that is, Sun>=1 == firstSun.
  712.             if (startDay != 1)
  713.                 startDay = 1 + (startDay / 7);
  714.             break;
  715.         case DOW_LE_DOM_MODE:
  716.             if (startDay >= 30)
  717.                 startDay = -1;
  718.             else
  719.                 startDay = 1 + (startDay / 7);
  720.             break;
  721.         }
  722.  
  723.         switch (endMode)
  724.         {
  725.         case DOM_MODE:
  726.             endDay = 1 + (endDay / 7);
  727.             endDayOfWeek = Calendar.SUNDAY;
  728.             break;
  729.         case DOW_GE_DOM_MODE:
  730.             // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  731.             // that is, Sun>=1 == firstSun.
  732.             if (endDay != 1)
  733.                 endDay = 1 + (endDay / 7);
  734.             break;
  735.         case DOW_LE_DOM_MODE:
  736.             if (endDay >= 30)
  737.                 endDay = -1;
  738.             else
  739.                 endDay = 1 + (endDay / 7);
  740.             break;
  741.         }
  742.     }
  743.  
  744.     /**
  745.      * Pack the start and end rules into an array of bytes.  Only pack
  746.      * data which is not preserved by makeRulesCompatible.
  747.      */
  748.     private byte[] packRules()
  749.     {
  750.         byte[] rules = new byte[4];
  751.         rules[0] = (byte)startDay;
  752.         rules[1] = (byte)startDayOfWeek;
  753.         rules[2] = (byte)endDay;
  754.         rules[3] = (byte)endDayOfWeek;
  755.         return rules;
  756.     }
  757.  
  758.     /**
  759.      * Given an array of bytes produced by packRules, interpret them
  760.      * as the start and end rules.
  761.      */
  762.     private void unpackRules(byte[] rules)
  763.     {
  764.         startDay       = rules[0];
  765.         startDayOfWeek = rules[1];
  766.         endDay         = rules[2];
  767.         endDayOfWeek   = rules[3];
  768.     }
  769.  
  770.     /**
  771.      * Write object out to a serialization stream.  Note that we write out two
  772.      * formats, a 1.1 FCS-compatible format, using DOW_IN_MONTH_MODE rules,
  773.      * in the "required" section, followed by the full rules, in packed format,
  774.      * in the "optional" section.  The optional section will be ignored by 1.1
  775.      * FCS code upon stream in.
  776.      */
  777.     private void writeObject(ObjectOutputStream stream)
  778.          throws IOException
  779.     {
  780.         // Construct a binary rule
  781.         byte[] rules = packRules();
  782.  
  783.         // Convert to 1.1 FCS rules.  This step may cause us to lose information.
  784.         makeRulesCompatible();
  785.  
  786.         // Write out the 1.1 FCS rules
  787.         stream.defaultWriteObject();
  788.  
  789.         // Write out the binary rules in the optional data area of the stream.
  790.         stream.writeInt(rules.length);
  791.         stream.write(rules);
  792.  
  793.         // Recover the original rules.  This recovers the information lost
  794.         // by makeRulesCompatible.
  795.         unpackRules(rules);
  796.     }
  797.  
  798.     /**
  799.      * Read this object out to a serialization stream.  We handle both 1.1 FCS
  800.      * binary formats, and full formats with a packed byte array.
  801.      */
  802.     private void readObject(ObjectInputStream stream)
  803.          throws IOException, ClassNotFoundException
  804.     {
  805.         stream.defaultReadObject();
  806.  
  807.         if (serialVersionOnStream < 1)
  808.         {
  809.             // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
  810.             // startDayOfWeek and endDayOfWeek were usually uninitialized.  We can't do
  811.             // too much, so we assume SUNDAY, which actually works most of the time.
  812.             if (startDayOfWeek == 0) startDayOfWeek = Calendar.SUNDAY;
  813.             if (endDayOfWeek == 0) endDayOfWeek = Calendar.SUNDAY;
  814.  
  815.             // The variables dstSavings, startMode, and endMode are post-1.1, so they
  816.             // won't be present if we're reading from a 1.1 stream.  Fix them up.
  817.             startMode = endMode = DOW_IN_MONTH_MODE;
  818.             dstSavings = millisPerHour;
  819.         }
  820.         else
  821.         {
  822.             // For 1.1.4, in addition to the 3 new instance variables, we also
  823.             // store the actual rules (which have not be made compatible with 1.1)
  824.             // in the optional area.  Read them in here and parse them.
  825.             int length = stream.readInt();
  826.             byte[] rules = new byte[length];
  827.             stream.readFully(rules);
  828.             unpackRules(rules);
  829.         }
  830.  
  831.         serialVersionOnStream = currentSerialVersion;
  832.     }
  833. }
  834.  
  835. //eof
  836.