SimpleTimeZone.java
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
* Copyright (C) 1996-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*/
package com.ibm.icu.util;
import com.ibm.icu.impl.Grego;
import java.io.IOException;
import java.util.Date;
/**
* {@icuenhanced java.util.SimpleTimeZone}.{@icu _usage_}
*
* <p><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, if Daylight Savings Time
* starts or ends at the last Sunday in a month, use <code>dayOfWeekInMonth = -1</code> along with
* <code>dayOfWeek = Calendar.SUNDAY</code> 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 implements Cloneable {
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).
*
* <p>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.
*
* <p>Day of month. To specify an exact day of the month, such as March 1, set startDayOfWeek to
* zero.
*
* <p>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.
*
* <p>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.
*
* <p>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: <code>setStartRule(Calendar.MARCH, 2, Calendar.SUNDAY, 2*60*60*1000);
* </code> 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 specified 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) {
assert (!isFrozen());
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.
* @throws IllegalArgumentException the month, dayOfMonth, or time parameters are out of range
* @stable ICU 2.0
*/
public void setStartRule(int month, int dayOfMonth, int time) {
if (isFrozen()) {
throw new UnsupportedOperationException(
"Attempt to modify a frozen SimpleTimeZone instance.");
}
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.
* @throws 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) {
if (isFrozen()) {
throw new UnsupportedOperationException(
"Attempt to modify a frozen SimpleTimeZone instance.");
}
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, if Daylight Savings Time ends at the last
* (-1) Sunday in October, at 2 AM in standard time, you can set the end rule by calling: <code>
* setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
*
* @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.
*
* <p>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 2.0
*/
@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 accommodate 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 static final 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<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}
*
* @stable ICU 69
*/
@Override
public void getOffsetFromLocal(
long date,
LocalOption nonExistingTimeOpt,
LocalOption duplicatedTimeOpt,
int[] offsets) {
int nonExistingTimeOptVal = getLocalOptionValue(nonExistingTimeOpt);
int duplicatedTimeOptVal = getLocalOptionValue(duplicatedTimeOpt);
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 ((nonExistingTimeOptVal & STD_DST_MASK) == LOCAL_STD
|| (nonExistingTimeOptVal & STD_DST_MASK) != LOCAL_DST
&& (nonExistingTimeOptVal & FORMER_LATTER_MASK) != LOCAL_LATTER) {
date -= getDSTSavings();
recalc = true;
}
} else {
if ((duplicatedTimeOptVal & STD_DST_MASK) == LOCAL_DST
|| (duplicatedTimeOptVal & STD_DST_MASK) != LOCAL_STD
&& (duplicatedTimeOptVal & 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
*/
@Override
public boolean useDaylightTime() {
return useDaylight;
}
/**
* {@inheritDoc}
*
* @stable ICU 49
*/
@Override
public boolean observesDaylightTime() {
return useDaylight;
}
/**
* Overrides TimeZone Queries if the give date is in Daylight Saving Time.
*
* @stable ICU 2.0
*/
@Override
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 = (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 2.0
*/
@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.
*
* @return a hash code value for this object.
* @stable ICU 2.0
*/
@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 2.0
*/
@Override
public SimpleTimeZone 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 transient volatile 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 SimpleTimeZone cloneAsThawed() {
SimpleTimeZone tz = (SimpleTimeZone) super.cloneAsThawed();
tz.isFrozen = false;
return tz;
}
}