531 * the offset value is adjusted with the amount of daylight
532 * saving.
533 *
534 * @param date the time at which the time zone offset is found
535 * @return the amount of time in milliseconds to add to UTC to get
536 * local time.
537 * @since 1.4
538 */
539 public int getOffset(long date) {
540 return getOffsets(date, null);
541 }
542
543 /**
544 * @see TimeZone#getOffsets
545 */
546 int getOffsets(long date, int[] offsets) {
547 int offset = rawOffset;
548
549 computeOffset:
550 if (useDaylight) {
551 synchronized (this) {
552 if (cacheStart != 0) {
553 if (date >= cacheStart && date < cacheEnd) {
554 offset += dstSavings;
555 break computeOffset;
556 }
557 }
558 }
559 BaseCalendar cal = date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
560 gcal : (BaseCalendar) CalendarSystem.forName("julian");
561 BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
562 // Get the year in local time
563 cal.getCalendarDate(date + rawOffset, cdate);
564 int year = cdate.getNormalizedYear();
565 if (year >= startYear) {
566 // Clear time elements for the transition calculations
567 cdate.setTimeOfDay(0, 0, 0, 0);
568 offset = getOffset(cal, cdate, year, date);
569 }
570 }
571
572 if (offsets != null) {
573 offsets[0] = rawOffset;
574 offsets[1] = offset - rawOffset;
575 }
576 return offset;
577 }
578
654
655 if ((cdate.getNormalizedYear() != y)
656 || (cdate.getMonth() != m)
657 || (cdate.getDayOfMonth() != day)
658 // The validation should be cdate.getDayOfWeek() ==
659 // dayOfWeek. However, we don't check dayOfWeek for
660 // compatibility.
661 || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
662 || (millis < 0 || millis >= (24*60*60*1000))) {
663 throw new IllegalArgumentException();
664 }
665
666 if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
667 return rawOffset;
668 }
669
670 return getOffset(cal, cdate, y, time);
671 }
672
673 private int getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time) {
674 synchronized (this) {
675 if (cacheStart != 0) {
676 if (time >= cacheStart && time < cacheEnd) {
677 return rawOffset + dstSavings;
678 }
679 if (year == cacheYear) {
680 return rawOffset;
681 }
682 }
683 }
684
685 long start = getStart(cal, cdate, year);
686 long end = getEnd(cal, cdate, year);
687 int offset = rawOffset;
688 if (start <= end) {
689 if (time >= start && time < end) {
690 offset += dstSavings;
691 }
692 synchronized (this) {
693 cacheYear = year;
694 cacheStart = start;
695 cacheEnd = end;
696 }
697 } else {
698 if (time < end) {
699 // TODO: support Gregorian cutover. The previous year
700 // may be in the other calendar system.
701 start = getStart(cal, cdate, year - 1);
702 if (time >= start) {
703 offset += dstSavings;
704 }
705 } else if (time >= start) {
706 // TODO: support Gregorian cutover. The next year
707 // may be in the other calendar system.
708 end = getEnd(cal, cdate, year + 1);
709 if (time < end) {
710 offset += dstSavings;
711 }
712 }
713 if (start <= end) {
714 synchronized (this) {
715 // The start and end transitions are in multiple years.
716 cacheYear = (long) startYear - 1;
717 cacheStart = start;
718 cacheEnd = end;
719 }
720 }
721 }
722 return offset;
723 }
724
725 private long getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
726 int time = startTime;
727 if (startTimeMode != UTC_TIME) {
728 time -= rawOffset;
729 }
730 return getTransition(cal, cdate, startMode, year, startMonth, startDay,
731 startDayOfWeek, time);
732 }
733
734 private long getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
735 int time = endTime;
736 if (endTimeMode != UTC_TIME) {
737 time -= rawOffset;
738 }
739 if (endTimeMode == WALL_TIME) {
859 * given date; false otherwise.
860 */
861 public boolean inDaylightTime(Date date)
862 {
863 return (getOffset(date.getTime()) != rawOffset);
864 }
865
866 /**
867 * Returns a clone of this <code>SimpleTimeZone</code> instance.
868 * @return a clone of this instance.
869 */
870 public Object clone()
871 {
872 return super.clone();
873 }
874
875 /**
876 * Generates the hash code for the SimpleDateFormat object.
877 * @return the hash code for this object
878 */
879 public synchronized int hashCode()
880 {
881 return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
882 endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
883 }
884
885 /**
886 * Compares the equality of two <code>SimpleTimeZone</code> objects.
887 *
888 * @param obj The <code>SimpleTimeZone</code> object to be compared with.
889 * @return True if the given <code>obj</code> is the same as this
890 * <code>SimpleTimeZone</code> object; false otherwise.
891 */
892 public boolean equals(Object obj)
893 {
894 if (this == obj) {
895 return true;
896 }
897 if (!(obj instanceof SimpleTimeZone)) {
898 return false;
899 }
1184 * <p>If <code>useDaylight</code> is false, this value is ignored.
1185 * @serial
1186 * @since 1.1.4
1187 */
1188 private int endMode;
1189
1190 /**
1191 * A positive value indicating the amount of time saved during DST in
1192 * milliseconds.
1193 * Typically one hour (3600000); sometimes 30 minutes (1800000).
1194 * <p>If <code>useDaylight</code> is false, this value is ignored.
1195 * @serial
1196 * @since 1.1.4
1197 */
1198 private int dstSavings;
1199
1200 private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
1201
1202 /**
1203 * Cache values representing a single period of daylight saving
1204 * time. When the cache values are valid, cacheStart is the start
1205 * time (inclusive) of daylight saving time and cacheEnd is the
1206 * end time (exclusive).
1207 *
1208 * cacheYear has a year value if both cacheStart and cacheEnd are
1209 * in the same year. cacheYear is set to startYear - 1 if
1210 * cacheStart and cacheEnd are in different years. cacheStart is 0
1211 * if the cache values are void. cacheYear is a long to support
1212 * Integer.MIN_VALUE - 1 (JCK requirement).
1213 */
1214 private transient long cacheYear;
1215 private transient long cacheStart;
1216 private transient long cacheEnd;
1217
1218 /**
1219 * Constants specifying values of startMode and endMode.
1220 */
1221 private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
1222 private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
1223 private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
1224 private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
1225
1226 /**
1227 * Constant for a mode of start or end time specified as wall clock
1228 * time. Wall clock time is standard time for the onset rule, and
1229 * daylight time for the end rule.
1230 * @since 1.4
1231 */
1232 public static final int WALL_TIME = 0; // Zero for backward compatibility
1233
1234 /**
1235 * Constant for a mode of start or end time specified as standard time.
1236 * @since 1.4
1265 * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
1266 * <code>endMode</code>, and <code>dstSavings</code>.
1267 * </dd>
1268 * <dt><b>2</b></dt>
1269 * <dd>
1270 * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code>
1271 * and <code>endTimeMode</code>.
1272 * </dd>
1273 * </dl>
1274 * When streaming out this class, the most recent format
1275 * and the highest allowable <code>serialVersionOnStream</code>
1276 * is written.
1277 * @serial
1278 * @since 1.1.4
1279 */
1280 private int serialVersionOnStream = currentSerialVersion;
1281
1282 // Maximum number of rules.
1283 private static final int MAX_RULE_NUM = 6;
1284
1285 private synchronized void invalidateCache() {
1286 cacheYear = startYear - 1;
1287 cacheStart = cacheEnd = 0;
1288 }
1289
1290 //----------------------------------------------------------------------
1291 // Rule representation
1292 //
1293 // We represent the following flavors of rules:
1294 // 5 the fifth of the month
1295 // lastSun the last Sunday in the month
1296 // lastMon the last Monday in the month
1297 // Sun>=8 first Sunday on or after the eighth
1298 // Sun<=25 last Sunday on or before the 25th
1299 // This is further complicated by the fact that we need to remain
1300 // backward compatible with the 1.1 FCS. Finally, we need to minimize
1301 // API changes. In order to satisfy these requirements, we support
1302 // three representation systems, and we translate between them.
1303 //
1304 // INTERNAL REPRESENTATION
1305 // This is the format SimpleTimeZone objects take after construction or
1306 // streaming in is complete. Rules are represented directly, using an
1307 // unencoded format. We will discuss the start rule only below; the end
|
531 * the offset value is adjusted with the amount of daylight
532 * saving.
533 *
534 * @param date the time at which the time zone offset is found
535 * @return the amount of time in milliseconds to add to UTC to get
536 * local time.
537 * @since 1.4
538 */
539 public int getOffset(long date) {
540 return getOffsets(date, null);
541 }
542
543 /**
544 * @see TimeZone#getOffsets
545 */
546 int getOffsets(long date, int[] offsets) {
547 int offset = rawOffset;
548
549 computeOffset:
550 if (useDaylight) {
551 Cache cache = this.cache;
552 if (cache != null) {
553 if (date >= cache.start && date < cache.end) {
554 offset += dstSavings;
555 break computeOffset;
556 }
557 }
558 BaseCalendar cal = date >= GregorianCalendar.DEFAULT_GREGORIAN_CUTOVER ?
559 gcal : (BaseCalendar) CalendarSystem.forName("julian");
560 BaseCalendar.Date cdate = (BaseCalendar.Date) cal.newCalendarDate(TimeZone.NO_TIMEZONE);
561 // Get the year in local time
562 cal.getCalendarDate(date + rawOffset, cdate);
563 int year = cdate.getNormalizedYear();
564 if (year >= startYear) {
565 // Clear time elements for the transition calculations
566 cdate.setTimeOfDay(0, 0, 0, 0);
567 offset = getOffset(cal, cdate, year, date);
568 }
569 }
570
571 if (offsets != null) {
572 offsets[0] = rawOffset;
573 offsets[1] = offset - rawOffset;
574 }
575 return offset;
576 }
577
653
654 if ((cdate.getNormalizedYear() != y)
655 || (cdate.getMonth() != m)
656 || (cdate.getDayOfMonth() != day)
657 // The validation should be cdate.getDayOfWeek() ==
658 // dayOfWeek. However, we don't check dayOfWeek for
659 // compatibility.
660 || (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
661 || (millis < 0 || millis >= (24*60*60*1000))) {
662 throw new IllegalArgumentException();
663 }
664
665 if (!useDaylight || year < startYear || era != GregorianCalendar.CE) {
666 return rawOffset;
667 }
668
669 return getOffset(cal, cdate, y, time);
670 }
671
672 private int getOffset(BaseCalendar cal, BaseCalendar.Date cdate, int year, long time) {
673 Cache cache = this.cache;
674 if (cache != null) {
675 if (time >= cache.start && time < cache.end) {
676 return rawOffset + dstSavings;
677 }
678 if (year == cache.year) {
679 return rawOffset;
680 }
681 }
682
683 long start = getStart(cal, cdate, year);
684 long end = getEnd(cal, cdate, year);
685 int offset = rawOffset;
686 if (start <= end) {
687 if (time >= start && time < end) {
688 offset += dstSavings;
689 }
690 this.cache = new Cache(year, start, end);
691 } else {
692 if (time < end) {
693 // TODO: support Gregorian cutover. The previous year
694 // may be in the other calendar system.
695 start = getStart(cal, cdate, year - 1);
696 if (time >= start) {
697 offset += dstSavings;
698 }
699 } else if (time >= start) {
700 // TODO: support Gregorian cutover. The next year
701 // may be in the other calendar system.
702 end = getEnd(cal, cdate, year + 1);
703 if (time < end) {
704 offset += dstSavings;
705 }
706 }
707 if (start <= end) {
708 this.cache = new Cache((long) startYear - 1, start, end);
709 }
710 }
711 return offset;
712 }
713
714 private long getStart(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
715 int time = startTime;
716 if (startTimeMode != UTC_TIME) {
717 time -= rawOffset;
718 }
719 return getTransition(cal, cdate, startMode, year, startMonth, startDay,
720 startDayOfWeek, time);
721 }
722
723 private long getEnd(BaseCalendar cal, BaseCalendar.Date cdate, int year) {
724 int time = endTime;
725 if (endTimeMode != UTC_TIME) {
726 time -= rawOffset;
727 }
728 if (endTimeMode == WALL_TIME) {
848 * given date; false otherwise.
849 */
850 public boolean inDaylightTime(Date date)
851 {
852 return (getOffset(date.getTime()) != rawOffset);
853 }
854
855 /**
856 * Returns a clone of this <code>SimpleTimeZone</code> instance.
857 * @return a clone of this instance.
858 */
859 public Object clone()
860 {
861 return super.clone();
862 }
863
864 /**
865 * Generates the hash code for the SimpleDateFormat object.
866 * @return the hash code for this object
867 */
868 public int hashCode()
869 {
870 return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
871 endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
872 }
873
874 /**
875 * Compares the equality of two <code>SimpleTimeZone</code> objects.
876 *
877 * @param obj The <code>SimpleTimeZone</code> object to be compared with.
878 * @return True if the given <code>obj</code> is the same as this
879 * <code>SimpleTimeZone</code> object; false otherwise.
880 */
881 public boolean equals(Object obj)
882 {
883 if (this == obj) {
884 return true;
885 }
886 if (!(obj instanceof SimpleTimeZone)) {
887 return false;
888 }
1173 * <p>If <code>useDaylight</code> is false, this value is ignored.
1174 * @serial
1175 * @since 1.1.4
1176 */
1177 private int endMode;
1178
1179 /**
1180 * A positive value indicating the amount of time saved during DST in
1181 * milliseconds.
1182 * Typically one hour (3600000); sometimes 30 minutes (1800000).
1183 * <p>If <code>useDaylight</code> is false, this value is ignored.
1184 * @serial
1185 * @since 1.1.4
1186 */
1187 private int dstSavings;
1188
1189 private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
1190
1191 /**
1192 * Cache values representing a single period of daylight saving
1193 * time. Cache.start is the start time (inclusive) of daylight
1194 * saving time and Cache.end is the end time (exclusive).
1195 *
1196 * Cache.year has a year value if both Cache.start and Cache.end are
1197 * in the same year. Cache.year is set to startYear - 1 if
1198 * Cache.start and Cache.end are in different years.
1199 * Cache.year is a long to support Integer.MIN_VALUE - 1 (JCK requirement).
1200 */
1201 private static final class Cache {
1202 final long year;
1203 final long start;
1204 final long end;
1205
1206 Cache(long year, long start, long end) {
1207 this.year = year;
1208 this.start = start;
1209 this.end = end;
1210 }
1211 }
1212
1213 private transient volatile Cache cache;
1214
1215 /**
1216 * Constants specifying values of startMode and endMode.
1217 */
1218 private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
1219 private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
1220 private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
1221 private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
1222
1223 /**
1224 * Constant for a mode of start or end time specified as wall clock
1225 * time. Wall clock time is standard time for the onset rule, and
1226 * daylight time for the end rule.
1227 * @since 1.4
1228 */
1229 public static final int WALL_TIME = 0; // Zero for backward compatibility
1230
1231 /**
1232 * Constant for a mode of start or end time specified as standard time.
1233 * @since 1.4
1262 * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
1263 * <code>endMode</code>, and <code>dstSavings</code>.
1264 * </dd>
1265 * <dt><b>2</b></dt>
1266 * <dd>
1267 * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code>
1268 * and <code>endTimeMode</code>.
1269 * </dd>
1270 * </dl>
1271 * When streaming out this class, the most recent format
1272 * and the highest allowable <code>serialVersionOnStream</code>
1273 * is written.
1274 * @serial
1275 * @since 1.1.4
1276 */
1277 private int serialVersionOnStream = currentSerialVersion;
1278
1279 // Maximum number of rules.
1280 private static final int MAX_RULE_NUM = 6;
1281
1282 private void invalidateCache() {
1283 cache = null;
1284 }
1285
1286 //----------------------------------------------------------------------
1287 // Rule representation
1288 //
1289 // We represent the following flavors of rules:
1290 // 5 the fifth of the month
1291 // lastSun the last Sunday in the month
1292 // lastMon the last Monday in the month
1293 // Sun>=8 first Sunday on or after the eighth
1294 // Sun<=25 last Sunday on or before the 25th
1295 // This is further complicated by the fact that we need to remain
1296 // backward compatible with the 1.1 FCS. Finally, we need to minimize
1297 // API changes. In order to satisfy these requirements, we support
1298 // three representation systems, and we translate between them.
1299 //
1300 // INTERNAL REPRESENTATION
1301 // This is the format SimpleTimeZone objects take after construction or
1302 // streaming in is complete. Rules are represented directly, using an
1303 // unencoded format. We will discuss the start rule only below; the end
|