// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html#License /* * Copyright (C) 1996-2016, International Business Machines * Corporation and others. All Rights Reserved. */ package com.fr.third.ibm.icu.util; import java.io.IOException; import java.util.Date; import com.fr.third.ibm.icu.impl.Grego; /** * {@icuenhanced java.util.SimpleTimeZone}.{@icu _usage_} * *
SimpleTimeZone
is a concrete subclass of TimeZone
* that represents a time zone for use with a Gregorian calendar. This
* class does not handle historical changes.
*
*
Use a negative value for dayOfWeekInMonth
to indicate that
* SimpleTimeZone
should count from the end of the month backwards. For
* example, if Daylight Savings Time starts or ends at the last Sunday in a month, use
* dayOfWeekInMonth = -1
along with dayOfWeek = Calendar.SUNDAY
* to specify the rule.
*
* @see Calendar
* @see GregorianCalendar
* @see TimeZone
* @author Deborah 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) {
super(ID);
construct(rawOffset, 0, 0, 0,
0, WALL_TIME,
0, 0, 0,
0, WALL_TIME,
Grego.MILLIS_PER_HOUR);
}
/**
* Constructs 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.
* @throws 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) {
super(ID);
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, WALL_TIME,
endMonth, endDay, endDayOfWeek,
endTime, WALL_TIME,
Grego.MILLIS_PER_HOUR);
}
/**
* Constructs 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.
* @throws 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){
super(ID);
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, startTimeMode,
endMonth, endDay, endDayOfWeek,
endTime, endTimeMode,
dstSavings);
}
/**
* 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.
* @throws 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) {
super(ID);
construct(rawOffset,
startMonth, startDay, startDayOfWeek,
startTime, WALL_TIME,
endMonth, endDay, endDayOfWeek,
endTime, WALL_TIME,
dstSavings);
}
/**
* {@inheritDoc}
*
* @stable ICU 3.8
*/
@Override
public void setID(String ID) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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
*/
@Override
public void setRawOffset(int offsetMillis) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
raw = offsetMillis;
transitionRulesInitialized = false;
}
/**
* Overrides TimeZone
* Gets the GMT offset for this time zone.
* @return the raw offset
* @stable ICU 2.0
*/
@Override
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) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
getSTZInfo().sy = year;
this.startYear = year;
transitionRulesInitialized = false;
}
/**
* Sets the daylight savings starting rule. For example, Daylight Savings
* Time starts at the second Sunday in March, at 2 AM in standard time.
* Therefore, you can set the start rule by calling:
* setStartRule(Calendar.MARCH, 2, Calendar.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.
* @throws 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) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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 second Sunday in March, at 2 AM in standard time.
* Therefore, you can set the start rule by calling:
* setStartRule(Calendar.MARCH, 2, Calendar.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:
setEndRule(Calendar.OCTOBER, -1, Calendar.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.
* @throws 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) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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.
* @throws IllegalArgumentException the month,
* dayOfMonth, or time parameters are out of range
* @stable ICU 2.0
*/
public void setEndRule(int month, int dayOfMonth, int time) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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.
* @throws 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) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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){
assert (!isFrozen());
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 first Sunday in November, at 2 AM in standard time.
* Therefore, you can set the end rule by calling:
* setEndRule(Calendar.NOVEMBER, 1, Calendar.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){
assert (!isFrozen());
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. Typically one hour (+3600000). The amount could be negative,
* but not 0.
* @stable ICU 2.0
*/
public void setDSTSavings(int millisSavedDuringDST) {
if (isFrozen()) {
throw new UnsupportedOperationException("Attempt to modify a frozen SimpleTimeZone instance.");
}
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. Typically one hour (3600000). The amount could be negative,
* but not 0.
* @stable ICU 2.0
*/
@Override
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
*/
@Override
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
*/
@Override
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.
*/
@Deprecated
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));
}
private 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();
}
}
//Eclipse stated the following is "dead code"
/*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*
* 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 ** 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 = (startDay != 0) && (endDay != 0); 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 = -startDayOfWeek; if (startDay > 0) { startMode = DOW_GE_DOM_MODE; } else { startDay = -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 = (startDay != 0) && (endDay != 0); 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 = -endDayOfWeek; if (endDay > 0) { endMode = DOW_GE_DOM_MODE; } else { endDay = -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(); } } } /** * Overrides equals. * @return true if obj is a SimpleTimeZone equivalent to this * @stable ICU 3.6 */ @Override 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; } /** * Overrides hashCode. * @stable ICU 3.6 */ @Override public int hashCode(){ int ret = super.hashCode() + raw ^ (raw >>> 8) + (useDaylight ? 0 : 1); if(!useDaylight){ ret += 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; } /** * Overrides clone. * @stable ICU 3.6 */ @Override public Object clone() { if (isFrozen()) { return this; } return cloneAsThawed(); } /** * Returns 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 */ @Override public boolean hasSameRules(TimeZone othr) { if (this == othr) { return true; } 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 */ @Override public TimeZoneTransition getNextTransition(long base, boolean inclusive) { if (!useDaylight) { 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 */ @Override public TimeZoneTransition getPreviousTransition(long base, boolean inclusive) { if (!useDaylight) { 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 */ @Override public TimeZoneRule[] getTimeZoneRules() { initTransitionRules(); int size = useDaylight ? 3 : 1; TimeZoneRule[] rules = new TimeZoneRule[size]; rules[0] = initialRule; if (useDaylight) { 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 (useDaylight) { 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; } // Freezable stuffs private volatile transient boolean isFrozen = false; /** * {@inheritDoc} * @stable ICU 49 */ @Override public boolean isFrozen() { return isFrozen; } /** * {@inheritDoc} * @stable ICU 49 */ @Override public TimeZone freeze() { isFrozen = true; return this; } /** * {@inheritDoc} * @stable ICU 49 */ @Override public TimeZone cloneAsThawed() { SimpleTimeZone tz = (SimpleTimeZone)super.cloneAsThawed(); tz.isFrozen = false; return tz; } }