// © 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:

* @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: * 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= 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. */ @Override @Deprecated 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 */ @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: *

*

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