| /* java.util.SimpleTimeZone |
| Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package java.util; |
| |
| |
| /** |
| * This class represents a simple time zone offset and handles |
| * daylight savings. It can only handle one daylight savings rule, so |
| * it can't represent historical changes. |
| * |
| * This object is tightly bound to the Gregorian calendar. It assumes |
| * a regular seven days week, and the month lengths are that of the |
| * Gregorian Calendar. It can only handle daylight savings for years |
| * lying in the AD era. |
| * |
| * @see Calendar |
| * @see GregorianCalendar |
| * @author Jochen Hoenicke |
| */ |
| public class SimpleTimeZone extends TimeZone |
| { |
| /** |
| * The raw time zone offset in milliseconds to GMT, ignoring |
| * daylight savings. |
| * @serial |
| */ |
| private int rawOffset; |
| |
| /** |
| * True, if this timezone uses daylight savings, false otherwise. |
| * @serial |
| */ |
| private boolean useDaylight; |
| |
| /** |
| * The daylight savings offset. This is a positive offset in |
| * milliseconds with respect to standard time. Typically this |
| * is one hour, but for some time zones this may be half an hour. |
| * @serial |
| * @since JDK1.1.4 |
| */ |
| private int dstSavings = 60 * 60 * 1000; |
| |
| /** |
| * The first year, in which daylight savings rules applies. |
| * @serial |
| */ |
| private int startYear; |
| private static final int DOM_MODE = 1; |
| private static final int DOW_IN_MONTH_MODE = 2; |
| private static final int DOW_GE_DOM_MODE = 3; |
| private static final int DOW_LE_DOM_MODE = 4; |
| |
| /** |
| * The mode of the start rule. This takes one of the following values: |
| * <dl> |
| * <dt>DOM_MODE (1)</dt> |
| * <dd> startDay contains the day in month of the start date, |
| * startDayOfWeek is unused. </dd> |
| * <dt>DOW_IN_MONTH_MODE (2)</dt> |
| * <dd> The startDay gives the day of week in month, and |
| * startDayOfWeek the day of week. For example startDay=2 and |
| * startDayOfWeek=Calender.SUNDAY specifies that the change is on |
| * the second sunday in that month. You must make sure, that this |
| * day always exists (ie. don't specify the 5th sunday). |
| * </dd> |
| * <dt>DOW_GE_DOM_MODE (3)</dt> |
| * <dd> The start is on the first startDayOfWeek on or after |
| * startDay. For example startDay=13 and |
| * startDayOfWeek=Calendar.FRIDAY specifies that the daylight |
| * savings start on the first FRIDAY on or after the 13th of that |
| * Month. Make sure that the change is always in the given month, or |
| * the result is undefined. |
| * </dd> |
| * <dt>DOW_LE_DOM_MONTH (4)</dt> |
| * <dd> The start is on the first startDayOfWeek on or before the |
| * startDay. Make sure that the change is always in the given |
| * month, or the result is undefined. |
| </dd> |
| * </dl> |
| * @serial */ |
| private int startMode; |
| |
| /** |
| * The month in which daylight savings start. This is one of the |
| * constants Calendar.JANUARY, ..., Calendar.DECEMBER. |
| * @serial |
| */ |
| private int startMonth; |
| |
| /** |
| * This variable can have different meanings. See startMode for details |
| * @see #startMode |
| * @serial |
| */ |
| private int startDay; |
| |
| /** |
| * This variable specifies the day of week the change takes place. If |
| * startMode == DOM_MODE, this is undefined. |
| * @serial |
| * @see #startMode |
| */ |
| private int startDayOfWeek; |
| |
| /** |
| * This variable specifies the time of change to daylight savings. |
| * This time is given in milliseconds after midnight local |
| * standard time. |
| * @serial |
| */ |
| private int startTime; |
| |
| /** |
| * This variable specifies the mode that startTime is specified in. By |
| * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. For |
| * startTime, STANDARD_TIME and WALL_TIME are equivalent. |
| * @serial |
| */ |
| private int startTimeMode = WALL_TIME; |
| |
| /** |
| * The month in which daylight savings ends. This is one of the |
| * constants Calendar.JANUARY, ..., Calendar.DECEMBER. |
| * @serial |
| */ |
| private int endMonth; |
| |
| /** |
| * This variable gives the mode for the end of daylight savings rule. |
| * It can take the same values as startMode. |
| * @serial |
| * @see #startMode |
| */ |
| private int endMode; |
| |
| /** |
| * This variable can have different meanings. See startMode for details |
| * @serial |
| * @see #startMode |
| */ |
| private int endDay; |
| |
| /** |
| * This variable specifies the day of week the change takes place. If |
| * endMode == DOM_MODE, this is undefined. |
| * @serial |
| * @see #startMode |
| */ |
| private int endDayOfWeek; |
| |
| /** |
| * This variable specifies the time of change back to standard time. |
| * This time is given in milliseconds after midnight local |
| * standard time. |
| * @serial |
| */ |
| private int endTime; |
| |
| /** |
| * This variable specifies the mode that endTime is specified in. By |
| * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. |
| * @serial |
| */ |
| private int endTimeMode = WALL_TIME; |
| |
| /** |
| * This variable points to a deprecated array from JDK 1.1. It is |
| * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. |
| * The array contains the lengths of the months in the year and is |
| * assigned from a private static final field to avoid allocating |
| * the array for every instance of the object. |
| * Note that static final fields are not serialized. |
| * @serial |
| */ |
| private byte[] monthLength = monthArr; |
| private static final byte[] monthArr = |
| { |
| 31, 28, 31, 30, 31, 30, 31, 31, 30, |
| 31, 30, 31 |
| }; |
| |
| /** |
| * The version of the serialized data on the stream. |
| * <dl> |
| * <dt>0 or not present on stream</dt> |
| * <dd> JDK 1.1.3 or earlier, only provides this fields: |
| * rawOffset, startDay, startDayOfWeek, startMonth, startTime, |
| * startYear, endDay, endDayOfWeek, endMonth, endTime |
| * </dd> |
| * <dd> JDK 1.1.4 or later. This includes three new fields, namely |
| * startMode, endMode and dstSavings. And there is a optional section |
| * as described in writeObject. |
| * </dd> |
| * </dl> |
| * |
| * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old |
| * version. |
| * |
| * When streaming out this class it is always written in the latest |
| * version. |
| * @serial |
| * @since JDK1.1.4 |
| */ |
| private int serialVersionOnStream = 2; |
| private static final long serialVersionUID = -403250971215465050L; |
| |
| /** |
| * Constant to indicate that start and end times are specified in standard |
| * time, without adjusting for daylight savings. |
| */ |
| public static final int STANDARD_TIME = 1; |
| |
| /** |
| * Constant to indicate that start and end times are specified in wall |
| * time, adjusting for daylight savings. This is the default. |
| */ |
| public static final int WALL_TIME = 0; |
| |
| /** |
| * Constant to indicate that start and end times are specified in UTC. |
| */ |
| public static final int UTC_TIME = 2; |
| |
| /** |
| * Create a <code>SimpleTimeZone</code> with the given time offset |
| * from GMT and without daylight savings. |
| * @param rawOffset the time offset from GMT in milliseconds. |
| * @param id The identifier of this time zone. |
| */ |
| public SimpleTimeZone(int rawOffset, String id) |
| { |
| this.rawOffset = rawOffset; |
| setID(id); |
| useDaylight = false; |
| startYear = 0; |
| } |
| |
| /** |
| * Create a <code>SimpleTimeZone</code> with the given time offset |
| * from GMT and with daylight savings. The start/end parameters |
| * can have different meaning (replace WEEKDAY with a real day of |
| * week). Only the first two meanings were supported by earlier |
| * versions of jdk. |
| * |
| * <dl> |
| * <dt><code>day > 0, dayOfWeek = Calendar.WEEKDAY</code></dt> |
| * <dd>The start/end of daylight savings is on the <code>day</code>-th |
| * <code>WEEKDAY</code> in the given month. </dd> |
| * <dt><code>day < 0, dayOfWeek = Calendar.WEEKDAY</code></dt> |
| * <dd>The start/end of daylight savings is on the <code>-day</code>-th |
| * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd> |
| * <dt><code>day > 0, dayOfWeek = 0</code></dt> |
| * <dd>The start/end of daylight is on the <code>day</code>-th day of |
| * the month. </dd> |
| * <dt><code>day > 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> |
| * <dd>The start/end of daylight is on the first WEEKDAY on or after |
| * the <code>day</code>-th day of the month. You must make sure that |
| * this day lies in the same month. </dd> |
| * <dt><code>day < 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> |
| * <dd>The start/end of daylight is on the first WEEKDAY on or |
| * <i>before</i> the <code>-day</code>-th day of the month. You |
| * must make sure that this day lies in the same month. </dd> |
| * </dl> |
| * |
| * If you give a non existing month, a day that is zero, or too big, |
| * or a dayOfWeek that is too big, the result is undefined. |
| * |
| * The start rule must have a different month than the end rule. |
| * This restriction shouldn't hurt for all possible time zones. |
| * |
| * @param rawOffset The time offset from GMT in milliseconds. |
| * @param id The identifier of this time zone. |
| * @param startMonth The start month of daylight savings; use the |
| * constants in Calendar. |
| * @param startDayOfWeekInMonth A day in month or a day of week number, as |
| * described above. |
| * @param startDayOfWeek The start rule day of week; see above. |
| * @param startTime A time in millis in standard time. |
| * @param endMonth The end month of daylight savings; use the |
| * constants in Calendar. |
| * @param endDayOfWeekInMonth A day in month or a day of week number, as |
| * described above. |
| * @param endDayOfWeek The end rule day of week; see above. |
| * @param endTime A time in millis in standard time. |
| * @throws IllegalArgumentException if parameters are invalid or out of |
| * range. |
| */ |
| public SimpleTimeZone(int rawOffset, String id, int startMonth, |
| int startDayOfWeekInMonth, int startDayOfWeek, |
| int startTime, int endMonth, int endDayOfWeekInMonth, |
| int endDayOfWeek, int endTime) |
| { |
| this.rawOffset = rawOffset; |
| setID(id); |
| useDaylight = true; |
| |
| setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); |
| setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); |
| if (startMonth == endMonth) |
| throw new IllegalArgumentException("startMonth and endMonth must be different"); |
| this.startYear = 0; |
| } |
| |
| /** |
| * This constructs a new SimpleTimeZone that supports a daylight savings |
| * rule. The parameter are the same as for the constructor above, except |
| * there is the additional dstSavaings parameter. |
| * |
| * @param dstSavings the amount of savings for daylight savings |
| * time in milliseconds. This must be positive. |
| * @since 1.2 |
| */ |
| public SimpleTimeZone(int rawOffset, String id, int startMonth, |
| int startDayOfWeekInMonth, int startDayOfWeek, |
| int startTime, int endMonth, int endDayOfWeekInMonth, |
| int endDayOfWeek, int endTime, int dstSavings) |
| { |
| this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, |
| startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); |
| |
| this.dstSavings = dstSavings; |
| } |
| |
| /** |
| * This constructs a new SimpleTimeZone that supports a daylight savings |
| * rule. The parameter are the same as for the constructor above, except |
| * there are the additional startTimeMode, endTimeMode, and dstSavings |
| * parameters. |
| * |
| * @param startTimeMode the mode that start times are specified in. One of |
| * WALL_TIME, STANDARD_TIME, or UTC_TIME. |
| * @param endTimeMode the mode that end times are specified in. One of |
| * WALL_TIME, STANDARD_TIME, or UTC_TIME. |
| * @param dstSavings the amount of savings for daylight savings |
| * time in milliseconds. This must be positive. |
| * @throws IllegalArgumentException if parameters are invalid or out of |
| * range. |
| * @since 1.4 |
| */ |
| public SimpleTimeZone(int rawOffset, String id, int startMonth, |
| int startDayOfWeekInMonth, int startDayOfWeek, |
| int startTime, int startTimeMode, int endMonth, |
| int endDayOfWeekInMonth, int endDayOfWeek, |
| int endTime, int endTimeMode, int dstSavings) |
| { |
| this.rawOffset = rawOffset; |
| setID(id); |
| useDaylight = true; |
| |
| if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) |
| throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); |
| if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) |
| throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); |
| this.startTimeMode = startTimeMode; |
| this.endTimeMode = endTimeMode; |
| |
| setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); |
| setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); |
| if (startMonth == endMonth) |
| throw new IllegalArgumentException("startMonth and endMonth must be different"); |
| this.startYear = 0; |
| |
| this.dstSavings = dstSavings; |
| } |
| |
| /** |
| * Sets the first year, where daylight savings applies. The daylight |
| * savings rule never apply for years in the BC era. Note that this |
| * is gregorian calendar specific. |
| * @param year the start year. |
| */ |
| public void setStartYear(int year) |
| { |
| startYear = year; |
| useDaylight = true; |
| } |
| |
| /** |
| * Checks if the month, day, dayOfWeek arguments are in range and |
| * returns the mode of the rule. |
| * @param month the month parameter as in the constructor |
| * @param day the day parameter as in the constructor |
| * @param dayOfWeek the day of week parameter as in the constructor |
| * @return the mode of this rule see startMode. |
| * @exception IllegalArgumentException if parameters are out of range. |
| * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) |
| * @see #startMode |
| */ |
| private int checkRule(int month, int day, int dayOfWeek) |
| { |
| if (month < 0 || month > 11) |
| throw new IllegalArgumentException("month out of range"); |
| |
| int daysInMonth = getDaysInMonth(month, 1); |
| if (dayOfWeek == 0) |
| { |
| if (day <= 0 || day > daysInMonth) |
| throw new IllegalArgumentException("day out of range"); |
| return DOM_MODE; |
| } |
| else if (dayOfWeek > 0) |
| { |
| if (Math.abs(day) > (daysInMonth + 6) / 7) |
| throw new IllegalArgumentException("dayOfWeekInMonth out of range"); |
| if (dayOfWeek > Calendar.SATURDAY) |
| throw new IllegalArgumentException("dayOfWeek out of range"); |
| return DOW_IN_MONTH_MODE; |
| } |
| else |
| { |
| if (day == 0 || Math.abs(day) > daysInMonth) |
| throw new IllegalArgumentException("day out of range"); |
| if (dayOfWeek < -Calendar.SATURDAY) |
| throw new IllegalArgumentException("dayOfWeek out of range"); |
| if (day < 0) |
| return DOW_LE_DOM_MODE; |
| else |
| return DOW_GE_DOM_MODE; |
| } |
| } |
| |
| /** |
| * Sets the daylight savings start rule. You must also set the |
| * end rule with <code>setEndRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * @param month The month where daylight savings start, zero |
| * based. You should use the constants in Calendar. |
| * @param day A day of month or day of week in month. |
| * @param dayOfWeek The day of week where daylight savings start. |
| * @param time The time in milliseconds standard time where daylight |
| * savings start. |
| * @exception IllegalArgumentException if parameters are out of range. |
| * @see SimpleTimeZone |
| */ |
| public void setStartRule(int month, int day, int dayOfWeek, int time) |
| { |
| this.startMode = checkRule(month, day, dayOfWeek); |
| this.startMonth = month; |
| this.startDay = day; |
| this.startDayOfWeek = Math.abs(dayOfWeek); |
| if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) |
| this.startTime = time; |
| else |
| // Convert from UTC to STANDARD |
| this.startTime = time + this.rawOffset; |
| useDaylight = true; |
| } |
| |
| /** |
| * Sets the daylight savings start rule. You must also set the |
| * end rule with <code>setEndRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * Note that this API isn't incredibly well specified. It appears that the |
| * after flag must override the parameters, since normally, the day and |
| * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or |
| * before mode is chosen. But if after == true, this implementation |
| * overrides the signs of the other arguments. And if dayOfWeek == 0, it |
| * falls back to the behavior in the other APIs. I guess this should be |
| * checked against Sun's implementation. |
| * |
| * @param month The month where daylight savings start, zero |
| * based. You should use the constants in Calendar. |
| * @param day A day of month or day of week in month. |
| * @param dayOfWeek The day of week where daylight savings start. |
| * @param time The time in milliseconds standard time where daylight |
| * savings start. |
| * @param after If true, day and dayOfWeek specify first day of week on or |
| * after day, else first day of week on or before. |
| * @since 1.2 |
| * @see SimpleTimeZone |
| */ |
| public void setStartRule(int month, int day, int dayOfWeek, int time, |
| boolean after) |
| { |
| // FIXME: XXX: Validate that checkRule and offset processing work with on |
| // or before mode. |
| this.startDay = after ? Math.abs(day) : -Math.abs(day); |
| this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); |
| this.startMode = (dayOfWeek != 0) |
| ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) |
| : checkRule(month, day, dayOfWeek); |
| this.startDay = Math.abs(this.startDay); |
| this.startDayOfWeek = Math.abs(this.startDayOfWeek); |
| |
| this.startMonth = month; |
| |
| if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) |
| this.startTime = time; |
| else |
| // Convert from UTC to STANDARD |
| this.startTime = time + this.rawOffset; |
| useDaylight = true; |
| } |
| |
| /** |
| * Sets the daylight savings start rule. You must also set the |
| * end rule with <code>setEndRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * @param month The month where daylight savings start, zero |
| * based. You should use the constants in Calendar. |
| * @param day A day of month or day of week in month. |
| * @param time The time in milliseconds standard time where daylight |
| * savings start. |
| * @see SimpleTimeZone |
| * @since 1.2 |
| */ |
| public void setStartRule(int month, int day, int time) |
| { |
| setStartRule(month, day, 0, time); |
| } |
| |
| /** |
| * Sets the daylight savings end rule. You must also set the |
| * start rule with <code>setStartRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * @param month The end month of daylight savings. |
| * @param day A day in month, or a day of week in month. |
| * @param dayOfWeek A day of week, when daylight savings ends. |
| * @param time A time in millis in standard time. |
| * @see #setStartRule(int, int, int, int) |
| */ |
| public void setEndRule(int month, int day, int dayOfWeek, int time) |
| { |
| this.endMode = checkRule(month, day, dayOfWeek); |
| this.endMonth = month; |
| this.endDay = day; |
| this.endDayOfWeek = Math.abs(dayOfWeek); |
| if (this.endTimeMode == WALL_TIME) |
| this.endTime = time; |
| else if (this.endTimeMode == STANDARD_TIME) |
| // Convert from STANDARD to DST |
| this.endTime = time + this.dstSavings; |
| else |
| // Convert from UTC to DST |
| this.endTime = time + this.rawOffset + this.dstSavings; |
| useDaylight = true; |
| } |
| |
| /** |
| * Sets the daylight savings end rule. You must also set the |
| * start rule with <code>setStartRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * Note that this API isn't incredibly well specified. It appears that the |
| * after flag must override the parameters, since normally, the day and |
| * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or |
| * before mode is chosen. But if after == true, this implementation |
| * overrides the signs of the other arguments. And if dayOfWeek == 0, it |
| * falls back to the behavior in the other APIs. I guess this should be |
| * checked against Sun's implementation. |
| * |
| * @param month The end month of daylight savings. |
| * @param day A day in month, or a day of week in month. |
| * @param dayOfWeek A day of week, when daylight savings ends. |
| * @param time A time in millis in standard time. |
| * @param after If true, day and dayOfWeek specify first day of week on or |
| * after day, else first day of week on or before. |
| * @since 1.2 |
| * @see #setStartRule(int, int, int, int, boolean) |
| */ |
| public void setEndRule(int month, int day, int dayOfWeek, int time, |
| boolean after) |
| { |
| // FIXME: XXX: Validate that checkRule and offset processing work with on |
| // or before mode. |
| this.endDay = after ? Math.abs(day) : -Math.abs(day); |
| this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); |
| this.endMode = (dayOfWeek != 0) |
| ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) |
| : checkRule(month, day, dayOfWeek); |
| this.endDay = Math.abs(this.endDay); |
| this.endDayOfWeek = Math.abs(endDayOfWeek); |
| |
| this.endMonth = month; |
| |
| if (this.endTimeMode == WALL_TIME) |
| this.endTime = time; |
| else if (this.endTimeMode == STANDARD_TIME) |
| // Convert from STANDARD to DST |
| this.endTime = time + this.dstSavings; |
| else |
| // Convert from UTC to DST |
| this.endTime = time + this.rawOffset + this.dstSavings; |
| useDaylight = true; |
| } |
| |
| /** |
| * Sets the daylight savings end rule. You must also set the |
| * start rule with <code>setStartRule</code> or the result of |
| * getOffset is undefined. For the parameters see the ten-argument |
| * constructor above. |
| * |
| * @param month The end month of daylight savings. |
| * @param day A day in month, or a day of week in month. |
| * @param time A time in millis in standard time. |
| * @see #setStartRule(int, int, int) |
| */ |
| public void setEndRule(int month, int day, int time) |
| { |
| setEndRule(month, day, 0, time); |
| } |
| |
| /** |
| * Gets the time zone offset, for current date, modified in case of |
| * daylight savings. This is the offset to add to UTC to get the local |
| * time. |
| * |
| * In the standard JDK the results given by this method may result in |
| * inaccurate results at the end of February or the beginning of March. |
| * To avoid this, you should use Calendar instead: |
| * <code>offset = cal.get(Calendar.ZONE_OFFSET) |
| * + cal.get(Calendar.DST_OFFSET);</code> |
| * |
| * This version doesn't suffer this inaccuracy. |
| * |
| * The arguments don't follow the approach for setting start and end rules. |
| * The day must be a positive number and dayOfWeek must be a positive value |
| * from Calendar. dayOfWeek is redundant, but must match the other values |
| * or an inaccurate result may be returned. |
| * |
| * @param era the era of the given date |
| * @param year the year of the given date |
| * @param month the month of the given date, 0 for January. |
| * @param day the day of month |
| * @param dayOfWeek the day of week; this must match the other fields. |
| * @param millis the millis in the day (in local standard time) |
| * @return the time zone offset in milliseconds. |
| * @throws IllegalArgumentException if arguments are incorrect. |
| */ |
| public int getOffset(int era, int year, int month, int day, int dayOfWeek, |
| int millis) |
| { |
| int daysInMonth = getDaysInMonth(month, year); |
| if (day < 1 || day > daysInMonth) |
| throw new IllegalArgumentException("day out of range"); |
| if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) |
| throw new IllegalArgumentException("dayOfWeek out of range"); |
| if (month < Calendar.JANUARY || month > Calendar.DECEMBER) |
| throw new IllegalArgumentException("month out of range:" + month); |
| |
| // This method is called by Calendar, so we mustn't use that class. |
| int daylightSavings = 0; |
| if (useDaylight && era == GregorianCalendar.AD && year >= startYear) |
| { |
| // This does only work for Gregorian calendars :-( |
| // This is mainly because setStartYear doesn't take an era. |
| boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, |
| startMode, startMonth, startDay, |
| startDayOfWeek, startTime); |
| boolean beforeEnd = isBefore(year, month, day, dayOfWeek, |
| millis + dstSavings, |
| endMode, endMonth, endDay, endDayOfWeek, |
| endTime); |
| |
| if (startMonth < endMonth) |
| // use daylight savings, if the date is after the start of |
| // savings, and before the end of savings. |
| daylightSavings = afterStart && beforeEnd ? dstSavings : 0; |
| else |
| // use daylight savings, if the date is before the end of |
| // savings, or after the start of savings. |
| daylightSavings = beforeEnd || afterStart ? dstSavings : 0; |
| } |
| return rawOffset + daylightSavings; |
| } |
| |
| /** |
| * Returns the time zone offset to GMT in milliseconds, ignoring |
| * day light savings. |
| * @return the time zone offset. |
| */ |
| public int getRawOffset() |
| { |
| return rawOffset; |
| } |
| |
| /** |
| * Sets the standard time zone offset to GMT. |
| * @param rawOffset The time offset from GMT in milliseconds. |
| */ |
| public void setRawOffset(int rawOffset) |
| { |
| this.rawOffset = rawOffset; |
| } |
| |
| /** |
| * Gets the daylight savings offset. This is a positive offset in |
| * milliseconds with respect to standard time. Typically this |
| * is one hour, but for some time zones this may be half an our. |
| * @return the daylight savings offset in milliseconds. |
| * |
| * @since 1.2 |
| */ |
| public int getDSTSavings() |
| { |
| return dstSavings; |
| } |
| |
| /** |
| * Sets the daylight savings offset. This is a positive offset in |
| * milliseconds with respect to standard time. |
| * |
| * @param dstSavings the daylight savings offset in milliseconds. |
| * |
| * @since 1.2 |
| */ |
| public void setDSTSavings(int dstSavings) |
| { |
| if (dstSavings <= 0) |
| throw new IllegalArgumentException("illegal value for dstSavings"); |
| |
| this.dstSavings = dstSavings; |
| } |
| |
| /** |
| * Returns if this time zone uses daylight savings time. |
| * @return true, if we use daylight savings time, false otherwise. |
| */ |
| public boolean useDaylightTime() |
| { |
| return useDaylight; |
| } |
| |
| /** |
| * Returns the number of days in the given month. |
| * Uses gregorian rules prior to 1582 (The default and earliest cutover) |
| * @param month The month, zero based; use one of the Calendar constants. |
| * @param year The year. |
| */ |
| private int getDaysInMonth(int month, int year) |
| { |
| if (month == Calendar.FEBRUARY) |
| { |
| if ((year & 3) != 0) |
| return 28; |
| |
| // Assume default Gregorian cutover, |
| // all years prior to this must be Julian |
| if (year < 1582) |
| return 29; |
| |
| // Gregorian rules |
| return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28; |
| } |
| else |
| return monthArr[month]; |
| } |
| |
| /** |
| * Checks if the date given in calXXXX, is before the change between |
| * dst and standard time. |
| * @param calYear the year of the date to check (for leap day checking). |
| * @param calMonth the month of the date to check. |
| * @param calDayOfMonth the day of month of the date to check. |
| * @param calDayOfWeek the day of week of the date to check. |
| * @param calMillis the millis of day of the date to check (standard time). |
| * @param mode the change mode; same semantic as startMode. |
| * @param month the change month; same semantic as startMonth. |
| * @param day the change day; same semantic as startDay. |
| * @param dayOfWeek the change day of week; |
| * @param millis the change time in millis since midnight standard time. |
| * same semantic as startDayOfWeek. |
| * @return true, if cal is before the change, false if cal is on |
| * or after the change. |
| */ |
| private boolean isBefore(int calYear, int calMonth, int calDayOfMonth, |
| int calDayOfWeek, int calMillis, int mode, |
| int month, int day, int dayOfWeek, int millis) |
| { |
| // This method is called by Calendar, so we mustn't use that class. |
| // We have to do all calculations by hand. |
| // check the months: |
| // XXX - this is not correct: |
| // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may |
| // be in a different month. |
| if (calMonth != month) |
| return calMonth < month; |
| |
| // check the day: |
| switch (mode) |
| { |
| case DOM_MODE: |
| if (calDayOfMonth != day) |
| return calDayOfMonth < day; |
| break; |
| case DOW_IN_MONTH_MODE: |
| { |
| // This computes the day of month of the day of type |
| // "dayOfWeek" that lies in the same (sunday based) week as cal. |
| calDayOfMonth += (dayOfWeek - calDayOfWeek); |
| |
| // Now we convert it to 7 based number (to get a one based offset |
| // after dividing by 7). If we count from the end of the |
| // month, we get want a -7 based number counting the days from |
| // the end: |
| if (day < 0) |
| calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; |
| else |
| calDayOfMonth += 6; |
| |
| // day > 0 day < 0 |
| // S M T W T F S S M T W T F S |
| // 7 8 9 10 11 12 -36-35-34-33-32-31 |
| // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 |
| // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 |
| // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 |
| // 34 35 36 -9 -8 -7 |
| // Now we calculate the day of week in month: |
| int week = calDayOfMonth / 7; |
| |
| // day > 0 day < 0 |
| // S M T W T F S S M T W T F S |
| // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 |
| // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 |
| // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 |
| // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 |
| // 4 5 5 -1 -1 -1 |
| if (week != day) |
| return week < day; |
| |
| if (calDayOfWeek != dayOfWeek) |
| return calDayOfWeek < dayOfWeek; |
| |
| // daylight savings starts/ends on the given day. |
| break; |
| } |
| case DOW_LE_DOM_MODE: |
| // The greatest sunday before or equal December, 12 |
| // is the same as smallest sunday after or equal December, 6. |
| day = Math.abs(day) - 6; |
| case DOW_GE_DOM_MODE: |
| // Calculate the day of month of the day of type |
| // "dayOfWeek" that lies before (or on) the given date. |
| calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek |
| - dayOfWeek; |
| if (calDayOfMonth < day) |
| return true; |
| if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) |
| return false; |
| |
| // now we have the same day |
| break; |
| } |
| |
| // the millis decides: |
| return (calMillis < millis); |
| } |
| |
| /** |
| * Determines if the given date is in daylight savings time. |
| * @return true, if it is in daylight savings time, false otherwise. |
| */ |
| public boolean inDaylightTime(Date date) |
| { |
| Calendar cal = Calendar.getInstance(this); |
| cal.setTime(date); |
| return (cal.get(Calendar.DST_OFFSET) != 0); |
| } |
| |
| /** |
| * Generates the hashCode for the SimpleDateFormat object. It is |
| * the rawOffset, possibly, if useDaylightSavings is true, xored |
| * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. |
| */ |
| public synchronized int hashCode() |
| { |
| return rawOffset |
| ^ (useDaylight |
| ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth |
| ^ endDay ^ endDayOfWeek ^ endTime : 0); |
| } |
| |
| public synchronized boolean equals(Object o) |
| { |
| if (this == o) |
| return true; |
| if (! (o instanceof SimpleTimeZone)) |
| return false; |
| SimpleTimeZone zone = (SimpleTimeZone) o; |
| if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID()) |
| || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) |
| return false; |
| if (! useDaylight) |
| return true; |
| return (startYear == zone.startYear && startMonth == zone.startMonth |
| && startDay == zone.startDay |
| && startDayOfWeek == zone.startDayOfWeek |
| && startTime == zone.startTime |
| && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth |
| && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek |
| && endTime == zone.endTime && endTimeMode == zone.endTimeMode); |
| } |
| |
| /** |
| * Test if the other time zone uses the same rule and only |
| * possibly differs in ID. This implementation for this particular |
| * class will return true if the other object is a SimpleTimeZone, |
| * the raw offsets and useDaylight are identical and if useDaylight |
| * is true, also the start and end datas are identical. |
| * @return true if this zone uses the same rule. |
| */ |
| public boolean hasSameRules(TimeZone other) |
| { |
| if (this == other) |
| return true; |
| if (! (other instanceof SimpleTimeZone)) |
| return false; |
| SimpleTimeZone zone = (SimpleTimeZone) other; |
| if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset |
| || useDaylight != zone.useDaylight) |
| return false; |
| if (! useDaylight) |
| return true; |
| return (startYear == zone.startYear && startMonth == zone.startMonth |
| && startDay == zone.startDay |
| && startDayOfWeek == zone.startDayOfWeek |
| && startTime == zone.startTime |
| && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth |
| && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek |
| && endTime == zone.endTime && endTimeMode == zone.endTimeMode); |
| } |
| |
| /** |
| * Returns a string representation of this SimpleTimeZone object. |
| * @return a string representation of this SimpleTimeZone object. |
| */ |
| public String toString() |
| { |
| // the test for useDaylight is an incompatibility to jdk1.2, but |
| // I think this shouldn't hurt. |
| return getClass().getName() + "[" + "id=" + getID() + ",offset=" |
| + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight=" |
| + useDaylight |
| + (useDaylight |
| ? ",startYear=" + startYear + ",startMode=" + startMode |
| + ",startMonth=" + startMonth + ",startDay=" + startDay |
| + ",startDayOfWeek=" + startDayOfWeek + ",startTime=" |
| + startTime + ",startTimeMode=" + startTimeMode + ",endMode=" |
| + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay |
| + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime |
| + ",endTimeMode=" + endTimeMode : "") + "]"; |
| } |
| |
| /** |
| * Reads a serialized simple time zone from stream. |
| * @see #writeObject |
| */ |
| private void readObject(java.io.ObjectInputStream input) |
| throws java.io.IOException, ClassNotFoundException |
| { |
| input.defaultReadObject(); |
| if (serialVersionOnStream == 0) |
| { |
| // initialize the new fields to default values. |
| dstSavings = 60 * 60 * 1000; |
| endMode = DOW_IN_MONTH_MODE; |
| startMode = DOW_IN_MONTH_MODE; |
| startTimeMode = WALL_TIME; |
| endTimeMode = WALL_TIME; |
| serialVersionOnStream = 2; |
| } |
| else |
| { |
| int length = input.readInt(); |
| byte[] byteArray = new byte[length]; |
| input.read(byteArray, 0, length); |
| if (length >= 4) |
| { |
| // Lets hope that Sun does extensions to the serialized |
| // form in a sane manner. |
| startDay = byteArray[0]; |
| startDayOfWeek = byteArray[1]; |
| endDay = byteArray[2]; |
| endDayOfWeek = byteArray[3]; |
| } |
| } |
| } |
| |
| /** |
| * Serializes this object to a stream. @serialdata The object is |
| * first written in the old JDK 1.1 format, so that it can be read |
| * by by the old classes. This means, that the |
| * <code>start/endDay(OfWeek)</code>-Fields are written in the |
| * DOW_IN_MONTH_MODE rule, since this was the only supported rule |
| * in 1.1. |
| * |
| * In the optional section, we write first the length of an byte |
| * array as int and afterwards the byte array itself. The byte |
| * array contains in this release four elements, namely the real |
| * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. |
| * These fields are needed, because for compatibility reasons only |
| * approximative values are written to the required section, as |
| * described above. |
| */ |
| private void writeObject(java.io.ObjectOutputStream output) |
| throws java.io.IOException |
| { |
| byte[] byteArray = new byte[] |
| { |
| (byte) startDay, (byte) startDayOfWeek, (byte) endDay, |
| (byte) endDayOfWeek |
| }; |
| |
| /* calculate the approximation for JDK 1.1 */ |
| switch (startMode) |
| { |
| case DOM_MODE: |
| startDayOfWeek = Calendar.SUNDAY; // random day of week |
| |
| // fall through |
| case DOW_GE_DOM_MODE: |
| case DOW_LE_DOM_MODE: |
| startDay = (startDay + 6) / 7; |
| } |
| switch (endMode) |
| { |
| case DOM_MODE: |
| endDayOfWeek = Calendar.SUNDAY; |
| |
| // fall through |
| case DOW_GE_DOM_MODE: |
| case DOW_LE_DOM_MODE: |
| endDay = (endDay + 6) / 7; |
| } |
| |
| // the required part: |
| output.defaultWriteObject(); |
| // the optional part: |
| output.writeInt(byteArray.length); |
| output.write(byteArray, 0, byteArray.length); |
| } |
| } |