| /* 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); | |
| } | |
| } |