blob: c8ffcd310d5ac0c2ffe83085abc498c57e2028fc [file] [log] [blame]
/*
* Copyright (C) 1996-2008, International Business Machines
* Corporation and others. All Rights Reserved.
*/
package com.ibm.icu.util;
import java.io.IOException;
import java.util.Date;
import com.ibm.icu.impl.Grego;
/**
* <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
* that represents a time zone for use with a Gregorian calendar. This
* class does not handle historical changes.
*
* <P>
* Use a negative value for <code>dayOfWeekInMonth</code> to indicate that
* <code>SimpleTimeZone</code> should count from the end of the month backwards.
* For example, Daylight Savings Time ends at the last
* (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
*
* @see Calendar
* @see GregorianCalendar
* @see TimeZone
* @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
* @stable ICU 2.0
*/
public class SimpleTimeZone extends BasicTimeZone {
private static final long serialVersionUID = -7034676239311322769L;
/**
* Constant for a mode of start or end time specified as local wall time.
* @stable ICU 3.8
*/
public static final int WALL_TIME = 0;
/**
* Constant for a mode of start or end time specified as local standard time.
* @stable ICU 3.8
*/
public static final int STANDARD_TIME = 1;
/**
* Constant for a mode of start or end time specified as UTC.
* @stable ICU 3.8
*/
public static final int UTC_TIME = 2;
/**
* Constructs a SimpleTimeZone with the given base time zone offset from GMT
* and time zone ID. Timezone IDs can be obtained from
* TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to
* construct a TimeZone.
*
* @param rawOffset The given base time zone offset to GMT.
* @param ID The time zone ID which is obtained from
* TimeZone.getAvailableIDs.
* @stable ICU 2.0
*/
public SimpleTimeZone(int rawOffset, String ID) {
construct(rawOffset, 0, 0, 0,
0, WALL_TIME,
0, 0, 0,
0, WALL_TIME,
Grego.MILLIS_PER_HOUR);
super.setID(ID);
}
/**
* Construct a SimpleTimeZone with the given base time zone offset from
* GMT, time zone ID, time to start and end the daylight time. Timezone IDs
* can be obtained from TimeZone.getAvailableIDs. Normally you should use
* TimeZone.getDefault to create a TimeZone. For a time zone that does not
* use daylight saving time, do not use this constructor; instead you should
* use SimpleTimeZone(rawOffset, ID).
*
* By default, this constructor specifies day-of-week-in-month rules. That
* is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this
* indicates the first Sunday in the startMonth. A startDay of -1 likewise
* indicates the last Sunday. However, by using negative or zero values for
* certain parameters, other types of rules can be specified.
*
* Day of month. To specify an exact day of the month, such as March 1, set
* startDayOfWeek to zero.
*
* Day of week after day of month. To specify the first day of the week
* occurring on or after an exact day of the month, make the day of the week
* negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY,
* this indicates the first Monday on or after the 5th day of the
* startMonth.
*
* Day of week before day of month. To specify the last day of the week
* occurring on or before an exact day of the month, make the day of the
* week and the day of the month negative. For example, if startDay is -21
* and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or
* before the 21st of the startMonth.
*
* The above examples refer to the startMonth, startDay, and startDayOfWeek;
* the same applies for the endMonth, endDay, and endDayOfWeek.
*
* @param rawOffset The given base time zone offset to GMT.
* @param ID The time zone ID which is obtained from
* TimeZone.getAvailableIDs.
* @param startMonth The daylight savings starting month. Month is
* 0-based. eg, 0 for January.
* @param startDay The daylight savings starting
* day-of-week-in-month. Please see the member
* description for an example.
* @param startDayOfWeek The daylight savings starting day-of-week. Please
* see the member description for an example.
* @param startTime The daylight savings starting time in local wall
* time, which is standard time in this case. Please see the
* member description for an example.
* @param endMonth The daylight savings ending month. Month is
* 0-based. eg, 0 for January.
* @param endDay The daylight savings ending day-of-week-in-month.
* Please see the member description for an example.
* @param endDayOfWeek The daylight savings ending day-of-week. Please
* see the member description for an example.
* @param endTime The daylight savings ending time in local wall time,
* which is daylight time in this case. Please see the
* member description for an example.
* @exception IllegalArgumentException the month, day, dayOfWeek, or time
* parameters are out of range for the start or end rule
* @stable ICU 2.0
*/
public SimpleTimeZone(int rawOffset, String ID,
int startMonth, int startDay, int startDayOfWeek, int startTime,
int endMonth, int endDay, int endDayOfWeek, int endTime) {
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, WALL_TIME,
endMonth, endDay, endDayOfWeek,
endTime, WALL_TIME,
Grego.MILLIS_PER_HOUR);
super.setID(ID);
}
/**
* Construct a SimpleTimeZone with the given base time zone offset from
* GMT, time zone ID, time and its mode to start and end the daylight time.
* The mode specifies either {@link #WALL_TIME} or {@link #STANDARD_TIME}
* or {@link #UTC_TIME}.
*
* @param rawOffset The given base time zone offset to GMT.
* @param ID The time zone ID which is obtained from
* TimeZone.getAvailableIDs.
* @param startMonth The daylight savings starting month. Month is
* 0-based. eg, 0 for January.
* @param startDay The daylight savings starting
* day-of-week-in-month. Please see the member
* description for an example.
* @param startDayOfWeek The daylight savings starting day-of-week. Please
* see the member description for an example.
* @param startTime The daylight savings starting time in local wall
* time, which is standard time in this case. Please see the
* member description for an example.
* @param startTimeMode The mode of the start time specified by startTime.
* @param endMonth The daylight savings ending month. Month is
* 0-based. eg, 0 for January.
* @param endDay The daylight savings ending day-of-week-in-month.
* Please see the member description for an example.
* @param endDayOfWeek The daylight savings ending day-of-week. Please
* see the member description for an example.
* @param endTime The daylight savings ending time in local wall time,
* which is daylight time in this case. Please see the
* member description for an example.
* @param endTimeMode The mode of the end time specified by endTime.
* @param dstSavings The amount of time in ms saved during DST.
* @exception IllegalArgumentException the month, day, dayOfWeek, or time
* parameters are out of range for the start or end rule
* @stable ICU 3.8
*/
public SimpleTimeZone(int rawOffset, String ID,
int startMonth, int startDay,
int startDayOfWeek, int startTime,
int startTimeMode,
int endMonth, int endDay,
int endDayOfWeek, int endTime,
int endTimeMode,int dstSavings){
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, startTimeMode,
endMonth, endDay, endDayOfWeek,
endTime, endTimeMode,
dstSavings);
super.setID(ID);
}
/**
* Constructor. This constructor is identical to the 10-argument
* constructor, but also takes a dstSavings parameter.
* @param rawOffset The given base time zone offset to GMT.
* @param ID The time zone ID which is obtained from
* TimeZone.getAvailableIDs.
* @param startMonth The daylight savings starting month. Month is
* 0-based. eg, 0 for January.
* @param startDay The daylight savings starting
* day-of-week-in-month. Please see the member
* description for an example.
* @param startDayOfWeek The daylight savings starting day-of-week. Please
* see the member description for an example.
* @param startTime The daylight savings starting time in local wall
* time, which is standard time in this case. Please see the
* member description for an example.
* @param endMonth The daylight savings ending month. Month is
* 0-based. eg, 0 for January.
* @param endDay The daylight savings ending day-of-week-in-month.
* Please see the member description for an example.
* @param endDayOfWeek The daylight savings ending day-of-week. Please
* see the member description for an example.
* @param endTime The daylight savings ending time in local wall time,
* which is daylight time in this case. Please see the
* member description for an example.
* @param dstSavings The amount of time in ms saved during DST.
* @exception IllegalArgumentException the month, day, dayOfWeek, or time
* parameters are out of range for the start or end rule
* @stable ICU 2.0
*/
public SimpleTimeZone(int rawOffset, String ID,
int startMonth, int startDay, int startDayOfWeek, int startTime,
int endMonth, int endDay, int endDayOfWeek, int endTime,
int dstSavings) {
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, WALL_TIME,
endMonth, endDay, endDayOfWeek,
endTime, WALL_TIME,
dstSavings);
super.setID(ID);
}
/**
* {@inheritDoc}
*
* @stable ICU 3.8
*/
public void setID(String ID) {
super.setID(ID);
transitionRulesInitialized = false;
}
/**
* Overrides TimeZone
* Sets the base time zone offset to GMT.
* This is the offset to add "to" UTC to get local time.
* @param offsetMillis the raw offset of the time zone
* @stable ICU 2.0
*/
public void setRawOffset(int offsetMillis) {
raw = offsetMillis;
transitionRulesInitialized = false;
}
/**
* Overrides TimeZone
* Gets the GMT offset for this time zone.
* @return the raw offset
* @stable ICU 2.0
*/
public int getRawOffset() {
return raw;
}
/**
* Sets the daylight savings starting year.
*
* @param year The daylight savings starting year.
* @stable ICU 2.0
*/
public void setStartYear(int year) {
//unwrapSTZ().setStartYear(year);
getSTZInfo().sy = year;
this.startYear = year;
transitionRulesInitialized = false;
}
/**
* Sets the daylight savings starting rule. For example, Daylight Savings
* Time starts at the first Sunday in April, at 2 AM in standard time.
* Therefore, you can set the start rule by calling:
* setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
*
* @param month The daylight savings starting month. Month is
* 0-based. eg, 0 for January.
* @param dayOfWeekInMonth The daylight savings starting
* day-of-week-in-month. Please see the member
* description for an example.
* @param dayOfWeek The daylight savings starting day-of-week.
* Please see the member description for an
* example.
* @param time The daylight savings starting time in local wall
* time, which is standard time in this case. Please see
* the member description for an example.
* @exception IllegalArgumentException the month, dayOfWeekInMonth,
* dayOfWeek, or time parameters are out of range
* @stable ICU 2.0
*/
public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek,
int time) {
getSTZInfo().setStart(month, dayOfWeekInMonth, dayOfWeek, time, -1, false);
setStartRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME);
}
/**
* Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
* Time starts at the first Sunday in April, at 2 AM in standard time.
* Therefore, you can set the start rule by calling:
* setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
* The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
* the exact starting date. Their exact meaning depend on their respective signs,
* allowing various types of rules to be constructed, as follows:<ul>
* <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
* day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
* of the month).
* <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
* the day of week in the month counting backward from the end of the month.
* (e.g., (-1, MONDAY) is the last Monday in the month)
* <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
* specifies the day of the month, regardless of what day of the week it is.
* (e.g., (10, 0) is the tenth day of the month)
* <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
* specifies the day of the month counting backward from the end of the
* month, regardless of what day of the week it is (e.g., (-2, 0) is the
* next-to-last day of the month).
* <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
* first specified day of the week on or after the specfied day of the month.
* (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
* [or the 15th itself if the 15th is a Sunday].)
* <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
* last specified day of the week on or before the specified day of the month.
* (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
* [or the 20th itself if the 20th is a Tuesday].)</ul>
* @param month the daylight savings starting month. Month is 0-based.
* eg, 0 for January.
* @param dayOfWeekInMonth the daylight savings starting
* day-of-week-in-month. Please see the member description for an example.
* @param dayOfWeek the daylight savings starting day-of-week. Please see
* the member description for an example.
* @param time the daylight savings starting time. Please see the member
* description for an example.
*/
private void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode)
{
startMonth = month;
startDay = dayOfWeekInMonth;
startDayOfWeek = dayOfWeek;
startTime = time;
startTimeMode = mode;
decodeStartRule();
transitionRulesInitialized = false;
}
/**
* Sets the DST start rule to a fixed date within a month.
*
* @param month The month in which this rule occurs (0-based).
* @param dayOfMonth The date in that month (1-based).
* @param time The time of that day (number of millis after midnight)
* when DST takes effect in local wall time, which is
* standard time in this case.
* @exception IllegalArgumentException the month,
* dayOfMonth, or time parameters are out of range
* @stable ICU 2.0
*/
public void setStartRule(int month, int dayOfMonth, int time) {
// unwrapSTZ().setStartRule(month, dayOfMonth, time);
getSTZInfo().setStart(month, -1, -1, time, dayOfMonth, false);
setStartRule(month, dayOfMonth, 0, time, WALL_TIME);
}
/**
* Sets the DST start rule to a weekday before or after a give date within
* a month, e.g., the first Monday on or after the 8th.
*
* @param month The month in which this rule occurs (0-based).
* @param dayOfMonth A date within that month (1-based).
* @param dayOfWeek The day of the week on which this rule occurs.
* @param time The time of that day (number of millis after midnight)
* when DST takes effect in local wall time, which is
* standard time in this case.
* @param after If true, this rule selects the first dayOfWeek on
* or after dayOfMonth. If false, this rule selects
* the last dayOfWeek on or before dayOfMonth.
* @exception IllegalArgumentException the month, dayOfMonth,
* dayOfWeek, or time parameters are out of range
* @stable ICU 2.0
*/
public void setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) {
getSTZInfo().setStart(month, -1, dayOfWeek, time, dayOfMonth, after);
setStartRule(month, after ? dayOfMonth : -dayOfMonth,
-dayOfWeek, time, WALL_TIME);
}
/**
* Sets the daylight savings ending rule. For example, Daylight Savings Time
* ends at the last (-1) Sunday in October, at 2 AM in standard time.
* Therefore, you can set the end rule by calling:
* setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
*
* @param month The daylight savings ending month. Month is
* 0-based. eg, 0 for January.
* @param dayOfWeekInMonth The daylight savings ending
* day-of-week-in-month. Please see the member
* description for an example.
* @param dayOfWeek The daylight savings ending day-of-week. Please
* see the member description for an example.
* @param time The daylight savings ending time in local wall time,
* which is daylight time in this case. Please see the
* member description for an example.
* @exception IllegalArgumentException the month, dayOfWeekInMonth,
* dayOfWeek, or time parameters are out of range
* @stable ICU 2.0
*/
public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time) {
getSTZInfo().setEnd(month, dayOfWeekInMonth, dayOfWeek, time, -1, false);
setEndRule(month, dayOfWeekInMonth, dayOfWeek, time, WALL_TIME);
}
/**
* Sets the DST end rule to a fixed date within a month.
*
* @param month The month in which this rule occurs (0-based).
* @param dayOfMonth The date in that month (1-based).
* @param time The time of that day (number of millis after midnight)
* when DST ends in local wall time, which is daylight
* time in this case.
* @exception IllegalArgumentException the month,
* dayOfMonth, or time parameters are out of range
* @stable ICU 2.0
*/
public void setEndRule(int month, int dayOfMonth, int time) {
getSTZInfo().setEnd(month, -1, -1, time, dayOfMonth, false);
setEndRule(month, dayOfMonth, WALL_TIME, time);
}
/**
* Sets the DST end rule to a weekday before or after a give date within
* a month, e.g., the first Monday on or after the 8th.
*
* @param month The month in which this rule occurs (0-based).
* @param dayOfMonth A date within that month (1-based).
* @param dayOfWeek The day of the week on which this rule occurs.
* @param time The time of that day (number of millis after midnight)
* when DST ends in local wall time, which is daylight
* time in this case.
* @param after If true, this rule selects the first dayOfWeek on
* or after dayOfMonth. If false, this rule selects
* the last dayOfWeek on or before dayOfMonth.
* @exception IllegalArgumentException the month, dayOfMonth,
* dayOfWeek, or time parameters are out of range
* @stable ICU 2.0
*/
public void setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after) {
getSTZInfo().setEnd(month, -1, dayOfWeek, time, dayOfMonth, after);
setEndRule(month, dayOfMonth, dayOfWeek, time, WALL_TIME, after);
}
private void setEndRule(int month, int dayOfMonth, int dayOfWeek,
int time, int mode, boolean after){
setEndRule(month, after ? dayOfMonth : -dayOfMonth, -dayOfWeek, time, mode);
}
/**
* Sets the daylight savings ending rule. For example, in the U.S., Daylight
* Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
* Therefore, you can set the end rule by calling:
* setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
* Various other types of rules can be specified by manipulating the dayOfWeek
* and dayOfWeekInMonth parameters. For complete details, see the documentation
* for setStartRule().
* @param month the daylight savings ending month. Month is 0-based.
* eg, 0 for January.
* @param dayOfWeekInMonth the daylight savings ending
* day-of-week-in-month. See setStartRule() for a complete explanation.
* @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
* for a complete explanation.
* @param time the daylight savings ending time. Please see the member
* description for an example.
*/
private void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek, int time, int mode){
endMonth = month;
endDay = dayOfWeekInMonth;
endDayOfWeek = dayOfWeek;
endTime = time;
endTimeMode = mode;
decodeEndRule();
transitionRulesInitialized = false;
}
/**
* Sets the amount of time in ms that the clock is advanced during DST.
* @param millisSavedDuringDST the number of milliseconds the time is
* advanced with respect to standard time when the daylight savings rules
* are in effect. A positive number, typically one hour (3600000).
* @stable ICU 2.0
*/
public void setDSTSavings(int millisSavedDuringDST) {
if (millisSavedDuringDST <= 0) {
throw new IllegalArgumentException();
}
dst = millisSavedDuringDST;
transitionRulesInitialized = false;
}
/**
* Returns the amount of time in ms that the clock is advanced during DST.
* @return the number of milliseconds the time is
* advanced with respect to standard time when the daylight savings rules
* are in effect. A positive number, typically one hour (3600000).
* @stable ICU 2.0
*/
public int getDSTSavings() {
return dst;
}
/**
* Returns the java.util.SimpleTimeZone that this class wraps.
*
java.util.SimpleTimeZone unwrapSTZ() {
return (java.util.SimpleTimeZone) unwrap();
}
*/
// on JDK 1.4 and later, can't deserialize a SimpleTimeZone as a SimpleTimeZone...
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
/*
String id = getID();
if (id!=null && !(zone instanceof java.util.SimpleTimeZone && zone.getID().equals(id))) {
// System.out.println("*** readjust " + zone.getClass().getName() + " " + zone.getID() + " ***");
java.util.SimpleTimeZone stz =
new java.util.SimpleTimeZone(raw, id);
if (dst != 0) {
stz.setDSTSavings(dst);
// if it is 0, then there shouldn't be start/end rules and the default
// behavior should be no dst
}
if (xinfo != null) {
xinfo.applyTo(stz);
}
zoneJDK = stz;
}
*/
/* set all instance variables in this object
* to the values in zone
*/
if (xinfo != null) {
xinfo.applyTo(this);
}
}
/**
* Returns a string representation of this object.
* @return a string representation of this object
* @stable ICU 3.6
*/
public String toString() {
return "SimpleTimeZone: " + getID();
}
private STZInfo getSTZInfo() {
if (xinfo == null) {
xinfo = new STZInfo();
}
return xinfo;
}
// Use only for decodeStartRule() and decodeEndRule() where the year is not
// available. Set February to 29 days to accomodate rules with that date
// and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
// The compareToRule() method adjusts to February 28 in non-leap years.
//
// For actual getOffset() calculations, use TimeZone::monthLength() and
// TimeZone::previousMonthLength() which take leap years into account.
// We handle leap years assuming always
// Gregorian, since we know they didn't have daylight time when
// Gregorian calendar started.
private final static byte staticMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
/**
* {@inheritDoc}
* @stable ICU 2.0
*/
public int getOffset(int era, int year, int month, int day,
int dayOfWeek, int millis)
{
// Check the month before calling Grego.monthLength(). This
// duplicates the test that occurs in the 7-argument getOffset(),
// however, this is unavoidable. We don't mind because this method, in
// fact, should not be called; internal code should always call the
// 7-argument getOffset(), and outside code should use Calendar.get(int
// field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
// this method because it's public API. - liu 8/10/98
if(month < Calendar.JANUARY || month > Calendar.DECEMBER) {
throw new IllegalArgumentException();
}
return getOffset(era, year, month, day, dayOfWeek, millis, Grego.monthLength(year, month));
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
public int getOffset(int era, int year, int month, int day,
int dayOfWeek, int millis,
int monthLength) {
// Check the month before calling Grego.monthLength(). This
// duplicates a test that occurs in the 9-argument getOffset(),
// however, this is unavoidable. We don't mind because this method, in
// fact, should not be called; internal code should always call the
// 9-argument getOffset(), and outside code should use Calendar.get(int
// field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
// this method because it's public API. - liu 8/10/98
if(month < Calendar.JANUARY || month > Calendar.DECEMBER) {
throw new IllegalArgumentException();
}
return getOffset(era, year, month, day, dayOfWeek, millis,
Grego.monthLength(year, month), Grego.previousMonthLength(year, month));
}
int getOffset(int era, int year, int month, int day,
int dayOfWeek, int millis,
int monthLength, int prevMonthLength ){
if (true) {
/* Use this parameter checking code for normal operation. Only one
* of these two blocks should actually get compiled into the class
* file. */
if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
|| month < Calendar.JANUARY
|| month > Calendar.DECEMBER
|| day < 1
|| day > monthLength
|| dayOfWeek < Calendar.SUNDAY
|| dayOfWeek > Calendar.SATURDAY
|| millis < 0
|| millis >= Grego.MILLIS_PER_DAY
|| monthLength < 28
|| monthLength > 31
|| prevMonthLength < 28
|| prevMonthLength > 31) {
throw new IllegalArgumentException();
}
} else {
/* This parameter checking code is better for debugging, but
* overkill for normal operation. Only one of these two blocks
* should actually get compiled into the class file. */
if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
throw new IllegalArgumentException("Illegal era " + era);
}
if (month < Calendar.JANUARY
|| month > Calendar.DECEMBER) {
throw new IllegalArgumentException("Illegal month " + month);
}
if (day < 1
|| day > monthLength) {
throw new IllegalArgumentException("Illegal day " + day+" max month len: "+monthLength);
}
if (dayOfWeek < Calendar.SUNDAY
|| dayOfWeek > Calendar.SATURDAY) {
throw new IllegalArgumentException("Illegal day of week " + dayOfWeek);
}
if (millis < 0
|| millis >= Grego.MILLIS_PER_DAY) {
throw new IllegalArgumentException("Illegal millis " + millis);
}
if (monthLength < 28
|| monthLength > 31) {
throw new IllegalArgumentException("Illegal month length " + monthLength);
}
if (prevMonthLength < 28
|| prevMonthLength > 31) {
throw new IllegalArgumentException("Illegal previous month length " + prevMonthLength);
}
}
int result = raw;
// Bail out if we are before the onset of daylight savings time
if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result;
// Check for southern hemisphere. We assume that the start and end
// month are different.
boolean southern = (startMonth > endMonth);
// Compare the date to the starting and ending rules.+1 = date>rule, -1
// = date<rule, 0 = date==rule.
int startCompare = compareToRule(month, monthLength, prevMonthLength,
day, dayOfWeek, millis,
startTimeMode == UTC_TIME ? -raw : 0,
startMode, startMonth, startDayOfWeek,
startDay, startTime);
int endCompare = 0;
/* We don't always have to compute endCompare. For many instances,
* startCompare is enough to determine if we are in DST or not. In the
* northern hemisphere, if we are before the start rule, we can't have
* DST. In the southern hemisphere, if we are after the start rule, we
* must have DST. This is reflected in the way the next if statement
* (not the one immediately following) short circuits. */
if (southern != (startCompare >= 0)) {
/* For the ending rule comparison, we add the dstSavings to the millis
* passed in to convert them from standard to wall time. We then must
* normalize the millis to the range 0..millisPerDay-1. */
endCompare = compareToRule(month, monthLength, prevMonthLength,
day, dayOfWeek, millis,
endTimeMode == WALL_TIME ? dst :
(endTimeMode == UTC_TIME ? -raw : 0),
endMode, endMonth, endDayOfWeek,
endDay, endTime);
}
// Check for both the northern and southern hemisphere cases. We
// assume that in the northern hemisphere, the start rule is before the
// end rule within the calendar year, and vice versa for the southern
// hemisphere.
if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
(southern && (startCompare >= 0 || endCompare < 0)))
result += dst;
return result;
}
/**
* {@inheritDoc}
* @internal
* @deprecated This API is ICU internal only.
*/
public void getOffsetFromLocal(long date,
int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) {
offsets[0] = getRawOffset();
int fields[] = new int[6];
Grego.timeToFields(date, fields);
offsets[1] = getOffset(GregorianCalendar.AD,
fields[0], fields[1], fields[2],
fields[3], fields[5]) - offsets[0];
boolean recalc = false;
// Now, we need some adjustment
if (offsets[1] > 0) {
if ((nonExistingTimeOpt & STD_DST_MASK) == LOCAL_STD
|| (nonExistingTimeOpt & STD_DST_MASK) != LOCAL_DST && (nonExistingTimeOpt & FORMER_LATTER_MASK) != LOCAL_LATTER) {
date -= getDSTSavings();
recalc = true;
}
} else {
if ((duplicatedTimeOpt & STD_DST_MASK) == LOCAL_DST
|| (duplicatedTimeOpt & STD_DST_MASK) != LOCAL_STD && (duplicatedTimeOpt & FORMER_LATTER_MASK) == LOCAL_FORMER) {
date -= getDSTSavings();
recalc = true;
}
}
if (recalc) {
Grego.timeToFields(date, fields);
offsets[1] = getOffset(GregorianCalendar.AD,
fields[0], fields[1], fields[2],
fields[3], fields[5]) - offsets[0];
}
}
private static final int
DOM_MODE = 1,
DOW_IN_MONTH_MODE=2,
DOW_GE_DOM_MODE=3,
DOW_LE_DOM_MODE=4;
/**
* Compare a given date in the year to a rule. Return 1, 0, or -1, depending
* on whether the date is after, equal to, or before the rule date. The
* millis are compared directly against the ruleMillis, so any
* standard-daylight adjustments must be handled by the caller.
*
* @return 1 if the date is after the rule date, -1 if the date is before
* the rule date, or 0 if the date is equal to the rule date.
*/
private int compareToRule(int month, int monthLen, int prevMonthLen,
int dayOfMonth,
int dayOfWeek, int millis, int millisDelta,
int ruleMode, int ruleMonth, int ruleDayOfWeek,
int ruleDay, int ruleMillis)
{
// Make adjustments for startTimeMode and endTimeMode
millis += millisDelta;
while (millis >= Grego.MILLIS_PER_DAY) {
millis -= Grego.MILLIS_PER_DAY;
++dayOfMonth;
dayOfWeek = 1 + (dayOfWeek % 7); // dayOfWeek is one-based
if (dayOfMonth > monthLen) {
dayOfMonth = 1;
/* When incrementing the month, it is desirable to overflow
* from DECEMBER to DECEMBER+1, since we use the result to
* compare against a real month. Wraparound of the value
* leads to bug 4173604. */
++month;
}
}
/*
* For some reasons, Sun Java 6 on Solaris/Linux has a problem with
* the while loop below (at least Java 6 up to build 1.6.0_02-b08).
* It looks the JRE messes up the variable 'millis' while executing
* the code in the while block. The problem is not reproduced with
* JVM option -Xint, that is, it is likely a bug of the HotSpot
* adaptive compiler. Moving 'millis += Grego.MILLIS_PER_DAY'
* to the end of this while block seems to resolve the problem.
* See ticket#5887 about the problem in detail.
*/
while (millis < 0) {
//millis += Grego.MILLIS_PER_DAY;
--dayOfMonth;
dayOfWeek = 1 + ((dayOfWeek+5) % 7); // dayOfWeek is one-based
if (dayOfMonth < 1) {
dayOfMonth = prevMonthLen;
--month;
}
millis += Grego.MILLIS_PER_DAY;
}
if (month < ruleMonth) return -1;
else if (month > ruleMonth) return 1;
int ruleDayOfMonth = 0;
// Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
if (ruleDay > monthLen) {
ruleDay = monthLen;
}
switch (ruleMode)
{
case DOM_MODE:
ruleDayOfMonth = ruleDay;
break;
case DOW_IN_MONTH_MODE:
// In this case ruleDay is the day-of-week-in-month
if (ruleDay > 0)
ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
(7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
else // Assume ruleDay < 0 here
{
ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
(7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
}
break;
case DOW_GE_DOM_MODE:
ruleDayOfMonth = ruleDay +
(49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
break;
case DOW_LE_DOM_MODE:
ruleDayOfMonth = ruleDay -
(49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
// Note at this point ruleDayOfMonth may be <1, although it will
// be >=1 for well-formed rules.
break;
}
if (dayOfMonth < ruleDayOfMonth) return -1;
else if (dayOfMonth > ruleDayOfMonth) return 1;
if (millis < ruleMillis){
return -1;
}else if (millis > ruleMillis){
return 1;
}else{
return 0;
}
}
// data needed for streaming mutated SimpleTimeZones in JDK14
private int raw;// the TimeZone's raw GMT offset
private int dst = 3600000;
private STZInfo xinfo = null;
private int startMonth, startDay, startDayOfWeek; // the month, day, DOW, and time DST starts
private int startTime;
private int startTimeMode, endTimeMode; // Mode for startTime, endTime; see TimeMode
private int endMonth, endDay, endDayOfWeek; // the month, day, DOW, and time DST ends
private int endTime;
private int startYear; // the year these DST rules took effect
private boolean useDaylight; // flag indicating whether this TimeZone uses DST
private int startMode, endMode; // flags indicating what kind of rules the DST rules are
/**
* Overrides TimeZone
* Queries if this time zone uses Daylight Saving Time.
* @stable ICU 2.0
*/
public boolean useDaylightTime(){
return useDaylight;
}
/**
* Overrides TimeZone
* Queries if the give date is in Daylight Saving Time.
* @stable ICU 2.0
*/
public boolean inDaylightTime(Date date){
GregorianCalendar gc = new GregorianCalendar(this);
gc.setTime(date);
return gc.inDaylightTime();
}
/**
* Internal construction method.
*/
private void construct(int _raw,
int _startMonth,
int _startDay,
int _startDayOfWeek,
int _startTime,
int _startTimeMode,
int _endMonth,
int _endDay,
int _endDayOfWeek,
int _endTime,
int _endTimeMode,
int _dst) {
raw = _raw;
startMonth = _startMonth;
startDay = _startDay;
startDayOfWeek = _startDayOfWeek;
startTime = _startTime;
startTimeMode = _startTimeMode;
endMonth = _endMonth;
endDay = _endDay;
endDayOfWeek = _endDayOfWeek;
endTime = _endTime;
endTimeMode = _endTimeMode;
dst = _dst;
startYear = 0;
startMode = DOM_MODE;
endMode = DOM_MODE;
decodeRules();
if (_dst <= 0) {
throw new IllegalArgumentException();
}
}
private void decodeRules(){
decodeStartRule();
decodeEndRule();
}
/**
* Decode the start rule and validate the parameters. The parameters are
* expected to be in encoded form, which represents the various rule modes
* by negating or zeroing certain values. Representation formats are:
* <p>
* <pre>
* DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
* ------------ ----- -------- -------- ----------
* month 0..11 same same same don't care
* day -5..5 1..31 1..31 -1..-31 0
* dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
* time 0..ONEDAY same same same don't care
* </pre>
* The range for month does not include UNDECIMBER since this class is
* really specific to GregorianCalendar, which does not use that month.
* The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
* end rule is an exclusive limit point. That is, the range of times that
* are in DST include those >= the start and < the end. For this reason,
* it should be possible to specify an end of ONEDAY in order to include the
* entire day. Although this is equivalent to time 0 of the following day,
* it's not always possible to specify that, for example, on December 31.
* While arguably the start range should still be 0..ONEDAY-1, we keep
* the start and end ranges the same for consistency.
*/
private void decodeStartRule() {
useDaylight = (boolean)((startDay != 0) && (endDay != 0) ? true : false );
if (useDaylight && dst == 0) {
dst = Grego.MILLIS_PER_DAY;
}
if (startDay != 0) {
if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
throw new IllegalArgumentException();
}
if (startTime < 0 || startTime >= Grego.MILLIS_PER_DAY ||
startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
throw new IllegalArgumentException();
}
if (startDayOfWeek == 0) {
startMode = DOM_MODE;
} else {
if (startDayOfWeek > 0) {
startMode = DOW_IN_MONTH_MODE;
} else {
startDayOfWeek = (int)-startDayOfWeek;
if (startDay > 0) {
startMode = DOW_GE_DOM_MODE;
} else {
startDay = (int)-startDay;
startMode = DOW_LE_DOM_MODE;
}
}
if (startDayOfWeek > Calendar.SATURDAY) {
throw new IllegalArgumentException();
}
}
if (startMode == DOW_IN_MONTH_MODE) {
if (startDay < -5 || startDay > 5) {
throw new IllegalArgumentException();
}
} else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
throw new IllegalArgumentException();
}
}
}
/**
* Decode the end rule and validate the parameters. This method is exactly
* analogous to decodeStartRule().
* @see #decodeStartRule
*/
private void decodeEndRule() {
useDaylight = (boolean)((startDay != 0) && (endDay != 0) ? true : false);
if (useDaylight && dst == 0) {
dst = Grego.MILLIS_PER_DAY;
}
if (endDay != 0) {
if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
throw new IllegalArgumentException();
}
if (endTime < 0 || endTime > Grego.MILLIS_PER_DAY ||
endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
throw new IllegalArgumentException();
}
if (endDayOfWeek == 0) {
endMode = DOM_MODE;
} else {
if (endDayOfWeek > 0) {
endMode = DOW_IN_MONTH_MODE;
} else {
endDayOfWeek = (int)-endDayOfWeek;
if (endDay > 0) {
endMode = DOW_GE_DOM_MODE;
} else {
endDay = (int)-endDay;
endMode = DOW_LE_DOM_MODE;
}
}
if (endDayOfWeek > Calendar.SATURDAY) {
throw new IllegalArgumentException();
}
}
if (endMode == DOW_IN_MONTH_MODE) {
if (endDay < -5 || endDay > 5) {
throw new IllegalArgumentException();
}
} else if (endDay<1 || endDay > staticMonthLength[endMonth]) {
throw new IllegalArgumentException();
}
}
}
/**
* Return true if obj is a SimpleTimeZone equivalent to this.
* @return true if obj is a SimpleTimeZone equivalent to this
* @stable ICU 3.6
*/
public boolean equals(Object obj){
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
SimpleTimeZone that = (SimpleTimeZone) obj;
return raw == that.raw &&
useDaylight == that.useDaylight &&
idEquals(getID(),that.getID()) &&
(!useDaylight
// Only check rules if using DST
|| (dst == that.dst &&
startMode == that.startMode &&
startMonth == that.startMonth &&
startDay == that.startDay &&
startDayOfWeek == that.startDayOfWeek &&
startTime == that.startTime &&
startTimeMode == that.startTimeMode &&
endMode == that.endMode &&
endMonth == that.endMonth &&
endDay == that.endDay &&
endDayOfWeek == that.endDayOfWeek &&
endTime == that.endTime &&
endTimeMode == that.endTimeMode &&
startYear == that.startYear ));
}
private boolean idEquals(String id1, String id2){
if(id1==null && id2==null){
return true;
}
if(id1!=null && id2!=null){
return id1.equals(id2);
}
return false;
}
/**
* Return the hash code.
* @return the hash code
* @stable ICU 3.6
*/
public int hashCode(){
int ret = (int)( super.hashCode() +
raw ^ (raw>>>8) +
(useDaylight?0:1));
if(!useDaylight){
ret += (int)(dst ^ (dst>>>10) +
startMode ^ (startMode>>>11) +
startMonth ^ (startMonth>>>12) +
startDay ^ (startDay>>>13) +
startDayOfWeek ^ (startDayOfWeek>>>14) +
startTime ^ (startTime>>>15) +
startTimeMode ^ (startTimeMode>>>16) +
endMode ^ (endMode>>>17) +
endMonth ^ (endMonth>>>18) +
endDay ^ (endDay>>>19) +
endDayOfWeek ^ (endDayOfWeek>>>20) +
endTime ^ (endTime>>>21) +
endTimeMode ^ (endTimeMode>>>22) +
startYear ^ (startYear>>>23));
}
return ret;
}
/**
* Return a clone of this time zone.
* @return a clone of this time zone
* @stable ICU 3.6
*/
public Object clone() {
return super.clone();
}
/**
* Return true if this zone has the same rules and offset as another zone.
* @param othr the TimeZone object to be compared with
* @return true if the given zone has the same rules and offset as this one
* @stable ICU 2.0
*/
public boolean hasSameRules(TimeZone othr) {
if(!(othr instanceof SimpleTimeZone)){
return false;
}
SimpleTimeZone other = (SimpleTimeZone)othr;
return other != null &&
raw == other.raw &&
useDaylight == other.useDaylight &&
(!useDaylight
// Only check rules if using DST
|| (dst == other.dst &&
startMode == other.startMode &&
startMonth == other.startMonth &&
startDay == other.startDay &&
startDayOfWeek == other.startDayOfWeek &&
startTime == other.startTime &&
startTimeMode == other.startTimeMode &&
endMode == other.endMode &&
endMonth == other.endMonth &&
endDay == other.endDay &&
endDayOfWeek == other.endDayOfWeek &&
endTime == other.endTime &&
endTimeMode == other.endTimeMode &&
startYear == other.startYear));
}
// BasicTimeZone methods
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneTransition getNextTransition(long base, boolean inclusive) {
if (startMonth == 0) {
return null;
}
initTransitionRules();
long firstTransitionTime = firstTransition.getTime();
if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
return firstTransition;
}
Date stdDate = stdRule.getNextStart(base, dstRule.getRawOffset(), dstRule.getDSTSavings(), inclusive);
Date dstDate = dstRule.getNextStart(base, stdRule.getRawOffset(), stdRule.getDSTSavings(), inclusive);
if (stdDate != null && (dstDate == null || stdDate.before(dstDate))) {
return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule);
}
if (dstDate != null && (stdDate == null || dstDate.before(stdDate))) {
return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule);
}
return null;
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) {
if (startMonth == 0) {
return null;
}
initTransitionRules();
long firstTransitionTime = firstTransition.getTime();
if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
return null;
}
Date stdDate = stdRule.getPreviousStart(base, dstRule.getRawOffset(), dstRule.getDSTSavings(), inclusive);
Date dstDate = dstRule.getPreviousStart(base, stdRule.getRawOffset(), stdRule.getDSTSavings(), inclusive);
if (stdDate != null && (dstDate == null || stdDate.after(dstDate))) {
return new TimeZoneTransition(stdDate.getTime(), dstRule, stdRule);
}
if (dstDate != null && (stdDate == null || dstDate.after(stdDate))) {
return new TimeZoneTransition(dstDate.getTime(), stdRule, dstRule);
}
return null;
}
/**
* {@inheritDoc}
* @stable ICU 3.8
*/
public TimeZoneRule[] getTimeZoneRules() {
initTransitionRules();
int size = (startMonth == 0) ? 1 : 3;
TimeZoneRule[] rules = new TimeZoneRule[size];
rules[0] = initialRule;
if (startMonth != 0) {
rules[1] = stdRule;
rules[2] = dstRule;
}
return rules;
}
private transient boolean transitionRulesInitialized;
private transient InitialTimeZoneRule initialRule;
private transient TimeZoneTransition firstTransition;
private transient AnnualTimeZoneRule stdRule;
private transient AnnualTimeZoneRule dstRule;
private synchronized void initTransitionRules() {
if (transitionRulesInitialized) {
return;
}
if (startMonth != 0) {
DateTimeRule dtRule = null;
int timeRuleType;
long firstStdStart, firstDstStart;
// Create a TimeZoneRule for daylight saving time
timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME :
((startTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME);
switch (startMode) {
case DOM_MODE:
dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
break;
case DOW_IN_MONTH_MODE:
dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
break;
case DOW_GE_DOM_MODE:
dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
break;
case DOW_LE_DOM_MODE:
dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
break;
}
// For now, use ID + "(DST)" as the name
dstRule = new AnnualTimeZoneRule(getID() + "(DST)", getRawOffset(), getDSTSavings(),
dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR);
// Calculate the first DST start time
firstDstStart = dstRule.getFirstStart(getRawOffset(), 0).getTime();
// Create a TimeZoneRule for standard time
timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule.STANDARD_TIME :
((endTimeMode == UTC_TIME) ? DateTimeRule.UTC_TIME : DateTimeRule.WALL_TIME);
switch (endMode) {
case DOM_MODE:
dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
break;
case DOW_IN_MONTH_MODE:
dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
break;
case DOW_GE_DOM_MODE:
dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
break;
case DOW_LE_DOM_MODE:
dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
break;
}
// For now, use ID + "(STD)" as the name
stdRule = new AnnualTimeZoneRule(getID() + "(STD)", getRawOffset(), 0,
dtRule, startYear, AnnualTimeZoneRule.MAX_YEAR);
// Calculate the first STD start time
firstStdStart = stdRule.getFirstStart(getRawOffset(), dstRule.getDSTSavings()).getTime();
// Create a TimeZoneRule for initial time
if (firstStdStart < firstDstStart) {
initialRule = new InitialTimeZoneRule(getID() + "(DST)", getRawOffset(), dstRule.getDSTSavings());
firstTransition = new TimeZoneTransition(firstStdStart, initialRule, stdRule);
} else {
initialRule = new InitialTimeZoneRule(getID() + "(STD)", getRawOffset(), 0);
firstTransition = new TimeZoneTransition(firstDstStart, initialRule, dstRule);
}
} else {
// Create a TimeZoneRule for initial time
initialRule = new InitialTimeZoneRule(getID(), getRawOffset(), 0);
}
transitionRulesInitialized = true;
}
}