1. /*
  2. * @(#)SimpleTimeZone.java 1.45 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  9. * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  10. *
  11. * The original version of this source code and documentation is copyrighted
  12. * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  13. * materials are provided under terms of a License Agreement between Taligent
  14. * and Sun. This technology is protected by multiple US and International
  15. * patents. This notice and attribution to Taligent may not be removed.
  16. * Taligent is a registered trademark of Taligent, Inc.
  17. *
  18. */
  19. package java.util;
  20. import java.io.ObjectInputStream;
  21. import java.io.ObjectOutputStream;
  22. import java.io.IOException;
  23. import sun.util.calendar.CalendarDate;
  24. import sun.util.calendar.Gregorian;
  25. /**
  26. * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
  27. * that represents a time zone for use with a Gregorian calendar.
  28. * The class holds an offset from GMT, called <em>raw offset</em>, and start
  29. * and end rules for a daylight saving time schedule. Since it only holds
  30. * single values for each, it cannot handle historical changes in the offset
  31. * from GMT and the daylight saving schedule, except that the {@link
  32. * #setStartYear setStartYear} method can specify the year when the daylight
  33. * saving time schedule starts in effect.
  34. * <p>
  35. * To construct a <code>SimpleTimeZone</code> with a daylight saving time
  36. * schedule, the schedule can be described with a set of rules,
  37. * <em>start-rule</em> and <em>end-rule</em>. A day when daylight saving time
  38. * starts or ends is specified by a combination of <em>month</em>,
  39. * <em>day-of-month</em>, and <em>day-of-week</em> values. The <em>month</em>
  40. * value is represented by a Calendar {@link Calendar#MONTH MONTH} field
  41. * value, such as {@link Calendar#MARCH}. The <em>day-of-week</em> value is
  42. * represented by a Calendar {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value,
  43. * such as {@link Calendar#SUNDAY SUNDAY}. The meanings of value combinations
  44. * are as follows.
  45. *
  46. * <ul>
  47. * <li><b>Exact day of month</b><br>
  48. * To specify an exact day of month, set the <em>month</em> and
  49. * <em>day-of-month</em> to an exact value, and <em>day-of-week</em> to zero. For
  50. * example, to specify March 1, set the <em>month</em> to {@link Calendar#MARCH
  51. * MARCH}, <em>day-of-month</em> to 1, and <em>day-of-week</em> to 0.</li>
  52. *
  53. * <li><b>Day of week on or after day of month</b><br>
  54. * To specify a day of week on or after an exact day of month, set the
  55. * <em>month</em> to an exact month value, <em>day-of-month</em> to the day on
  56. * or after which the rule is applied, and <em>day-of-week</em> to a {@link
  57. * Calendar#DAY_OF_WEEK DAY_OF_WEEK} field value. For example, to specify the
  58. * second Sunday of April, set <em>month</em> to {@link Calendar#APRIL APRIL},
  59. * <em>day-of-month</em> to 8, and <em>day-of-week</em> to {@link
  60. * Calendar#SUNDAY SUNDAY}.</li>
  61. *
  62. * <li><b>Day of week on or before day of month</b><br>
  63. * To specify a day of the week on or before an exact day of the month, set
  64. * <em>day-of-month</em> and <em>day-of-week</em> to a negative value. For
  65. * example, to specify the last Wednesday on or before the 21st of March, set
  66. * <em>month</em> to {@link Calendar#MARCH MARCH}, <em>day-of-month</em> is -21
  67. * and <em>day-of-week</em> is {@link Calendar#WEDNESDAY -WEDNESDAY}. </li>
  68. *
  69. * <li><b>Last day-of-week of month</b><br>
  70. * To specify, the last day-of-week of the month, set <em>day-of-week</em> to a
  71. * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} value and <em>day-of-month</em> to
  72. * -1. For example, to specify the last Sunday of October, set <em>month</em>
  73. * to {@link Calendar#OCTOBER OCTOBER}, <em>day-of-week</em> to {@link
  74. * Calendar#SUNDAY SUNDAY} and <em>day-of-month</em> to -1. </li>
  75. *
  76. * </ul>
  77. * The time of the day at which daylight saving time starts or ends is
  78. * specified by a millisecond value within the day. There are three kinds of
  79. * <em>mode</em>s to specify the time: {@link #WALL_TIME}, {@link
  80. * #STANDARD_TIME} and {@link #UTC_TIME}. For example, if daylight
  81. * saving time ends
  82. * at 2:00 am in the wall clock time, it can be specified by 7200000
  83. * milliseconds in the {@link #WALL_TIME} mode. In this case, the wall clock time
  84. * for an <em>end-rule</em> means the same thing as the daylight time.
  85. * <p>
  86. * The following are examples of parameters for constructing time zone objects.
  87. * <pre><code>
  88. * // Base GMT offset: -8:00
  89. * // DST starts: at 2:00am in standard time
  90. * // on the first Sunday in April
  91. * // DST ends: at 2:00am in daylight time
  92. * // on the last Sunday in October
  93. * // Save: 1 hour
  94. * SimpleTimeZone(-28800000,
  95. * "America/Los_Angeles",
  96. * Calendar.APRIL, 1, -Calendar.SUNDAY,
  97. * 7200000,
  98. * Calendar.OCTOBER, -1, Calendar.SUNDAY,
  99. * 7200000,
  100. * 3600000)
  101. *
  102. * // Base GMT offset: +1:00
  103. * // DST starts: at 1:00am in UTC time
  104. * // on the last Sunday in March
  105. * // DST ends: at 1:00am in UTC time
  106. * // on the last Sunday in October
  107. * // Save: 1 hour
  108. * SimpleTimeZone(3600000,
  109. * "Europe/Paris",
  110. * Calendar.MARCH, -1, Calendar.SUNDAY,
  111. * 3600000, SimpleTimeZone.UTC_TIME,
  112. * Calendar.OCTOBER, -1, Calendar.SUNDAY,
  113. * 3600000, SimpleTimeZone.UTC_TIME,
  114. * 3600000)
  115. * </code></pre>
  116. * These parameter rules are also applicable to the set rule methods, such as
  117. * <code>setStartRule</code>.
  118. *
  119. * @since 1.1
  120. * @see Calendar
  121. * @see GregorianCalendar
  122. * @see TimeZone
  123. * @version 1.45 01/23/03
  124. * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
  125. */
  126. public class SimpleTimeZone extends TimeZone {
  127. /**
  128. * Constructs a SimpleTimeZone with the given base time zone offset from GMT
  129. * and time zone ID with no daylight saving time schedule.
  130. *
  131. * @param rawOffset The base time zone offset in milliseconds to GMT.
  132. * @param ID The time zone name that is given to this instance.
  133. */
  134. public SimpleTimeZone(int rawOffset, String ID)
  135. {
  136. this.rawOffset = rawOffset;
  137. setID (ID);
  138. dstSavings = millisPerHour; // In case user sets rules later
  139. }
  140. /**
  141. * Constructs a SimpleTimeZone with the given base time zone offset from
  142. * GMT, time zone ID, and rules for starting and ending the daylight
  143. * time.
  144. * Both <code>startTime</code> and <code>endTime</code> are specified to be
  145. * represented in the wall clock time. The amount of daylight saving is
  146. * assumed to be 3600000 milliseconds (i.e., one hour). This constructor is
  147. * equivalent to:
  148. * <pre><code>
  149. * SimpleTimeZone(rawOffset,
  150. * ID,
  151. * startMonth,
  152. * startDay,
  153. * startDayOfWeek,
  154. * startTime,
  155. * SimpleTimeZone.{@link #WALL_TIME},
  156. * endMonth,
  157. * endDay,
  158. * endDayOfWeek,
  159. * endTime,
  160. * SimpleTimeZone.{@link #WALL_TIME},
  161. * 3600000)
  162. * </code></pre>
  163. *
  164. * @param rawOffset The given base time zone offset from GMT.
  165. * @param ID The time zone ID which is given to this object.
  166. * @param startMonth The daylight saving time starting month. Month is
  167. * a {@link Calendar#MONTH MONTH} field value (0-based. e.g., 0
  168. * for January).
  169. * @param startDay The day of the month on which the daylight saving time starts.
  170. * See the class description for the special cases of this parameter.
  171. * @param startDayOfWeek The daylight saving time starting day-of-week.
  172. * See the class description for the special cases of this parameter.
  173. * @param startTime The daylight saving time starting time in local wall clock
  174. * time (in milliseconds within the day), which is local
  175. * standard time in this case.
  176. * @param endMonth The daylight saving time ending month. Month is
  177. * a {@link Calendar#MONTH MONTH} field
  178. * value (0-based. e.g., 9 for October).
  179. * @param endDay The day of the month on which the daylight saving time ends.
  180. * See the class description for the special cases of this parameter.
  181. * @param endDayOfWeek The daylight saving time ending day-of-week.
  182. * See the class description for the special cases of this parameter.
  183. * @param endTime The daylight saving ending time in local wall clock time,
  184. * (in milliseconds within the day) which is local daylight
  185. * time in this case.
  186. * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
  187. * parameters are out of range for the start or end rule
  188. */
  189. public SimpleTimeZone(int rawOffset, String ID,
  190. int startMonth, int startDay, int startDayOfWeek, int startTime,
  191. int endMonth, int endDay, int endDayOfWeek, int endTime)
  192. {
  193. this(rawOffset, ID,
  194. startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
  195. endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
  196. millisPerHour);
  197. }
  198. /**
  199. * Constructs a SimpleTimeZone with the given base time zone offset from
  200. * GMT, time zone ID, and rules for starting and ending the daylight
  201. * time.
  202. * Both <code>startTime</code> and <code>endTime</code> are assumed to be
  203. * represented in the wall clock time. This constructor is equivalent to:
  204. * <pre><code>
  205. * SimpleTimeZone(rawOffset,
  206. * ID,
  207. * startMonth,
  208. * startDay,
  209. * startDayOfWeek,
  210. * startTime,
  211. * SimpleTimeZone.{@link #WALL_TIME},
  212. * endMonth,
  213. * endDay,
  214. * endDayOfWeek,
  215. * endTime,
  216. * SimpleTimeZone.{@link #WALL_TIME},
  217. * dstSavings)
  218. * </code></pre>
  219. *
  220. * @param rawOffset The given base time zone offset from GMT.
  221. * @param ID The time zone ID which is given to this object.
  222. * @param startMonth The daylight saving time starting month. Month is
  223. * a {@link Calendar#MONTH MONTH} field
  224. * value (0-based. e.g., 0 for January).
  225. * @param startDay The day of the month on which the daylight saving time starts.
  226. * See the class description for the special cases of this parameter.
  227. * @param startDayOfWeek The daylight saving time starting day-of-week.
  228. * See the class description for the special cases of this parameter.
  229. * @param startTime The daylight saving time starting time in local wall clock
  230. * time, which is local standard time in this case.
  231. * @param endMonth The daylight saving time ending month. Month is
  232. * a {@link Calendar#MONTH MONTH} field
  233. * value (0-based. e.g., 9 for October).
  234. * @param endDay The day of the month on which the daylight saving time ends.
  235. * See the class description for the special cases of this parameter.
  236. * @param endDayOfWeek The daylight saving time ending day-of-week.
  237. * See the class description for the special cases of this parameter.
  238. * @param endTime The daylight saving ending time in local wall clock time,
  239. * which is local daylight time in this case.
  240. * @param dstSavings The amount of time in milliseconds saved during
  241. * daylight saving time.
  242. * @exception IllegalArgumentException if the month, day, dayOfWeek, or time
  243. * parameters are out of range for the start or end rule
  244. * @since 1.2
  245. */
  246. public SimpleTimeZone(int rawOffset, String ID,
  247. int startMonth, int startDay, int startDayOfWeek, int startTime,
  248. int endMonth, int endDay, int endDayOfWeek, int endTime,
  249. int dstSavings)
  250. {
  251. this(rawOffset, ID,
  252. startMonth, startDay, startDayOfWeek, startTime, WALL_TIME,
  253. endMonth, endDay, endDayOfWeek, endTime, WALL_TIME,
  254. dstSavings);
  255. }
  256. /**
  257. * Constructs a SimpleTimeZone with the given base time zone offset from
  258. * GMT, time zone ID, and rules for starting and ending the daylight
  259. * time.
  260. * This constructor takes the full set of the start and end rules
  261. * parameters, including modes of <code>startTime</code> and
  262. * <code>endTime</code>. The mode specifies either {@link #WALL_TIME wall
  263. * time} or {@link #STANDARD_TIME standard time} or {@link #UTC_TIME UTC
  264. * time}.
  265. *
  266. * @param rawOffset The given base time zone offset from GMT.
  267. * @param ID The time zone ID which is given to this object.
  268. * @param startMonth The daylight saving time starting month. Month is
  269. * a {@link Calendar#MONTH MONTH} field
  270. * value (0-based. e.g., 0 for January).
  271. * @param startDay The day of the month on which the daylight saving time starts.
  272. * See the class description for the special cases of this parameter.
  273. * @param startDayOfWeek The daylight saving time starting day-of-week.
  274. * See the class description for the special cases of this parameter.
  275. * @param startTime The daylight saving time starting time in the time mode
  276. * specified by <code>startTimeMode</code>.
  277. * @param startTimeMode The mode of the start time specified by startTime.
  278. * @param endMonth The daylight saving time ending month. Month is
  279. * a {@link Calendar#MONTH MONTH} field
  280. * value (0-based. e.g., 9 for October).
  281. * @param endDay The day of the month on which the daylight saving time ends.
  282. * See the class description for the special cases of this parameter.
  283. * @param endDayOfWeek The daylight saving time ending day-of-week.
  284. * See the class description for the special cases of this parameter.
  285. * @param endTime The daylight saving ending time in time time mode
  286. * specified by <code>endTimeMode</code>.
  287. * @param endTimeMode The mode of the end time specified by endTime
  288. * @param dstSavings The amount of time in milliseconds saved during
  289. * daylight saving time.
  290. *
  291. * @exception IllegalArgumentException if the month, day, dayOfWeek, time more, or
  292. * time parameters are out of range for the start or end rule, or if a time mode
  293. * value is invalid.
  294. *
  295. * @see #WALL_TIME
  296. * @see #STANDARD_TIME
  297. * @see #UTC_TIME
  298. *
  299. * @since 1.4
  300. */
  301. public SimpleTimeZone(int rawOffset, String ID,
  302. int startMonth, int startDay, int startDayOfWeek,
  303. int startTime, int startTimeMode,
  304. int endMonth, int endDay, int endDayOfWeek,
  305. int endTime, int endTimeMode,
  306. int dstSavings) {
  307. // Workaround fix for 4278609 (JCK failure)
  308. if (endMonth == Calendar.JANUARY && endDay == 1 &&
  309. endDayOfWeek == 0 && endTime == 0 && endTimeMode == WALL_TIME &&
  310. dstSavings > 0) {
  311. endMonth = Calendar.DECEMBER;
  312. endDay = 31;
  313. endTime = (24 * 60 * 60 * 1000) - dstSavings;
  314. endTimeMode = STANDARD_TIME;
  315. }
  316. setID(ID);
  317. this.rawOffset = rawOffset;
  318. this.startMonth = startMonth;
  319. this.startDay = startDay;
  320. this.startDayOfWeek = startDayOfWeek;
  321. this.startTime = startTime;
  322. this.startTimeMode = startTimeMode;
  323. this.endMonth = endMonth;
  324. this.endDay = endDay;
  325. this.endDayOfWeek = endDayOfWeek;
  326. this.endTime = endTime;
  327. this.endTimeMode = endTimeMode;
  328. this.dstSavings = dstSavings;
  329. // this.useDaylight is set by decodeRules
  330. decodeRules();
  331. if (dstSavings <= 0) {
  332. throw new IllegalArgumentException("Illegal daylight saving value: " + dstSavings);
  333. }
  334. }
  335. /**
  336. * Sets the daylight saving time starting year.
  337. *
  338. * @param year The daylight saving starting year.
  339. */
  340. public void setStartYear(int year)
  341. {
  342. startYear = year;
  343. }
  344. /**
  345. * Sets the daylight saving time start rule. For example, if daylight saving
  346. * time starts on the first Sunday in April at 2 am in local wall clock
  347. * time, you can set the start rule by calling:
  348. * <pre><code>setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);</code></pre>
  349. *
  350. * @param startMonth The daylight saving time starting month. Month is
  351. * a {@link Calendar#MONTH MONTH} field
  352. * value (0-based. e.g., 0 for January).
  353. * @param startDay The day of the month on which the daylight saving time starts.
  354. * See the class description for the special cases of this parameter.
  355. * @param startDayOfWeek The daylight saving time starting day-of-week.
  356. * See the class description for the special cases of this parameter.
  357. * @param startTime The daylight saving time starting time in local wall clock
  358. * time, which is local standard time in this case.
  359. * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
  360. * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
  361. */
  362. public void setStartRule(int startMonth, int startDay, int startDayOfWeek, int startTime)
  363. {
  364. this.startMonth = startMonth;
  365. this.startDay = startDay;
  366. this.startDayOfWeek = startDayOfWeek;
  367. this.startTime = startTime;
  368. startTimeMode = WALL_TIME;
  369. // useDaylight = true; // Set by decodeRules
  370. decodeStartRule();
  371. }
  372. /**
  373. * Sets the daylight saving time start rule to a fixed date within a month.
  374. * This method is equivalent to:
  375. * <pre><code>setStartRule(startMonth, startDay, 0, startTime)</code></pre>
  376. *
  377. * @param startMonth The daylight saving time starting month. Month is
  378. * a {@link Calendar#MONTH MONTH} field
  379. * value (0-based. e.g., 0 for January).
  380. * @param startDay The day of the month on which the daylight saving time starts.
  381. * @param startTime The daylight saving time starting time in local wall clock
  382. * time, which is local standard time in this case.
  383. * See the class description for the special cases of this parameter.
  384. * @exception IllegalArgumentException if the <code>startMonth</code>,
  385. * <code>startDayOfMonth</code>, or <code>startTime</code> parameters are out of range
  386. * @since 1.2
  387. */
  388. public void setStartRule(int startMonth, int startDay, int startTime) {
  389. setStartRule(startMonth, startDay, 0, startTime);
  390. }
  391. /**
  392. * Sets the daylight saving time start rule to a weekday before or after the given date within
  393. * a month, e.g., the first Monday on or after the 8th.
  394. *
  395. * @param startMonth The daylight saving time starting month. Month is
  396. * a {@link Calendar#MONTH MONTH} field
  397. * value (0-based. e.g., 0 for January).
  398. * @param startDay The day of the month on which the daylight saving time starts.
  399. * @param startDayOfWeek The daylight saving time starting day-of-week.
  400. * @param startTime The daylight saving time starting time in local wall clock
  401. * time, which is local standard time in this case.
  402. * @param after If true, this rule selects the first <code>dayOfWeek</code> on or
  403. * <em>after</em> <code>dayOfMonth</code>. If false, this rule
  404. * selects the last <code>dayOfWeek</code> on or <em>before</em>
  405. * <code>dayOfMonth</code>.
  406. * @exception IllegalArgumentException if the <code>startMonth</code>, <code>startDay</code>,
  407. * <code>startDayOfWeek</code>, or <code>startTime</code> parameters are out of range
  408. * @since 1.2
  409. */
  410. public void setStartRule(int startMonth, int startDay, int startDayOfWeek,
  411. int startTime, boolean after)
  412. {
  413. // TODO: this method doesn't check the initial values of dayOfMonth or dsyOfWeek.
  414. if (after) {
  415. setStartRule(startMonth, startDay, -startDayOfWeek, startTime);
  416. } else {
  417. setStartRule(startMonth, -startDay, -startDayOfWeek, startTime);
  418. }
  419. }
  420. /**
  421. * Sets the daylight saving time end rule. For example, if daylight saving time
  422. * ends on the last Sunday in October at 2 am in wall clock time,
  423. * you can set the end rule by calling:
  424. * <code>setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);</code>
  425. *
  426. * @param endMonth The daylight saving time ending month. Month is
  427. * a {@link Calendar#MONTH MONTH} field
  428. * value (0-based. e.g., 9 for October).
  429. * @param endDay The day of the month on which the daylight saving time ends.
  430. * See the class description for the special cases of this parameter.
  431. * @param endDayOfWeek The daylight saving time ending day-of-week.
  432. * See the class description for the special cases of this parameter.
  433. * @param endTime The daylight saving ending time in local wall clock time,
  434. * (in milliseconds within the day) which is local daylight
  435. * time in this case.
  436. * @exception IllegalArgumentException if the <code>endMonth</code>, <code>endDay</code>,
  437. * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
  438. */
  439. public void setEndRule(int endMonth, int endDay, int endDayOfWeek,
  440. int endTime)
  441. {
  442. this.endMonth = endMonth;
  443. this.endDay = endDay;
  444. this.endDayOfWeek = endDayOfWeek;
  445. this.endTime = endTime;
  446. this.endTimeMode = WALL_TIME;
  447. // useDaylight = true; // Set by decodeRules
  448. decodeEndRule();
  449. }
  450. /**
  451. * Sets the daylight saving time end rule to a fixed date within a month.
  452. * This method is equivalent to:
  453. * <pre><code>setEndRule(endMonth, endDay, 0, endTime)</code></pre>
  454. *
  455. * @param endMonth The daylight saving time ending month. Month is
  456. * a {@link Calendar#MONTH MONTH} field
  457. * value (0-based. e.g., 9 for October).
  458. * @param endDay The day of the month on which the daylight saving time ends.
  459. * @param endTime The daylight saving ending time in local wall clock time,
  460. * (in milliseconds within the day) which is local daylight
  461. * time in this case.
  462. * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
  463. * or <code>endTime</code> parameters are out of range
  464. * @since 1.2
  465. */
  466. public void setEndRule(int endMonth, int endDay, int endTime)
  467. {
  468. setEndRule(endMonth, endDay, 0, endTime);
  469. }
  470. /**
  471. * Sets the daylight saving time end rule to a weekday before or after the given date within
  472. * a month, e.g., the first Monday on or after the 8th.
  473. *
  474. * @param endMonth The daylight saving time ending month. Month is
  475. * a {@link Calendar#MONTH MONTH} field
  476. * value (0-based. e.g., 9 for October).
  477. * @param endDay The day of the month on which the daylight saving time ends.
  478. * @param endDayOfWeek The daylight saving time ending day-of-week.
  479. * @param endTime The daylight saving ending time in local wall clock time,
  480. * (in milliseconds within the day) which is local daylight
  481. * time in this case.
  482. * @param after If true, this rule selects the first <code>endDayOfWeek</code> on
  483. * or <em>after</em> <code>endDay</code>. If false, this rule
  484. * selects the last <code>endDayOfWeek</code> on or before
  485. * <code>endDay</code> of the month.
  486. * @exception IllegalArgumentException the <code>endMonth</code>, <code>endDay</code>,
  487. * <code>endDayOfWeek</code>, or <code>endTime</code> parameters are out of range
  488. * @since 1.2
  489. */
  490. public void setEndRule(int endMonth, int endDay, int endDayOfWeek, int endTime, boolean after)
  491. {
  492. if (after) {
  493. setEndRule(endMonth, endDay, -endDayOfWeek, endTime);
  494. } else {
  495. setEndRule(endMonth, -endDay, -endDayOfWeek, endTime);
  496. }
  497. }
  498. /**
  499. * Returns the offset of this time zone from UTC at the given
  500. * time. If daylight saving time is in effect at the given time,
  501. * the offset value is adjusted with the amount of daylight
  502. * saving.
  503. *
  504. * @param date the time at which the time zone offset is found
  505. * @return the amount of time in milliseconds to add to UTC to get
  506. * local time.
  507. * @since 1.4
  508. */
  509. public int getOffset(long date) {
  510. return getOffsets(date, null);
  511. }
  512. /**
  513. * @see TimeZone#getOffsets
  514. */
  515. int getOffsets(long date, int[] offsets) {
  516. int offset;
  517. calc:
  518. {
  519. if (!useDaylight) {
  520. offset = rawOffset;
  521. break calc;
  522. }
  523. // get standard local time
  524. CalendarDate cdate = Gregorian.getCalendarDate(date + rawOffset);
  525. int year = cdate.getYear();
  526. // if it's BC, assume no DST.
  527. if (year <= 0) {
  528. offset = rawOffset;
  529. break calc;
  530. }
  531. int month = cdate.getMonth();
  532. int monthLength = staticMonthLength[month];
  533. int prevMonthLength = staticMonthLength[(month + Calendar.DECEMBER) % 12];
  534. if (Gregorian.isLeapYear(year)) {
  535. if (month == Calendar.FEBRUARY) {
  536. ++monthLength;
  537. } else if (month == Calendar.MARCH) {
  538. ++prevMonthLength;
  539. }
  540. }
  541. offset = getOffset(GregorianCalendar.AD, year, month, cdate.getDate(),
  542. cdate.getDayOfWeek(), cdate.getTimeOfDay(),
  543. monthLength, prevMonthLength);
  544. }
  545. if (offsets != null) {
  546. offsets[0] = rawOffset;
  547. offsets[1] = offset - rawOffset;
  548. }
  549. return offset;
  550. }
  551. /**
  552. * Returns the difference in milliseconds between local time and
  553. * UTC, taking into account both the raw offset and the effect of
  554. * daylight saving, for the specified date and time. This method
  555. * assumes that the start and end month are distinct. It also
  556. * uses a default {@link GregorianCalendar} object as its
  557. * underlying calendar, such as for determining leap years. Do
  558. * not use the result of this method with a calendar other than a
  559. * default <code>GregorianCalendar</code>.
  560. *
  561. * <p><em>Note: In general, clients should use
  562. * <code>Calendar.get(ZONE_OFFSET) + Calendar.get(DST_OFFSET)</code>
  563. * instead of calling this method.</em>
  564. *
  565. * @param era The era of the given date.
  566. * @param year The year in the given date.
  567. * @param month The month in the given date. Month is 0-based. e.g.,
  568. * 0 for January.
  569. * @param day The day-in-month of the given date.
  570. * @param dayOfWeek The day-of-week of the given date.
  571. * @param millis The milliseconds in day in <em>standard</em> local time.
  572. * @return The milliseconds to add to UTC to get local time.
  573. * @exception IllegalArgumentException the <code>era</code>,
  574. * <code>month</code>, <code>day</code>, <code>dayOfWeek</code>,
  575. * or <code>millis</code> parameters are out of range
  576. */
  577. public int getOffset(int era, int year, int month, int day, int dayOfWeek,
  578. int millis)
  579. {
  580. // Check the month before indexing into staticMonthLength. This
  581. // duplicates the test that occurs in the 7-argument getOffset(),
  582. // however, this is unavoidable. We don't mind because this method, in
  583. // fact, should not be called; internal code should always call the
  584. // 7-argument getOffset(), and outside code should use Calendar.get(int
  585. // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
  586. // this method because it's public API. - liu 8/10/98
  587. if (month < Calendar.JANUARY
  588. || month > Calendar.DECEMBER) {
  589. throw new IllegalArgumentException("Illegal month " + month);
  590. }
  591. int monthLength, prevMonthLength;
  592. if ((era == GregorianCalendar.AD) && Gregorian.isLeapYear(year)) {
  593. monthLength = staticLeapMonthLength[month];
  594. prevMonthLength = (month > 1) ? staticLeapMonthLength[month - 1] : 31;
  595. } else {
  596. monthLength = staticMonthLength[month];
  597. prevMonthLength = (month > 1) ? staticMonthLength[month - 1] : 31;
  598. }
  599. if (true) {
  600. /* Use this parameter checking code for normal operation. Only one
  601. * of these two blocks should actually get compiled into the class
  602. * file. */
  603. if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
  604. || month < Calendar.JANUARY
  605. || month > Calendar.DECEMBER
  606. || day < 1
  607. || day > monthLength
  608. || dayOfWeek < Calendar.SUNDAY
  609. || dayOfWeek > Calendar.SATURDAY
  610. || millis < 0
  611. || millis >= millisPerDay) {
  612. throw new IllegalArgumentException();
  613. }
  614. } else {
  615. /* This parameter checking code is better for debugging, but
  616. * overkill for normal operation. Only one of these two blocks
  617. * should actually get compiled into the class file. */
  618. if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
  619. throw new IllegalArgumentException("Illegal era " + era);
  620. }
  621. if (month < Calendar.JANUARY
  622. || month > Calendar.DECEMBER) {
  623. throw new IllegalArgumentException("Illegal month " + month);
  624. }
  625. if (day < 1 || day > monthLength) {
  626. throw new IllegalArgumentException("Illegal day " + day);
  627. }
  628. if (dayOfWeek < Calendar.SUNDAY
  629. || dayOfWeek > Calendar.SATURDAY) {
  630. throw new IllegalArgumentException("Illegal day of week " + dayOfWeek);
  631. }
  632. if (millis < 0 || millis >= millisPerDay) {
  633. throw new IllegalArgumentException("Illegal millis " + millis);
  634. }
  635. }
  636. return getOffset(era, year, month, day, dayOfWeek, millis,
  637. monthLength, prevMonthLength);
  638. }
  639. /**
  640. * Gets offset, for current date, modified in case of
  641. * daylight saving time. This is the offset to add <em>to</em> UTC to get local time.
  642. * Gets the time zone offset, for current date, modified in case of daylight
  643. * saving time. This is the offset to add to UTC to get local time. Assume
  644. * that the start and end month are distinct.
  645. * @param era The era of the given date.
  646. * @param year The year in the given date.
  647. * @param month The month in the given date. Month is 0-based. e.g.,
  648. * 0 for January.
  649. * @param day The day-in-month of the given date.
  650. * @param dayOfWeek The day-of-week of the given date.
  651. * @param millis The milliseconds in day in <em>standard</em> local time.
  652. * @param monthLength The length of the given month in days.
  653. * @param prevMonthLength The length of the previous month in days.
  654. * @return The offset to add to GMT to get local time.
  655. * @exception IllegalArgumentException the era, month, day,
  656. * dayOfWeek, millis, or monthLength parameters are out of range
  657. */
  658. private int getOffset(int era, int year, int month, int day, int dayOfWeek,
  659. int millis, int monthLength, int prevMonthLength) {
  660. int result = rawOffset;
  661. // Bail out if we are before the onset of daylight saving time
  662. if (!useDaylight || year < startYear || era != GregorianCalendar.AD) {
  663. return result;
  664. }
  665. // Check for southern hemisphere. We assume that the start and end
  666. // month are different.
  667. boolean southern = (startMonth > endMonth);
  668. // Compare the date to the starting and ending rules.+1 = date>rule, -1
  669. // = date<rule, 0 = date==rule.
  670. int startCompare = compareToRule(month, monthLength, prevMonthLength,
  671. day, dayOfWeek, millis,
  672. startTimeMode == UTC_TIME ? -rawOffset : 0,
  673. startMode, startMonth, startDayOfWeek,
  674. startDay, startTime);
  675. int endCompare = 0;
  676. /* We don't always have to compute endCompare. For many instances,
  677. * startCompare is enough to determine if we are in DST or not. In the
  678. * northern hemisphere, if we are before the start rule, we can't have
  679. * DST. In the southern hemisphere, if we are after the start rule, we
  680. * must have DST. This is reflected in the way the next if statement
  681. * (not the one immediately following) short circuits. */
  682. if (southern != (startCompare >= 0)) {
  683. /* For the ending rule comparison, we add the dstSavings to the millis
  684. * passed in to convert them from standard to wall time. We then must
  685. * normalize the millis to the range 0..millisPerDay-1. */
  686. endCompare = compareToRule(month, monthLength, prevMonthLength,
  687. day, dayOfWeek, millis,
  688. endTimeMode == WALL_TIME ? dstSavings :
  689. (endTimeMode == UTC_TIME ? -rawOffset : 0),
  690. endMode, endMonth, endDayOfWeek,
  691. endDay, endTime);
  692. }
  693. // Check for both the northern and southern hemisphere cases. We
  694. // assume that in the northern hemisphere, the start rule is before the
  695. // end rule within the calendar year, and vice versa for the southern
  696. // hemisphere.
  697. if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
  698. (southern && (startCompare >= 0 || endCompare < 0))) {
  699. result += dstSavings;
  700. }
  701. return result;
  702. }
  703. /**
  704. * Compares the given date in the year to the given rule and returns 1, 0,
  705. * or -1, depending on whether the date is after, equal to, or before the
  706. * rule date. The millis are compared directly against the ruleMillis, so
  707. * any standard-daylight adjustments must be handled by the caller.
  708. *
  709. * @return 1 if the date is after the rule date, -1 if the date is before
  710. * the rule date, or 0 if the date is equal to the rule date.
  711. */
  712. private static int compareToRule(int month, int monthLen, int prevMonthLen,
  713. int dayOfMonth,
  714. int dayOfWeek, int millis, int millisDelta,
  715. int ruleMode, int ruleMonth, int ruleDayOfWeek,
  716. int ruleDay, int ruleMillis)
  717. {
  718. // Make adjustments for startTimeMode and endTimeMode
  719. millis += millisDelta;
  720. while (millis >= millisPerDay) {
  721. millis -= millisPerDay;
  722. ++dayOfMonth;
  723. dayOfWeek = 1 + (dayOfWeek % 7); // dayOfWeek is one-based
  724. if (dayOfMonth > monthLen) {
  725. dayOfMonth = 1;
  726. /* When incrementing the month, it is desirable to overflow
  727. * from DECEMBER to DECEMBER+1, since we use the result to
  728. * compare against a real month. Wraparound of the value
  729. * leads to bug 4173604. */
  730. ++month;
  731. }
  732. }
  733. while (millis < 0) {
  734. millis += millisPerDay;
  735. --dayOfMonth;
  736. dayOfWeek = 1 + ((dayOfWeek+5) % 7); // dayOfWeek is one-based
  737. if (dayOfMonth < 1) {
  738. dayOfMonth = prevMonthLen;
  739. --month;
  740. }
  741. }
  742. if (month < ruleMonth) {
  743. return -1;
  744. }
  745. if (month > ruleMonth) {
  746. return 1;
  747. }
  748. int ruleDayOfMonth = 0;
  749. switch (ruleMode) {
  750. case DOM_MODE:
  751. ruleDayOfMonth = ruleDay;
  752. break;
  753. case DOW_IN_MONTH_MODE:
  754. // In this case ruleDay is the day-of-week-in-month
  755. if (ruleDay > 0) {
  756. ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
  757. (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
  758. } else {
  759. // Assume ruleDay < 0 here
  760. ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
  761. (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
  762. }
  763. break;
  764. case DOW_GE_DOM_MODE:
  765. ruleDayOfMonth = ruleDay +
  766. (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
  767. break;
  768. case DOW_LE_DOM_MODE:
  769. ruleDayOfMonth = ruleDay -
  770. (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
  771. // Note at this point ruleDayOfMonth may be <1, although it will
  772. // be >=1 for well-formed rules.
  773. break;
  774. }
  775. if (dayOfMonth < ruleDayOfMonth) {
  776. return -1;
  777. }
  778. if (dayOfMonth > ruleDayOfMonth) {
  779. return 1;
  780. }
  781. if (millis < ruleMillis) {
  782. return -1;
  783. }
  784. if (millis > ruleMillis) {
  785. return 1;
  786. }
  787. return 0;
  788. }
  789. /**
  790. * Gets the GMT offset for this time zone.
  791. * @return the GMT offset value in milliseconds
  792. * @see #setRawOffset
  793. */
  794. public int getRawOffset()
  795. {
  796. // The given date will be taken into account while
  797. // we have the historical time zone data in place.
  798. return rawOffset;
  799. }
  800. /**
  801. * Sets the base time zone offset to GMT.
  802. * This is the offset to add to UTC to get local time.
  803. * @see #getRawOffset
  804. */
  805. public void setRawOffset(int offsetMillis)
  806. {
  807. this.rawOffset = offsetMillis;
  808. }
  809. /**
  810. * Sets the amount of time in milliseconds that the clock is advanced
  811. * during daylight saving time.
  812. * @param millisSavedDuringDST the number of milliseconds the time is
  813. * advanced with respect to standard time when the daylight saving time rules
  814. * are in effect. A positive number, typically one hour (3600000).
  815. * @see #getDSTSavings
  816. * @since 1.2
  817. */
  818. public void setDSTSavings(int millisSavedDuringDST) {
  819. if (millisSavedDuringDST <= 0) {
  820. throw new IllegalArgumentException("Illegal daylight saving value: "
  821. + millisSavedDuringDST);
  822. }
  823. dstSavings = millisSavedDuringDST;
  824. }
  825. /**
  826. * Returns the amount of time in milliseconds that the clock is
  827. * advanced during daylight saving time.
  828. *
  829. * @return the number of milliseconds the time is advanced with
  830. * respect to standard time when the daylight saving rules are in
  831. * effect, or 0 (zero) if this time zone doesn't observe daylight
  832. * saving time.
  833. *
  834. * @see #setDSTSavings
  835. * @since 1.2
  836. */
  837. public int getDSTSavings() {
  838. if (useDaylight) {
  839. return dstSavings;
  840. }
  841. return 0;
  842. }
  843. /**
  844. * Queries if this time zone uses daylight saving time.
  845. * @return true if this time zone uses daylight saving time;
  846. * false otherwise.
  847. */
  848. public boolean useDaylightTime()
  849. {
  850. return useDaylight;
  851. }
  852. /**
  853. * Queries if the given date is in daylight saving time.
  854. * @return true if daylight saving time is in effective at the
  855. * given date; false otherwise.
  856. */
  857. public boolean inDaylightTime(Date date)
  858. {
  859. return (getOffset(date.getTime()) != rawOffset);
  860. }
  861. /**
  862. * Returns a clone of this <code>SimpleTimeZone</code> instance.
  863. * @return a clone of this instance.
  864. */
  865. public Object clone()
  866. {
  867. return super.clone();
  868. }
  869. /**
  870. * Generates the hash code for the SimpleDateFormat object.
  871. * @return the hash code for this object
  872. */
  873. public synchronized int hashCode()
  874. {
  875. return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
  876. endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
  877. }
  878. /**
  879. * Compares the equality of two <code>SimpleTimeZone</code> objects.
  880. *
  881. * @param obj The <code>SimpleTimeZone</code> object to be compared with.
  882. * @return True if the given <code>obj</code> is the same as this
  883. * <code>SimpleTimeZone</code> object; false otherwise.
  884. */
  885. public boolean equals(Object obj)
  886. {
  887. if (this == obj) {
  888. return true;
  889. }
  890. if (!(obj instanceof SimpleTimeZone)) {
  891. return false;
  892. }
  893. SimpleTimeZone that = (SimpleTimeZone) obj;
  894. return getID().equals(that.getID()) &&
  895. hasSameRules(that);
  896. }
  897. /**
  898. * Returns true if this zone has the same rules and offset as another zone.
  899. * @param other the TimeZone object to be compared with
  900. * @return true if the given zone is a SimpleTimeZone and has the
  901. * same rules and offset as this one
  902. * @since 1.2
  903. */
  904. public boolean hasSameRules(TimeZone other) {
  905. if (this == other) {
  906. return true;
  907. }
  908. if (!(other instanceof SimpleTimeZone)) {
  909. return false;
  910. }
  911. SimpleTimeZone that = (SimpleTimeZone) other;
  912. return rawOffset == that.rawOffset &&
  913. useDaylight == that.useDaylight &&
  914. (!useDaylight
  915. // Only check rules if using DST
  916. || (dstSavings == that.dstSavings &&
  917. startMode == that.startMode &&
  918. startMonth == that.startMonth &&
  919. startDay == that.startDay &&
  920. startDayOfWeek == that.startDayOfWeek &&
  921. startTime == that.startTime &&
  922. startTimeMode == that.startTimeMode &&
  923. endMode == that.endMode &&
  924. endMonth == that.endMonth &&
  925. endDay == that.endDay &&
  926. endDayOfWeek == that.endDayOfWeek &&
  927. endTime == that.endTime &&
  928. endTimeMode == that.endTimeMode &&
  929. startYear == that.startYear));
  930. }
  931. /**
  932. * Returns a string representation of this time zone.
  933. * @return a string representation of this time zone.
  934. */
  935. public String toString() {
  936. return getClass().getName() +
  937. "[id=" + getID() +
  938. ",offset=" + rawOffset +
  939. ",dstSavings=" + dstSavings +
  940. ",useDaylight=" + useDaylight +
  941. ",startYear=" + startYear +
  942. ",startMode=" + startMode +
  943. ",startMonth=" + startMonth +
  944. ",startDay=" + startDay +
  945. ",startDayOfWeek=" + startDayOfWeek +
  946. ",startTime=" + startTime +
  947. ",startTimeMode=" + startTimeMode +
  948. ",endMode=" + endMode +
  949. ",endMonth=" + endMonth +
  950. ",endDay=" + endDay +
  951. ",endDayOfWeek=" + endDayOfWeek +
  952. ",endTime=" + endTime +
  953. ",endTimeMode=" + endTimeMode + ']';
  954. }
  955. // =======================privates===============================
  956. /**
  957. * The month in which daylight saving time starts. This value must be
  958. * between <code>Calendar.JANUARY</code> and
  959. * <code>Calendar.DECEMBER</code> inclusive. This value must not equal
  960. * <code>endMonth</code>.
  961. * <p>If <code>useDaylight</code> is false, this value is ignored.
  962. * @serial
  963. */
  964. private int startMonth;
  965. /**
  966. * This field has two possible interpretations:
  967. * <dl>
  968. * <dt><code>startMode == DOW_IN_MONTH</code></dt>
  969. * <dd>
  970. * <code>startDay</code> indicates the day of the month of
  971. * <code>startMonth</code> on which daylight
  972. * saving time starts, from 1 to 28, 30, or 31, depending on the
  973. * <code>startMonth</code>.
  974. * </dd>
  975. * <dt><code>startMode != DOW_IN_MONTH</code></dt>
  976. * <dd>
  977. * <code>startDay</code> indicates which <code>startDayOfWeek</code> in th
  978. * month <code>startMonth</code> daylight
  979. * saving time starts on. For example, a value of +1 and a
  980. * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
  981. * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the
  982. * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
  983. * </dd>
  984. * </ul>
  985. * <p>If <code>useDaylight</code> is false, this value is ignored.
  986. * @serial
  987. */
  988. private int startDay;
  989. /**
  990. * The day of the week on which daylight saving time starts. This value
  991. * must be between <code>Calendar.SUNDAY</code> and
  992. * <code>Calendar.SATURDAY</code> inclusive.
  993. * <p>If <code>useDaylight</code> is false or
  994. * <code>startMode == DAY_OF_MONTH</code>, this value is ignored.
  995. * @serial
  996. */
  997. private int startDayOfWeek;
  998. /**
  999. * The time in milliseconds after midnight at which daylight saving
  1000. * time starts. This value is expressed as wall time, standard time,
  1001. * or UTC time, depending on the setting of <code>startTimeMode</code>.
  1002. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1003. * @serial
  1004. */
  1005. private int startTime;
  1006. /**
  1007. * The format of startTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
  1008. * @serial
  1009. * @since 1.3
  1010. */
  1011. private int startTimeMode;
  1012. /**
  1013. * The month in which daylight saving time ends. This value must be
  1014. * between <code>Calendar.JANUARY</code> and
  1015. * <code>Calendar.UNDECIMBER</code>. This value must not equal
  1016. * <code>startMonth</code>.
  1017. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1018. * @serial
  1019. */
  1020. private int endMonth;
  1021. /**
  1022. * This field has two possible interpretations:
  1023. * <dl>
  1024. * <dt><code>endMode == DOW_IN_MONTH</code></dt>
  1025. * <dd>
  1026. * <code>endDay</code> indicates the day of the month of
  1027. * <code>endMonth</code> on which daylight
  1028. * saving time ends, from 1 to 28, 30, or 31, depending on the
  1029. * <code>endMonth</code>.
  1030. * </dd>
  1031. * <dt><code>endMode != DOW_IN_MONTH</code></dt>
  1032. * <dd>
  1033. * <code>endDay</code> indicates which <code>endDayOfWeek</code> in th
  1034. * month <code>endMonth</code> daylight
  1035. * saving time ends on. For example, a value of +1 and a
  1036. * <code>endDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
  1037. * first Sunday of <code>endMonth</code>. Likewise, +2 would indicate the
  1038. * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
  1039. * </dd>
  1040. * </ul>
  1041. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1042. * @serial
  1043. */
  1044. private int endDay;
  1045. /**
  1046. * The day of the week on which daylight saving time ends. This value
  1047. * must be between <code>Calendar.SUNDAY</code> and
  1048. * <code>Calendar.SATURDAY</code> inclusive.
  1049. * <p>If <code>useDaylight</code> is false or
  1050. * <code>endMode == DAY_OF_MONTH</code>, this value is ignored.
  1051. * @serial
  1052. */
  1053. private int endDayOfWeek;
  1054. /**
  1055. * The time in milliseconds after midnight at which daylight saving
  1056. * time ends. This value is expressed as wall time, standard time,
  1057. * or UTC time, depending on the setting of <code>endTimeMode</code>.
  1058. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1059. * @serial
  1060. */
  1061. private int endTime;
  1062. /**
  1063. * The format of endTime, either WALL_TIME, STANDARD_TIME, or UTC_TIME.
  1064. * @serial
  1065. * @since 1.3
  1066. */
  1067. private int endTimeMode;
  1068. /**
  1069. * The year in which daylight saving time is first observed. This is an AD
  1070. * value. If this value is less than 1 then daylight saving time is observed
  1071. * for all AD years.
  1072. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1073. * @serial
  1074. */
  1075. private int startYear;
  1076. /**
  1077. * The offset in milliseconds between this zone and GMT. Negative offsets
  1078. * are to the west of Greenwich. To obtain local <em>standard</em> time,
  1079. * add the offset to GMT time. To obtain local wall time it may also be
  1080. * necessary to add <code>dstSavings</code>.
  1081. * @serial
  1082. */
  1083. private int rawOffset;
  1084. /**
  1085. * A boolean value which is true if and only if this zone uses daylight
  1086. * saving time. If this value is false, several other fields are ignored.
  1087. * @serial
  1088. */
  1089. private boolean useDaylight=false; // indicate if this time zone uses DST
  1090. private static final int millisPerHour = 60*60*1000;
  1091. private static final int millisPerDay = 24*millisPerHour;
  1092. /**
  1093. * This field was serialized in JDK 1.1, so we have to keep it that way
  1094. * to maintain serialization compatibility. However, there's no need to
  1095. * recreate the array each time we create a new time zone.
  1096. * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
  1097. * 31, 31, 30, 31, 30, 31}. This is ignored as of the Java 2 platform v1.2, however, it must
  1098. * be streamed out for compatibility with JDK 1.1.
  1099. */
  1100. private final byte monthLength[] = staticMonthLength;
  1101. private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  1102. private final static byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
  1103. /**
  1104. * Variables specifying the mode of the start rule. Takes the following
  1105. * values:
  1106. * <dl>
  1107. * <dt><code>DOM_MODE</code></dt>
  1108. * <dd>
  1109. * Exact day of week; e.g., March 1.
  1110. * </dd>
  1111. * <dt><code>DOW_IN_MONTH_MODE</code></dt>
  1112. * <dd>
  1113. * Day of week in month; e.g., last Sunday in March.
  1114. * </dd>
  1115. * <dt><code>DOW_GE_DOM_MODE</code></dt>
  1116. * <dd>
  1117. * Day of week after day of month; e.g., Sunday on or after March 15.
  1118. * </dd>
  1119. * <dt><code>DOW_LE_DOM_MODE</code></dt>
  1120. * <dd>
  1121. * Day of week before day of month; e.g., Sunday on or before March 15.
  1122. * </dd>
  1123. * </dl>
  1124. * The setting of this field affects the interpretation of the
  1125. * <code>startDay</code> field.
  1126. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1127. * @serial
  1128. * @since 1.1.4
  1129. */
  1130. private int startMode;
  1131. /**
  1132. * Variables specifying the mode of the end rule. Takes the following
  1133. * values:
  1134. * <dl>
  1135. * <dt><code>DOM_MODE</code></dt>
  1136. * <dd>
  1137. * Exact day of week; e.g., March 1.
  1138. * </dd>
  1139. * <dt><code>DOW_IN_MONTH_MODE</code></dt>
  1140. * <dd>
  1141. * Day of week in month; e.g., last Sunday in March.
  1142. * </dd>
  1143. * <dt><code>DOW_GE_DOM_MODE</code></dt>
  1144. * <dd>
  1145. * Day of week after day of month; e.g., Sunday on or after March 15.
  1146. * </dd>
  1147. * <dt><code>DOW_LE_DOM_MODE</code></dt>
  1148. * <dd>
  1149. * Day of week before day of month; e.g., Sunday on or before March 15.
  1150. * </dd>
  1151. * </dl>
  1152. * The setting of this field affects the interpretation of the
  1153. * <code>endDay</code> field.
  1154. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1155. * @serial
  1156. * @since 1.1.4
  1157. */
  1158. private int endMode;
  1159. /**
  1160. * A positive value indicating the amount of time saved during DST in
  1161. * milliseconds.
  1162. * Typically one hour (3600000); sometimes 30 minutes (1800000).
  1163. * <p>If <code>useDaylight</code> is false, this value is ignored.
  1164. * @serial
  1165. * @since 1.1.4
  1166. */
  1167. private int dstSavings;
  1168. /**
  1169. * Constants specifying values of startMode and endMode.
  1170. */
  1171. private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
  1172. private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
  1173. private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
  1174. private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
  1175. /**
  1176. * Constant for a mode of start or end time specified as wall clock
  1177. * time. Wall clock time is standard time for the onset rule, and
  1178. * daylight time for the end rule.
  1179. * @since 1.4
  1180. */
  1181. public static final int WALL_TIME = 0; // Zero for backward compatibility
  1182. /**
  1183. * Constant for a mode of start or end time specified as standard time.
  1184. * @since 1.4
  1185. */
  1186. public static final int STANDARD_TIME = 1;
  1187. /**
  1188. * Constant for a mode of start or end time specified as UTC. European
  1189. * Union rules are specified as UTC time, for example.
  1190. * @since 1.4
  1191. */
  1192. public static final int UTC_TIME = 2;
  1193. // Proclaim compatibility with 1.1
  1194. static final long serialVersionUID = -403250971215465050L;
  1195. // the internal serial version which says which version was written
  1196. // - 0 (default) for version up to JDK 1.1.3
  1197. // - 1 for version from JDK 1.1.4, which includes 3 new fields
  1198. // - 2 for JDK 1.3, which includes 2 new fields
  1199. static final int currentSerialVersion = 2;
  1200. /**
  1201. * The version of the serialized data on the stream. Possible values:
  1202. * <dl>
  1203. * <dt><b>0</b> or not present on stream</dt>
  1204. * <dd>
  1205. * JDK 1.1.3 or earlier.
  1206. * </dd>
  1207. * <dt><b>1</b></dt>
  1208. * <dd>
  1209. * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
  1210. * <code>endMode</code>, and <code>dstSavings</code>.
  1211. * </dd>
  1212. * <dt><b>2</b></dt>
  1213. * <dd>
  1214. * JDK 1.3 or later. Includes two new fields: <code>startTimeMode</code>
  1215. * and <code>endTimeMode</code>.
  1216. * </dd>
  1217. * </dl>
  1218. * When streaming out this class, the most recent format
  1219. * and the highest allowable <code>serialVersionOnStream</code>
  1220. * is written.
  1221. * @serial
  1222. * @since 1.1.4
  1223. */
  1224. private int serialVersionOnStream = currentSerialVersion;
  1225. //----------------------------------------------------------------------
  1226. // Rule representation
  1227. //
  1228. // We represent the following flavors of rules:
  1229. // 5 the fifth of the month
  1230. // lastSun the last Sunday in the month
  1231. // lastMon the last Monday in the month
  1232. // Sun>=8 first Sunday on or after the eighth
  1233. // Sun<=25 last Sunday on or before the 25th
  1234. // This is further complicated by the fact that we need to remain
  1235. // backward compatible with the 1.1 FCS. Finally, we need to minimize
  1236. // API changes. In order to satisfy these requirements, we support
  1237. // three representation systems, and we translate between them.
  1238. //
  1239. // INTERNAL REPRESENTATION
  1240. // This is the format SimpleTimeZone objects take after construction or
  1241. // streaming in is complete. Rules are represented directly, using an
  1242. // unencoded format. We will discuss the start rule only below; the end
  1243. // rule is analogous.
  1244. // startMode Takes on enumerated values DAY_OF_MONTH,
  1245. // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
  1246. // startDay The day of the month, or for DOW_IN_MONTH mode, a
  1247. // value indicating which DOW, such as +1 for first,
  1248. // +2 for second, -1 for last, etc.
  1249. // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
  1250. //
  1251. // ENCODED REPRESENTATION
  1252. // This is the format accepted by the constructor and by setStartRule()
  1253. // and setEndRule(). It uses various combinations of positive, negative,
  1254. // and zero values to encode the different rules. This representation
  1255. // allows us to specify all the different rule flavors without altering
  1256. // the API.
  1257. // MODE startMonth startDay startDayOfWeek
  1258. // DOW_IN_MONTH_MODE >=0 !=0 >0
  1259. // DOM_MODE >=0 >0 ==0
  1260. // DOW_GE_DOM_MODE >=0 >0 <0
  1261. // DOW_LE_DOM_MODE >=0 <0 <0
  1262. // (no DST) don't care ==0 don't care
  1263. //
  1264. // STREAMED REPRESENTATION
  1265. // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
  1266. // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
  1267. // flag useDaylight. When we stream an object out, we translate into an
  1268. // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
  1269. // and used by 1.1 code. Following that, we write out the full
  1270. // representation separately so that contemporary code can recognize and
  1271. // parse it. The full representation is written in a "packed" format,
  1272. // consisting of a version number, a length, and an array of bytes. Future
  1273. // versions of this class may specify different versions. If they wish to
  1274. // include additional data, they should do so by storing them after the
  1275. // packed representation below.
  1276. //----------------------------------------------------------------------
  1277. /**
  1278. * Given a set of encoded rules in startDay and startDayOfMonth, decode
  1279. * them and set the startMode appropriately. Do the same for endDay and
  1280. * endDayOfMonth. Upon entry, the day of week variables may be zero or
  1281. * negative, in order to indicate special modes. The day of month
  1282. * variables may also be negative. Upon exit, the mode variables will be
  1283. * set, and the day of week and day of month variables will be positive.
  1284. * This method also recognizes a startDay or endDay of zero as indicating
  1285. * no DST.
  1286. */
  1287. private void decodeRules()
  1288. {
  1289. decodeStartRule();
  1290. decodeEndRule();
  1291. }
  1292. /**
  1293. * Decode the start rule and validate the parameters. The parameters are
  1294. * expected to be in encoded form, which represents the various rule modes
  1295. * by negating or zeroing certain values. Representation formats are:
  1296. * <p>
  1297. * <pre>
  1298. * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
  1299. * ------------ ----- -------- -------- ----------
  1300. * month 0..11 same same same don't care
  1301. * day -5..5 1..31 1..31 -1..-31 0
  1302. * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
  1303. * time 0..ONEDAY same same same don't care
  1304. * </pre>
  1305. * The range for month does not include UNDECIMBER since this class is
  1306. * really specific to GregorianCalendar, which does not use that month.
  1307. * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
  1308. * end rule is an exclusive limit point. That is, the range of times that
  1309. * are in DST include those >= the start and < the end. For this reason,
  1310. * it should be possible to specify an end of ONEDAY in order to include the
  1311. * entire day. Although this is equivalent to time 0 of the following day,
  1312. * it's not always possible to specify that, for example, on December 31.
  1313. * While arguably the start range should still be 0..ONEDAY-1, we keep
  1314. * the start and end ranges the same for consistency.
  1315. */
  1316. private void decodeStartRule() {
  1317. useDaylight = (startDay != 0) && (endDay != 0);
  1318. if (startDay != 0) {
  1319. if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
  1320. throw new IllegalArgumentException(
  1321. "Illegal start month " + startMonth);
  1322. }
  1323. if (startTime < 0 || startTime >= millisPerDay) {
  1324. throw new IllegalArgumentException(
  1325. "Illegal start time " + startTime);
  1326. }
  1327. if (startDayOfWeek == 0) {
  1328. startMode = DOM_MODE;
  1329. } else {
  1330. if (startDayOfWeek > 0) {
  1331. startMode = DOW_IN_MONTH_MODE;
  1332. } else {
  1333. startDayOfWeek = -startDayOfWeek;
  1334. if (startDay > 0) {
  1335. startMode = DOW_GE_DOM_MODE;
  1336. } else {
  1337. startDay = -startDay;
  1338. startMode = DOW_LE_DOM_MODE;
  1339. }
  1340. }
  1341. if (startDayOfWeek > Calendar.SATURDAY) {
  1342. throw new IllegalArgumentException(
  1343. "Illegal start day of week " + startDayOfWeek);
  1344. }
  1345. }
  1346. if (startMode == DOW_IN_MONTH_MODE) {
  1347. if (startDay < -5 || startDay > 5) {
  1348. throw new IllegalArgumentException(
  1349. "Illegal start day of week in month " + startDay);
  1350. }
  1351. } else if (startDay < 1 || startDay > staticMonthLength[startMonth]) {
  1352. throw new IllegalArgumentException(
  1353. "Illegal start day " + startDay);
  1354. }
  1355. }
  1356. }
  1357. /**
  1358. * Decode the end rule and validate the parameters. This method is exactly
  1359. * analogous to decodeStartRule().
  1360. * @see decodeStartRule
  1361. */
  1362. private void decodeEndRule() {
  1363. useDaylight = (startDay != 0) && (endDay != 0);
  1364. if (endDay != 0) {
  1365. if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
  1366. throw new IllegalArgumentException(
  1367. "Illegal end month " + endMonth);
  1368. }
  1369. if (endTime < 0 || endTime >= millisPerDay) {
  1370. throw new IllegalArgumentException(
  1371. "Illegal end time " + endTime);
  1372. }
  1373. if (endDayOfWeek == 0) {
  1374. endMode = DOM_MODE;
  1375. } else {
  1376. if (endDayOfWeek > 0) {
  1377. endMode = DOW_IN_MONTH_MODE;
  1378. } else {
  1379. endDayOfWeek = -endDayOfWeek;
  1380. if (endDay > 0) {
  1381. endMode = DOW_GE_DOM_MODE;
  1382. } else {
  1383. endDay = -endDay;
  1384. endMode = DOW_LE_DOM_MODE;
  1385. }
  1386. }
  1387. if (endDayOfWeek > Calendar.SATURDAY) {
  1388. throw new IllegalArgumentException(
  1389. "Illegal end day of week " + endDayOfWeek);
  1390. }
  1391. }
  1392. if (endMode == DOW_IN_MONTH_MODE) {
  1393. if (endDay < -5 || endDay > 5) {
  1394. throw new IllegalArgumentException(
  1395. "Illegal end day of week in month " + endDay);
  1396. }
  1397. } else if (endDay < 1 || endDay > staticMonthLength[endMonth]) {
  1398. throw new IllegalArgumentException(
  1399. "Illegal end day " + endDay);
  1400. }
  1401. }
  1402. }
  1403. /**
  1404. * Make rules compatible to 1.1 FCS code. Since 1.1 FCS code only understands
  1405. * day-of-week-in-month rules, we must modify other modes of rules to their
  1406. * approximate equivalent in 1.1 FCS terms. This method is used when streaming
  1407. * out objects of this class. After it is called, the rules will be modified,
  1408. * with a possible loss of information. startMode and endMode will NOT be
  1409. * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
  1410. * since the rule modification is only intended to be temporary.
  1411. */
  1412. private void makeRulesCompatible()
  1413. {
  1414. switch (startMode) {
  1415. case DOM_MODE:
  1416. startDay = 1 + (startDay / 7);
  1417. startDayOfWeek = Calendar.SUNDAY;
  1418. break;
  1419. case DOW_GE_DOM_MODE:
  1420. // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  1421. // that is, Sun>=1 == firstSun.
  1422. if (startDay != 1) {
  1423. startDay = 1 + (startDay / 7);
  1424. }
  1425. break;
  1426. case DOW_LE_DOM_MODE:
  1427. if (startDay >= 30) {
  1428. startDay = -1;
  1429. } else {
  1430. startDay = 1 + (startDay / 7);
  1431. }
  1432. break;
  1433. }
  1434. switch (endMode) {
  1435. case DOM_MODE:
  1436. endDay = 1 + (endDay / 7);
  1437. endDayOfWeek = Calendar.SUNDAY;
  1438. break;
  1439. case DOW_GE_DOM_MODE:
  1440. // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  1441. // that is, Sun>=1 == firstSun.
  1442. if (endDay != 1) {
  1443. endDay = 1 + (endDay / 7);
  1444. }
  1445. break;
  1446. case DOW_LE_DOM_MODE:
  1447. if (endDay >= 30) {
  1448. endDay = -1;
  1449. } else {
  1450. endDay = 1 + (endDay / 7);
  1451. }
  1452. break;
  1453. }
  1454. /*
  1455. * Adjust the start and end times to wall time. This works perfectly
  1456. * well unless it pushes into the next or previous day. If that
  1457. * happens, we attempt to adjust the day rule somewhat crudely. The day
  1458. * rules have been forced into DOW_IN_MONTH mode already, so we change
  1459. * the day of week to move forward or back by a day. It's possible to
  1460. * make a more refined adjustment of the original rules first, but in
  1461. * most cases this extra effort will go to waste once we adjust the day
  1462. * rules anyway.
  1463. */
  1464. switch (startTimeMode) {
  1465. case UTC_TIME:
  1466. startTime += rawOffset;
  1467. break;
  1468. }
  1469. while (startTime < 0) {
  1470. startTime += millisPerDay;
  1471. startDayOfWeek = 1 + ((startDayOfWeek+5) % 7); // Back 1 day
  1472. }
  1473. while (startTime >= millisPerDay) {
  1474. startTime -= millisPerDay;
  1475. startDayOfWeek = 1 + (startDayOfWeek % 7); // Forward 1 day
  1476. }
  1477. switch (endTimeMode) {
  1478. case UTC_TIME:
  1479. endTime += rawOffset + dstSavings;
  1480. break;
  1481. case STANDARD_TIME:
  1482. endTime += dstSavings;
  1483. }
  1484. while (endTime < 0) {
  1485. endTime += millisPerDay;
  1486. endDayOfWeek = 1 + ((endDayOfWeek+5) % 7); // Back 1 day
  1487. }
  1488. while (endTime >= millisPerDay) {
  1489. endTime -= millisPerDay;
  1490. endDayOfWeek = 1 + (endDayOfWeek % 7); // Forward 1 day
  1491. }
  1492. }
  1493. /**
  1494. * Pack the start and end rules into an array of bytes. Only pack
  1495. * data which is not preserved by makeRulesCompatible.
  1496. */
  1497. private byte[] packRules()
  1498. {
  1499. byte[] rules = new byte[6];
  1500. rules[0] = (byte)startDay;
  1501. rules[1] = (byte)startDayOfWeek;
  1502. rules[2] = (byte)endDay;
  1503. rules[3] = (byte)endDayOfWeek;
  1504. // As of serial version 2, include time modes
  1505. rules[4] = (byte)startTimeMode;
  1506. rules[5] = (byte)endTimeMode;
  1507. return rules;
  1508. }
  1509. /**
  1510. * Given an array of bytes produced by packRules, interpret them
  1511. * as the start and end rules.
  1512. */
  1513. private void unpackRules(byte[] rules)
  1514. {
  1515. startDay = rules[0];
  1516. startDayOfWeek = rules[1];
  1517. endDay = rules[2];
  1518. endDayOfWeek = rules[3];
  1519. // As of serial version 2, include time modes
  1520. if (rules.length >= 6) {
  1521. startTimeMode = rules[4];
  1522. endTimeMode = rules[5];
  1523. }
  1524. }
  1525. /**
  1526. * Pack the start and end times into an array of bytes. This is required
  1527. * as of serial version 2.
  1528. */
  1529. private int[] packTimes() {
  1530. int[] times = new int[2];
  1531. times[0] = startTime;
  1532. times[1] = endTime;
  1533. return times;
  1534. }
  1535. /**
  1536. * Unpack the start and end times from an array of bytes. This is required
  1537. * as of serial version 2.
  1538. */
  1539. private void unpackTimes(int[] times) {
  1540. startTime = times[0];
  1541. endTime = times[1];
  1542. }
  1543. /**
  1544. * Save the state of this object to a stream (i.e., serialize it).
  1545. *
  1546. * @serialData We write out two formats, a JDK 1.1 compatible format, using
  1547. * <code>DOW_IN_MONTH_MODE</code> rules, in the required section, followed
  1548. * by the full rules, in packed format, in the optional section. The
  1549. * optional section will be ignored by JDK 1.1 code upon stream in.
  1550. * <p> Contents of the optional section: The length of a byte array is
  1551. * emitted (int); this is 4 as of this release. The byte array of the given
  1552. * length is emitted. The contents of the byte array are the true values of
  1553. * the fields <code>startDay</code>, <code>startDayOfWeek</code>,
  1554. * <code>endDay</code>, and <code>endDayOfWeek</code>. The values of these
  1555. * fields in the required section are approximate values suited to the rule
  1556. * mode <code>DOW_IN_MONTH_MODE</code>, which is the only mode recognized by
  1557. * JDK 1.1.
  1558. */
  1559. private void writeObject(ObjectOutputStream stream)
  1560. throws IOException
  1561. {
  1562. // Construct a binary rule
  1563. byte[] rules = packRules();
  1564. int[] times = packTimes();
  1565. // Convert to 1.1 FCS rules. This step may cause us to lose information.
  1566. makeRulesCompatible();
  1567. // Write out the 1.1 FCS rules
  1568. stream.defaultWriteObject();
  1569. // Write out the binary rules in the optional data area of the stream.
  1570. stream.writeInt(rules.length);
  1571. stream.write(rules);
  1572. stream.writeObject(times);
  1573. // Recover the original rules. This recovers the information lost
  1574. // by makeRulesCompatible.
  1575. unpackRules(rules);
  1576. unpackTimes(times);
  1577. }
  1578. /**
  1579. * Reconstitute this object from a stream (i.e., deserialize it).
  1580. *
  1581. * We handle both JDK 1.1
  1582. * binary formats and full formats with a packed byte array.
  1583. */
  1584. private void readObject(ObjectInputStream stream)
  1585. throws IOException, ClassNotFoundException
  1586. {
  1587. stream.defaultReadObject();
  1588. if (serialVersionOnStream < 1) {
  1589. // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
  1590. // startDayOfWeek and endDayOfWeek were usually uninitialized. We can't do
  1591. // too much, so we assume SUNDAY, which actually works most of the time.
  1592. if (startDayOfWeek == 0) {
  1593. startDayOfWeek = Calendar.SUNDAY;
  1594. }
  1595. if (endDayOfWeek == 0) {
  1596. endDayOfWeek = Calendar.SUNDAY;
  1597. }
  1598. // The variables dstSavings, startMode, and endMode are post-1.1, so they
  1599. // won't be present if we're reading from a 1.1 stream. Fix them up.
  1600. startMode = endMode = DOW_IN_MONTH_MODE;
  1601. dstSavings = millisPerHour;
  1602. } else {
  1603. // For 1.1.4, in addition to the 3 new instance variables, we also
  1604. // store the actual rules (which have not be made compatible with 1.1)
  1605. // in the optional area. Read them in here and parse them.
  1606. int length = stream.readInt();
  1607. byte[] rules = new byte[length];
  1608. stream.readFully(rules);
  1609. unpackRules(rules);
  1610. }
  1611. if (serialVersionOnStream >= 2) {
  1612. int[] times = (int[]) stream.readObject();
  1613. unpackTimes(times);
  1614. }
  1615. serialVersionOnStream = currentSerialVersion;
  1616. }
  1617. }