1. /*
  2. * @(#)SimpleTimeZone.java 1.34 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. /*
  8. * @(#)SimpleTimeZone.java 1.34 01/11/29
  9. *
  10. * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
  11. * (C) Copyright IBM Corp. 1996 - All Rights Reserved
  12. *
  13. * Portions copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
  14. *
  15. * The original version of this source code and documentation is copyrighted
  16. * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
  17. * materials are provided under terms of a License Agreement between Taligent
  18. * and Sun. This technology is protected by multiple US and International
  19. * patents. This notice and attribution to Taligent may not be removed.
  20. * Taligent is a registered trademark of Taligent, Inc.
  21. *
  22. * Permission to use, copy, modify, and distribute this software
  23. * and its documentation for NON-COMMERCIAL purposes and without
  24. * fee is hereby granted provided that this copyright notice
  25. * appears in all copies. Please refer to the file "copyright.html"
  26. * for further important copyright and licensing information.
  27. *
  28. * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  29. * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  30. * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  31. * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  32. * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  33. * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  34. *
  35. */
  36. package java.util;
  37. import java.io.ObjectInputStream;
  38. import java.io.ObjectOutputStream;
  39. import java.io.IOException;
  40. /**
  41. * <code>SimpleTimeZone</code> is a concrete subclass of <code>TimeZone</code>
  42. * that represents a time zone for use with a Gregorian calendar. This
  43. * class does not handle historical changes.
  44. *
  45. * <P>
  46. * Use a negative value for <code>dayOfWeekInMonth</code> to indicate that
  47. * <code>SimpleTimeZone</code> should count from the end of the month backwards.
  48. * For example, Daylight Savings Time ends at the last
  49. * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time.
  50. *
  51. * @see Calendar
  52. * @see GregorianCalendar
  53. * @see TimeZone
  54. * @version 1.34 11/29/01
  55. * @author David Goldsmith, Mark Davis, Chen-Lieh Huang, Alan Liu
  56. */
  57. public class SimpleTimeZone extends TimeZone {
  58. /**
  59. * Constructs a SimpleTimeZone with the given base time zone offset from GMT
  60. * and time zone ID. Timezone IDs can be obtained from
  61. * TimeZone.getAvailableIDs. Normally you should use TimeZone.getDefault to
  62. * construct a TimeZone.
  63. *
  64. * @param rawOffset The given base time zone offset to GMT.
  65. * @param ID The time zone ID which is obtained from
  66. * TimeZone.getAvailableIDs.
  67. */
  68. public SimpleTimeZone(int rawOffset, String ID)
  69. {
  70. this.rawOffset = rawOffset;
  71. setID (ID);
  72. dstSavings = millisPerHour; // In case user sets rules later
  73. }
  74. /**
  75. * Construct a SimpleTimeZone with the given base time zone offset from
  76. * GMT, time zone ID, time to start and end the daylight time. Timezone IDs
  77. * can be obtained from TimeZone.getAvailableIDs. Normally you should use
  78. * TimeZone.getDefault to create a TimeZone. For a time zone that does not
  79. * use daylight saving time, do not use this constructor; instead you should
  80. * use SimpleTimeZone(rawOffset, ID).
  81. *
  82. * By default, this constructor specifies day-of-week-in-month rules. That
  83. * is, if the startDay is 1, and the startDayOfWeek is SUNDAY, then this
  84. * indicates the first Sunday in the startMonth. A startDay of -1 likewise
  85. * indicates the last Sunday. However, by using negative or zero values for
  86. * certain parameters, other types of rules can be specified.
  87. *
  88. * Day of month. To specify an exact day of the month, such as March 1, set
  89. * startDayOfWeek to zero.
  90. *
  91. * Day of week after day of month. To specify the first day of the week
  92. * occurring on or after an exact day of the month, make the day of the week
  93. * negative. For example, if startDay is 5 and startDayOfWeek is -MONDAY,
  94. * this indicates the first Monday on or after the 5th day of the
  95. * startMonth.
  96. *
  97. * Day of week before day of month. To specify the last day of the week
  98. * occurring on or before an exact day of the month, make the day of the
  99. * week and the day of the month negative. For example, if startDay is -21
  100. * and startDayOfWeek is -WEDNESDAY, this indicates the last Wednesday on or
  101. * before the 21st of the startMonth.
  102. *
  103. * The above examples refer to the startMonth, startDay, and startDayOfWeek;
  104. * the same applies for the endMonth, endDay, and endDayOfWeek.
  105. *
  106. * @param rawOffset The given base time zone offset to GMT.
  107. * @param ID The time zone ID which is obtained from
  108. * TimeZone.getAvailableIDs.
  109. * @param startMonth The daylight savings starting month. Month is
  110. * 0-based. eg, 0 for January.
  111. * @param startDay The daylight savings starting
  112. * day-of-week-in-month. Please see the member
  113. * description for an example.
  114. * @param startDayOfWeek The daylight savings starting day-of-week. Please
  115. * see the member description for an example.
  116. * @param startTime The daylight savings starting time in local wall
  117. * time, which is standard time in this case. Please see the
  118. * member description for an example.
  119. * @param endMonth The daylight savings ending month. Month is
  120. * 0-based. eg, 0 for January.
  121. * @param endDay The daylight savings ending day-of-week-in-month.
  122. * Please see the member description for an example.
  123. * @param endDayOfWeek The daylight savings ending day-of-week. Please
  124. * see the member description for an example.
  125. * @param endTime The daylight savings ending time in local wall time,
  126. * which is daylight time in this case. Please see the
  127. * member description for an example.
  128. * @exception IllegalArgumentException the month, day, dayOfWeek, or time
  129. * parameters are out of range for the start or end rule
  130. */
  131. public SimpleTimeZone(int rawOffset, String ID,
  132. int startMonth, int startDay, int startDayOfWeek, int startTime,
  133. int endMonth, int endDay, int endDayOfWeek, int endTime)
  134. {
  135. this(rawOffset, ID,
  136. startMonth, startDay, startDayOfWeek, startTime,
  137. endMonth, endDay, endDayOfWeek, endTime,
  138. millisPerHour);
  139. }
  140. /**
  141. * Constructor. This constructor is identical to the 10-argument
  142. * constructor, but also takes a dstSavings parameter.
  143. * @param dstSavings The amount of time in ms saved during DST.
  144. * @exception IllegalArgumentException the month, day, dayOfWeek, or time
  145. * parameters are out of range for the start or end rule
  146. */
  147. public SimpleTimeZone(int rawOffset, String ID,
  148. int startMonth, int startDay, int startDayOfWeek, int startTime,
  149. int endMonth, int endDay, int endDayOfWeek, int endTime,
  150. int dstSavings)
  151. {
  152. setID(ID);
  153. this.rawOffset = rawOffset;
  154. this.startMonth = startMonth;
  155. this.startDay = startDay;
  156. this.startDayOfWeek = startDayOfWeek;
  157. this.startTime = startTime;
  158. this.endMonth = endMonth;
  159. this.endDay = endDay;
  160. this.endDayOfWeek = endDayOfWeek;
  161. this.endTime = endTime;
  162. this.dstSavings = dstSavings;
  163. // this.useDaylight = true; // Set by decodeRules
  164. decodeRules();
  165. if (dstSavings <= 0) {
  166. throw new IllegalArgumentException("Illegal DST savings");
  167. }
  168. }
  169. /**
  170. * Sets the daylight savings starting year.
  171. *
  172. * @param year The daylight savings starting year.
  173. */
  174. public void setStartYear(int year)
  175. {
  176. startYear = year;
  177. }
  178. /**
  179. * Sets the daylight savings starting rule. For example, Daylight Savings
  180. * Time starts at the first Sunday in April, at 2 AM in standard time.
  181. * Therefore, you can set the start rule by calling:
  182. * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
  183. *
  184. * @param month The daylight savings starting month. Month is
  185. * 0-based. eg, 0 for January.
  186. * @param dayOfWeekInMonth The daylight savings starting
  187. * day-of-week-in-month. Please see the member
  188. * description for an example.
  189. * @param dayOfWeek The daylight savings starting day-of-week.
  190. * Please see the member description for an
  191. * example.
  192. * @param time The daylight savings starting time in local wall
  193. * time, which is standard time in this case. Please see
  194. * the member description for an example.
  195. * @exception IllegalArgumentException the month, dayOfWeekInMonth,
  196. * dayOfWeek, or time parameters are out of range
  197. */
  198. public void setStartRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  199. int time)
  200. {
  201. startMonth = month;
  202. startDay = dayOfWeekInMonth;
  203. startDayOfWeek = dayOfWeek;
  204. startTime = time;
  205. // useDaylight = true; // Set by decodeRules
  206. decodeStartRule();
  207. }
  208. /**
  209. * Sets the DST start rule to a fixed date within a month.
  210. *
  211. * @param month The month in which this rule occurs (0-based).
  212. * @param dayOfMonth The date in that month (1-based).
  213. * @param time The time of that day (number of millis after midnight)
  214. * when DST takes effect in local wall time, which is
  215. * standard time in this case.
  216. * @exception IllegalArgumentException the month,
  217. * dayOfMonth, or time parameters are out of range
  218. */
  219. public void setStartRule(int month, int dayOfMonth, int time) {
  220. setStartRule(month, dayOfMonth, 0, time);
  221. }
  222. /**
  223. * Sets the DST start rule to a weekday before or after a give date within
  224. * a month, e.g., the first Monday on or after the 8th.
  225. *
  226. * @param month The month in which this rule occurs (0-based).
  227. * @param dayOfMonth A date within that month (1-based).
  228. * @param dayOfWeek The day of the week on which this rule occurs.
  229. * @param time The time of that day (number of millis after midnight)
  230. * when DST takes effect in local wall time, which is
  231. * standard time in this case.
  232. * @param after If true, this rule selects the first dayOfWeek on
  233. * or after dayOfMonth. If false, this rule selects
  234. * the last dayOfWeek on or before dayOfMonth.
  235. * @exception IllegalArgumentException the month, dayOfMonth,
  236. * dayOfWeek, or time parameters are out of range
  237. */
  238. public void setStartRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)
  239. {
  240. if (after)
  241. setStartRule(month, dayOfMonth, -dayOfWeek, time);
  242. else
  243. setStartRule(month, -dayOfMonth, -dayOfWeek, time);
  244. }
  245. /**
  246. * Sets the daylight savings ending rule. For example, Daylight Savings Time
  247. * ends at the last (-1) Sunday in October, at 2 AM in standard time.
  248. * Therefore, you can set the end rule by calling:
  249. * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
  250. *
  251. * @param month The daylight savings ending month. Month is
  252. * 0-based. eg, 0 for January.
  253. * @param dayOfWeekInMonth The daylight savings ending
  254. * day-of-week-in-month. Please see the member
  255. * description for an example.
  256. * @param dayOfWeek The daylight savings ending day-of-week. Please
  257. * see the member description for an example.
  258. * @param time The daylight savings ending time in local wall time,
  259. * which is daylight time in this case. Please see the
  260. * member description for an example.
  261. * @exception IllegalArgumentException the month, dayOfWeekInMonth,
  262. * dayOfWeek, or time parameters are out of range
  263. */
  264. public void setEndRule(int month, int dayOfWeekInMonth, int dayOfWeek,
  265. int time)
  266. {
  267. endMonth = month;
  268. endDay = dayOfWeekInMonth;
  269. endDayOfWeek = dayOfWeek;
  270. endTime = time;
  271. // useDaylight = true; // Set by decodeRules
  272. decodeEndRule();
  273. }
  274. /**
  275. * Sets the DST end rule to a fixed date within a month.
  276. *
  277. * @param month The month in which this rule occurs (0-based).
  278. * @param dayOfMonth The date in that month (1-based).
  279. * @param time The time of that day (number of millis after midnight)
  280. * when DST ends in local wall time, which is daylight
  281. * time in this case.
  282. * @exception IllegalArgumentException the month,
  283. * dayOfMonth, or time parameters are out of range
  284. */
  285. public void setEndRule(int month, int dayOfMonth, int time)
  286. {
  287. setEndRule(month, dayOfMonth, 0, time);
  288. }
  289. /**
  290. * Sets the DST end rule to a weekday before or after a give date within
  291. * a month, e.g., the first Monday on or after the 8th.
  292. *
  293. * @param month The month in which this rule occurs (0-based).
  294. * @param dayOfMonth A date within that month (1-based).
  295. * @param dayOfWeek The day of the week on which this rule occurs.
  296. * @param time The time of that day (number of millis after midnight)
  297. * when DST ends in local wall time, which is daylight
  298. * time in this case.
  299. * @param after If true, this rule selects the first dayOfWeek on
  300. * or after dayOfMonth. If false, this rule selects
  301. * the last dayOfWeek on or before dayOfMonth.
  302. * @exception IllegalArgumentException the month, dayOfMonth,
  303. * dayOfWeek, or time parameters are out of range
  304. */
  305. public void setEndRule(int month, int dayOfMonth, int dayOfWeek, int time, boolean after)
  306. {
  307. if (after)
  308. setEndRule(month, dayOfMonth, -dayOfWeek, time);
  309. else
  310. setEndRule(month, -dayOfMonth, -dayOfWeek, time);
  311. }
  312. /**
  313. * Overrides TimeZone
  314. * Gets offset, for current date, modified in case of
  315. * daylight savings. This is the offset to add *to* UTC to get local time.
  316. * Gets the time zone offset, for current date, modified in case of daylight
  317. * savings. This is the offset to add *to* UTC to get local time. Assume
  318. * that the start and end month are distinct. This method may return incorrect
  319. * results for rules that start at the end of February (e.g., last Sunday in
  320. * February) or the beginning of March (e.g., March 1). To avoid such
  321. * inaccuracies, use <code>Calendar.get(ZONE_OFFSET) +
  322. * Calendar.get(DST_OFFSET)</code> instead.
  323. *
  324. * @param era The era of the given date.
  325. * @param year The year in the given date.
  326. * @param month The month in the given date. Month is 0-based. e.g.,
  327. * 0 for January.
  328. * @param day The day-in-month of the given date.
  329. * @param dayOfWeek The day-of-week of the given date.
  330. * @param millis The milliseconds in day in <em>standard</em> local time.
  331. * @return The offset to add *to* GMT to get local time.
  332. * @exception IllegalArgumentException the era, month, day,
  333. * dayOfWeek, or millis parameters are out of range
  334. */
  335. public int getOffset(int era, int year, int month, int day, int dayOfWeek,
  336. int millis)
  337. {
  338. // Check the month before indexing into staticMonthLength. This
  339. // duplicates the test that occurs in the 7-argument getOffset(),
  340. // however, this is unavoidable. We don't mind because this method, in
  341. // fact, should not be called; internal code should always call the
  342. // 7-argument getOffset(), and outside code should use Calendar.get(int
  343. // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
  344. // this method because it's public API. - liu 8/10/98
  345. if (month < Calendar.JANUARY
  346. || month > Calendar.DECEMBER) {
  347. throw new IllegalArgumentException("Illegal month " + month);
  348. }
  349. int monthLength = 0;
  350. if ((era == GregorianCalendar.AD) && isLeapYear(year)) {
  351. monthLength = staticLeapMonthLength[month];
  352. } else {
  353. monthLength = staticMonthLength[month];
  354. }
  355. return getOffset(era, year, month, day, dayOfWeek, millis,
  356. monthLength);
  357. }
  358. /**
  359. * Gets offset, for current date, modified in case of
  360. * daylight savings. This is the offset to add <em>to</em> UTC to get local time.
  361. * Gets the time zone offset, for current date, modified in case of daylight
  362. * savings. This is the offset to add *to* UTC to get local time. Assume
  363. * that the start and end month are distinct.
  364. * @param era The era of the given date.
  365. * @param year The year in the given date.
  366. * @param month The month in the given date. Month is 0-based. e.g.,
  367. * 0 for January.
  368. * @param day The day-in-month of the given date.
  369. * @param dayOfWeek The day-of-week of the given date.
  370. * @param millis The milliseconds in day in <em>standard</em> local time.
  371. * @param monthLength The length of the given month in days.
  372. * @return The offset to add *to* GMT to get local time.
  373. * @exception IllegalArgumentException the era, month, day,
  374. * dayOfWeek, millis, or monthLength parameters are out of range
  375. */
  376. int getOffset(int era, int year, int month, int day, int dayOfWeek,
  377. int millis, int monthLength) {
  378. if (true) {
  379. /* Use this parameter checking code for normal operation. Only one
  380. * of these two blocks should actually get compiled into the class
  381. * file. */
  382. if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC)
  383. || month < Calendar.JANUARY
  384. || month > Calendar.DECEMBER
  385. || day < 1
  386. || day > monthLength
  387. || dayOfWeek < Calendar.SUNDAY
  388. || dayOfWeek > Calendar.SATURDAY
  389. || millis < 0
  390. || millis >= millisPerDay
  391. || monthLength < 28
  392. || monthLength > 31) {
  393. throw new IllegalArgumentException();
  394. }
  395. } else {
  396. /* This parameter checking code is better for debugging, but
  397. * overkill for normal operation. Only one of these two blocks
  398. * should actually get compiled into the class file. */
  399. if (era != GregorianCalendar.AD && era != GregorianCalendar.BC) {
  400. throw new IllegalArgumentException("Illegal era " + era);
  401. }
  402. if (month < Calendar.JANUARY
  403. || month > Calendar.DECEMBER) {
  404. throw new IllegalArgumentException("Illegal month " + month);
  405. }
  406. if (day < 1
  407. || day > monthLength) {
  408. throw new IllegalArgumentException("Illegal day " + day);
  409. }
  410. if (dayOfWeek < Calendar.SUNDAY
  411. || dayOfWeek > Calendar.SATURDAY) {
  412. throw new IllegalArgumentException("Illegal day of week " + dayOfWeek);
  413. }
  414. if (millis < 0
  415. || millis >= millisPerDay) {
  416. throw new IllegalArgumentException("Illegal millis " + millis);
  417. }
  418. if (monthLength < 28
  419. || monthLength > 31) {
  420. throw new IllegalArgumentException("Illegal month length " + monthLength);
  421. }
  422. }
  423. int result = rawOffset;
  424. // Bail out if we are before the onset of daylight savings time
  425. if (!useDaylight || year < startYear || era != GregorianCalendar.AD) return result;
  426. // Check for southern hemisphere. We assume that the start and end
  427. // month are different.
  428. boolean southern = (startMonth > endMonth);
  429. // Compare the date to the starting and ending rules.+1 = date>rule, -1
  430. // = date<rule, 0 = date==rule.
  431. int startCompare = compareToRule(month, monthLength, day, dayOfWeek, millis,
  432. startMode, startMonth, startDayOfWeek,
  433. startDay, startTime);
  434. int endCompare = 0;
  435. /* We don't always have to compute endCompare. For many instances,
  436. * startCompare is enough to determine if we are in DST or not. In the
  437. * northern hemisphere, if we are before the start rule, we can't have
  438. * DST. In the southern hemisphere, if we are after the start rule, we
  439. * must have DST. This is reflected in the way the next if statement
  440. * (not the one immediately following) short circuits. */
  441. if (southern != (startCompare >= 0)) {
  442. /* For the ending rule comparison, we add the dstSavings to the millis
  443. * passed in to convert them from standard to wall time. We then must
  444. * normalize the millis to the range 0..millisPerDay-1. */
  445. millis += dstSavings; // Assume dstSavings > 0
  446. while (millis >= millisPerDay) {
  447. millis -= millisPerDay;
  448. ++day;
  449. dayOfWeek = 1 + (dayOfWeek % 7); // Assume dayOfWeek is one-based
  450. if (day > monthLength) {
  451. day = 1;
  452. /* When incrementing the month, it is desirible to overflow
  453. * from DECEMBER to DECEMBER+1, since we use the result to
  454. * compare against a real month. Wraparound of the value
  455. * leads to bug 4173604. */
  456. ++month;
  457. }
  458. }
  459. endCompare = compareToRule(month, monthLength, day, dayOfWeek, millis,
  460. endMode, endMonth, endDayOfWeek,
  461. endDay, endTime);
  462. }
  463. // Check for both the northern and southern hemisphere cases. We
  464. // assume that in the northern hemisphere, the start rule is before the
  465. // end rule within the calendar year, and vice versa for the southern
  466. // hemisphere.
  467. if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
  468. (southern && (startCompare >= 0 || endCompare < 0)))
  469. result += dstSavings;
  470. return result;
  471. }
  472. /**
  473. * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
  474. * on whether the date is after, equal to, or before the rule date. The
  475. * millis are compared directly against the ruleMillis, so any
  476. * standard-daylight adjustments must be handled by the caller.
  477. *
  478. * @return 1 if the date is after the rule date, -1 if the date is before
  479. * the rule date, or 0 if the date is equal to the rule date.
  480. */
  481. private static int compareToRule(int month, int monthLen, int dayOfMonth,
  482. int dayOfWeek, int millis,
  483. int ruleMode, int ruleMonth, int ruleDayOfWeek,
  484. int ruleDay, int ruleMillis)
  485. {
  486. if (month < ruleMonth) return -1;
  487. else if (month > ruleMonth) return 1;
  488. int ruleDayOfMonth = 0;
  489. switch (ruleMode)
  490. {
  491. case DOM_MODE:
  492. ruleDayOfMonth = ruleDay;
  493. break;
  494. case DOW_IN_MONTH_MODE:
  495. // In this case ruleDay is the day-of-week-in-month
  496. if (ruleDay > 0)
  497. ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
  498. (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
  499. else // Assume ruleDay < 0 here
  500. {
  501. ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
  502. (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
  503. }
  504. break;
  505. case DOW_GE_DOM_MODE:
  506. ruleDayOfMonth = ruleDay +
  507. (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
  508. break;
  509. case DOW_LE_DOM_MODE:
  510. ruleDayOfMonth = ruleDay -
  511. (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
  512. // Note at this point ruleDayOfMonth may be <1, although it will
  513. // be >=1 for well-formed rules.
  514. break;
  515. }
  516. if (dayOfMonth < ruleDayOfMonth) return -1;
  517. else if (dayOfMonth > ruleDayOfMonth) return 1;
  518. if (millis < ruleMillis) return -1;
  519. else if (millis > ruleMillis) return 1;
  520. else return 0;
  521. }
  522. /**
  523. * Overrides TimeZone
  524. * Gets the GMT offset for this time zone.
  525. */
  526. public int getRawOffset()
  527. {
  528. // The given date will be taken into account while
  529. // we have the historical time zone data in place.
  530. return rawOffset;
  531. }
  532. /**
  533. * Overrides TimeZone
  534. * Sets the base time zone offset to GMT.
  535. * This is the offset to add *to* UTC to get local time.
  536. * Please see TimeZone.setRawOffset for descriptions on the parameter.
  537. */
  538. public void setRawOffset(int offsetMillis)
  539. {
  540. this.rawOffset = offsetMillis;
  541. }
  542. /**
  543. * Sets the amount of time in ms that the clock is advanced during DST.
  544. * @param millisSavedDuringDST the number of milliseconds the time is
  545. * advanced with respect to standard time when the daylight savings rules
  546. * are in effect. A positive number, typically one hour (3600000).
  547. */
  548. public void setDSTSavings(int millisSavedDuringDST) {
  549. dstSavings = millisSavedDuringDST;
  550. if (dstSavings <= 0) {
  551. throw new IllegalArgumentException("Illegal DST savings");
  552. }
  553. }
  554. /**
  555. * Returns the amount of time in ms that the clock is advanced during DST.
  556. * @return the number of milliseconds the time is
  557. * advanced with respect to standard time when the daylight savings rules
  558. * are in effect. A positive number, typically one hour (3600000).
  559. */
  560. public int getDSTSavings() {
  561. return dstSavings;
  562. }
  563. /**
  564. * Overrides TimeZone
  565. * Queries if this time zone uses Daylight Savings Time.
  566. */
  567. public boolean useDaylightTime()
  568. {
  569. return useDaylight;
  570. }
  571. /**
  572. * Overrides TimeZone
  573. * Queries if the given date is in Daylight Savings Time.
  574. */
  575. public boolean inDaylightTime(Date date)
  576. {
  577. GregorianCalendar gc = new GregorianCalendar(this);
  578. gc.setTime(date);
  579. return gc.inDaylightTime();
  580. }
  581. /**
  582. * Overrides Cloneable
  583. */
  584. public Object clone()
  585. {
  586. return super.clone();
  587. // other fields are bit-copied
  588. }
  589. /**
  590. * Override hashCode.
  591. * Generates the hash code for the SimpleDateFormat object
  592. */
  593. public synchronized int hashCode()
  594. {
  595. return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
  596. endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
  597. }
  598. /**
  599. * Compares the equality of two SimpleTimeZone objects.
  600. *
  601. * @param obj The SimpleTimeZone object to be compared with.
  602. * @return True if the given obj is the same as this SimpleTimeZone
  603. * object; false otherwise.
  604. */
  605. public boolean equals(Object obj)
  606. {
  607. if (this == obj)
  608. return true;
  609. if (!(obj instanceof SimpleTimeZone))
  610. return false;
  611. SimpleTimeZone that = (SimpleTimeZone) obj;
  612. return getID().equals(that.getID()) &&
  613. hasSameRules(that);
  614. }
  615. /**
  616. * Return true if this zone has the same rules and offset as another zone.
  617. * @param other the TimeZone object to be compared with
  618. * @return true if the given zone has the same rules and offset as this one
  619. */
  620. public boolean hasSameRules(TimeZone other) {
  621. if (this == other) return true;
  622. if (!(other instanceof SimpleTimeZone)) return false;
  623. SimpleTimeZone that = (SimpleTimeZone) other;
  624. return rawOffset == that.rawOffset &&
  625. useDaylight == that.useDaylight &&
  626. (!useDaylight
  627. // Only check rules if using DST
  628. || (dstSavings == that.dstSavings &&
  629. startMode == that.startMode &&
  630. startMonth == that.startMonth &&
  631. startDay == that.startDay &&
  632. startDayOfWeek == that.startDayOfWeek &&
  633. startTime == that.startTime &&
  634. endMode == that.endMode &&
  635. endMonth == that.endMonth &&
  636. endDay == that.endDay &&
  637. endDayOfWeek == that.endDayOfWeek &&
  638. endTime == that.endTime &&
  639. startYear == that.startYear));
  640. }
  641. /**
  642. * Return a string representation of this time zone.
  643. * @return a string representation of this time zone.
  644. */
  645. public String toString() {
  646. return getClass().getName() +
  647. "[id=" + getID() +
  648. ",offset=" + rawOffset +
  649. ",dstSavings=" + dstSavings +
  650. ",useDaylight=" + useDaylight +
  651. ",startYear=" + startYear +
  652. ",startMode=" + startMode +
  653. ",startMonth=" + startMonth +
  654. ",startDay=" + startDay +
  655. ",startDayOfWeek=" + startDayOfWeek +
  656. ",startTime=" + startTime +
  657. ",endMode=" + endMode +
  658. ",endMonth=" + endMonth +
  659. ",endDay=" + endDay +
  660. ",endDayOfWeek=" + endDayOfWeek +
  661. ",endTime=" + endTime + ']';
  662. }
  663. // =======================privates===============================
  664. /**
  665. * The month in which daylight savings time starts. This value must be
  666. * between <code>Calendar.JANUARY</code> and
  667. * <code>Calendar.DECEMBER</code> inclusive. This value must not equal
  668. * <code>endMonth</code>.
  669. * <p>If <code>useDaylight</code> is false, this value is ignored.
  670. * @serial
  671. */
  672. private int startMonth;
  673. /**
  674. * This field has two possible interpretations:
  675. * <dl>
  676. * <dt><code>startMode == DOW_IN_MONTH</code></dt>
  677. * <dd>
  678. * <code>startDay</code> indicates the day of the month of
  679. * <code>startMonth</code> on which daylight
  680. * savings time starts, from 1 to 28, 30, or 31, depending on the
  681. * <code>startMonth</code>.
  682. * </dd>
  683. * <dt><code>startMode != DOW_IN_MONTH</code></dt>
  684. * <dd>
  685. * <code>startDay</code> indicates which <code>startDayOfWeek</code> in th
  686. * month <code>startMonth</code> daylight
  687. * savings time starts on. For example, a value of +1 and a
  688. * <code>startDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
  689. * first Sunday of <code>startMonth</code>. Likewise, +2 would indicate the
  690. * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
  691. * </dd>
  692. * </ul>
  693. * <p>If <code>useDaylight</code> is false, this value is ignored.
  694. * @serial
  695. */
  696. private int startDay;
  697. /**
  698. * The day of the week on which daylight savings time starts. This value
  699. * must be between <code>Calendar.SUNDAY</code> and
  700. * <code>Calendar.SATURDAY</code> inclusive.
  701. * <p>If <code>useDaylight</code> is false or
  702. * <code>startMode == DAY_OF_MONTH</code>, this value is ignored.
  703. * @serial
  704. */
  705. private int startDayOfWeek;
  706. /**
  707. * The time in milliseconds after midnight at which daylight savings
  708. * time starts. This value is expressed as <em>wall time</em>, which means
  709. * it is compared to <em>standard</em> time for the daylight savings start.
  710. * <p>If <code>useDaylight</code> is false, this value is ignored.
  711. * @serial
  712. */
  713. private int startTime;
  714. /**
  715. * The month in which daylight savings time ends. This value must be
  716. * between <code>Calendar.JANUARY</code> and
  717. * <code>Calendar.UNDECIMBER</code>. This value must not equal
  718. * <code>startMonth</code>.
  719. * <p>If <code>useDaylight</code> is false, this value is ignored.
  720. * @serial
  721. */
  722. private int endMonth;
  723. /**
  724. * This field has two possible interpretations:
  725. * <dl>
  726. * <dt><code>endMode == DOW_IN_MONTH</code></dt>
  727. * <dd>
  728. * <code>endDay</code> indicates the day of the month of
  729. * <code>endMonth</code> on which daylight
  730. * savings time ends, from 1 to 28, 30, or 31, depending on the
  731. * <code>endMonth</code>.
  732. * </dd>
  733. * <dt><code>endMode != DOW_IN_MONTH</code></dt>
  734. * <dd>
  735. * <code>endDay</code> indicates which <code>endDayOfWeek</code> in th
  736. * month <code>endMonth</code> daylight
  737. * savings time ends on. For example, a value of +1 and a
  738. * <code>endDayOfWeek</code> of <code>Calendar.SUNDAY</code> indicates the
  739. * first Sunday of <code>endMonth</code>. Likewise, +2 would indicate the
  740. * second Sunday, and -1 the last Sunday. A value of 0 is illegal.
  741. * </dd>
  742. * </ul>
  743. * <p>If <code>useDaylight</code> is false, this value is ignored.
  744. * @serial
  745. */
  746. private int endDay;
  747. /**
  748. * The day of the week on which daylight savings time ends. This value
  749. * must be between <code>Calendar.SUNDAY</code> and
  750. * <code>Calendar.SATURDAY</code> inclusive.
  751. * <p>If <code>useDaylight</code> is false or
  752. * <code>endMode == DAY_OF_MONTH</code>, this value is ignored.
  753. * @serial
  754. */
  755. private int endDayOfWeek;
  756. /**
  757. * The time in milliseconds after midnight at which daylight savings
  758. * time ends. This value is expressed as <em>wall time</em>, which means
  759. * it is compared to <em>daylight</em> time for the daylight savings end.
  760. * <p>If <code>useDaylight</code> is false, this value is ignored.
  761. * @serial
  762. */
  763. private int endTime;
  764. /**
  765. * The year in which daylight savings time is first observed. This is an AD
  766. * value. If this value is less than 1 then daylight savings is observed
  767. * for all AD years.
  768. * <p>If <code>useDaylight</code> is false, this value is ignored.
  769. * @serial
  770. */
  771. private int startYear;
  772. /**
  773. * The offset in milliseconds between this zone and GMT. Negative offsets
  774. * are to the west of Greenwich. To obtain local <em>standard</em> time,
  775. * add the offset to GMT time. To obtain local wall time it may also be
  776. * necessary to add <code>dstSavings</code>.
  777. * @serial
  778. */
  779. private int rawOffset;
  780. /**
  781. * A boolean value which is true if and only if this zone uses daylight
  782. * savings time. If this value is false, several other fields are ignored.
  783. * @serial
  784. */
  785. private boolean useDaylight=false; // indicate if this time zone uses DST
  786. private static final int millisPerHour = 60*60*1000;
  787. private static final int millisPerDay = 24*millisPerHour;
  788. /**
  789. * This field was serialized in JDK 1.1, so we have to keep it that way
  790. * to maintain serialization compatibility. However, there's no need to
  791. * recreate the array each time we create a new time zone.
  792. * @serial An array of bytes containing the values {31, 28, 31, 30, 31, 30,
  793. * 31, 31, 30, 31, 30, 31}. This is ignored as of JDK 1.2, however, it must
  794. * be streamed out for compatibility with JDK 1.1.
  795. */
  796. private final byte monthLength[] = staticMonthLength;
  797. private final static byte staticMonthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  798. private final static byte staticLeapMonthLength[] = {31,29,31,30,31,30,31,31,30,31,30,31};
  799. /**
  800. * The year of the gregorianCutover, with 0 representing
  801. * 1 BC, -1 representing 2 BC, etc.
  802. */
  803. private transient int gregorianCutoverYear = 1582;
  804. /**
  805. * Variables specifying the mode of the start rule. Takes the following
  806. * values:
  807. * <dl>
  808. * <dt><code>DOM_MODE</code></dt>
  809. * <dd>
  810. * Exact day of week; e.g., March 1.
  811. * </dd>
  812. * <dt><code>DOW_IN_MONTH_MODE</code></dt>
  813. * <dd>
  814. * Day of week in month; e.g., last Sunday in March.
  815. * </dd>
  816. * <dt><code>DOW_GE_DOM_MODE</code></dt>
  817. * <dd>
  818. * Day of week after day of month; e.g., Sunday on or after March 15.
  819. * </dd>
  820. * <dt><code>DOW_LE_DOM_MODE</code></dt>
  821. * <dd>
  822. * Day of week before day of month; e.g., Sunday on or before March 15.
  823. * </dd>
  824. * </dl>
  825. * The setting of this field affects the interpretation of the
  826. * <code>startDay</code> field.
  827. * <p>If <code>useDaylight</code> is false, this value is ignored.
  828. * @serial
  829. * @since JDK1.1.4
  830. */
  831. private int startMode;
  832. /**
  833. * Variables specifying the mode of the end rule. Takes the following
  834. * values:
  835. * <dl>
  836. * <dt><code>DOM_MODE</code></dt>
  837. * <dd>
  838. * Exact day of week; e.g., March 1.
  839. * </dd>
  840. * <dt><code>DOW_IN_MONTH_MODE</code></dt>
  841. * <dd>
  842. * Day of week in month; e.g., last Sunday in March.
  843. * </dd>
  844. * <dt><code>DOW_GE_DOM_MODE</code></dt>
  845. * <dd>
  846. * Day of week after day of month; e.g., Sunday on or after March 15.
  847. * </dd>
  848. * <dt><code>DOW_LE_DOM_MODE</code></dt>
  849. * <dd>
  850. * Day of week before day of month; e.g., Sunday on or before March 15.
  851. * </dd>
  852. * </dl>
  853. * The setting of this field affects the interpretation of the
  854. * <code>endDay</code> field.
  855. * <p>If <code>useDaylight</code> is false, this value is ignored.
  856. * @serial
  857. * @since JDK1.1.4
  858. */
  859. private int endMode;
  860. /**
  861. * A positive value indicating the amount of time saved during DST in
  862. * milliseconds.
  863. * Typically one hour (3600000); sometimes 30 minutes (1800000).
  864. * <p>If <code>useDaylight</code> is false, this value is ignored.
  865. * @serial
  866. * @since JDK1.1.4
  867. */
  868. private int dstSavings;
  869. /**
  870. * Constants specifying values of startMode and endMode.
  871. */
  872. private static final int DOM_MODE = 1; // Exact day of month, "Mar 1"
  873. private static final int DOW_IN_MONTH_MODE = 2; // Day of week in month, "lastSun"
  874. private static final int DOW_GE_DOM_MODE = 3; // Day of week after day of month, "Sun>=15"
  875. private static final int DOW_LE_DOM_MODE = 4; // Day of week before day of month, "Sun<=21"
  876. // Proclaim compatibility with 1.1
  877. static final long serialVersionUID = -403250971215465050L;
  878. // the internal serial version which says which version was written
  879. // - 0 (default) for version up to JDK 1.1.3
  880. // - 1 for version from JDK 1.1.4, which includes 3 new fields
  881. static final int currentSerialVersion = 1;
  882. /**
  883. * The version of the serialized data on the stream. Possible values:
  884. * <dl>
  885. * <dt><b>0</b> or not present on stream</dt>
  886. * <dd>
  887. * JDK 1.1.3 or earlier.
  888. * </dd>
  889. * <dt><b>1</b></dt>
  890. * <dd>
  891. * JDK 1.1.4 or later. Includes three new fields: <code>startMode</code>,
  892. * <code>endMode</code>, and <code>dstSavings</code>.
  893. * </dd>
  894. * </dl>
  895. * When streaming out this class, the most recent format
  896. * and the highest allowable <code>serialVersionOnStream</code>
  897. * is written.
  898. * @serial
  899. * @since JDK1.1.4
  900. */
  901. private int serialVersionOnStream = currentSerialVersion;
  902. //----------------------------------------------------------------------
  903. // Rule representation
  904. //
  905. // We represent the following flavors of rules:
  906. // 5 the fifth of the month
  907. // lastSun the last Sunday in the month
  908. // lastMon the last Monday in the month
  909. // Sun>=8 first Sunday on or after the eighth
  910. // Sun<=25 last Sunday on or before the 25th
  911. // This is further complicated by the fact that we need to remain
  912. // backward compatible with the 1.1 FCS. Finally, we need to minimize
  913. // API changes. In order to satisfy these requirements, we support
  914. // three representation systems, and we translate between them.
  915. //
  916. // INTERNAL REPRESENTATION
  917. // This is the format SimpleTimeZone objects take after construction or
  918. // streaming in is complete. Rules are represented directly, using an
  919. // unencoded format. We will discuss the start rule only below; the end
  920. // rule is analogous.
  921. // startMode Takes on enumerated values DAY_OF_MONTH,
  922. // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
  923. // startDay The day of the month, or for DOW_IN_MONTH mode, a
  924. // value indicating which DOW, such as +1 for first,
  925. // +2 for second, -1 for last, etc.
  926. // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
  927. //
  928. // ENCODED REPRESENTATION
  929. // This is the format accepted by the constructor and by setStartRule()
  930. // and setEndRule(). It uses various combinations of positive, negative,
  931. // and zero values to encode the different rules. This representation
  932. // allows us to specify all the different rule flavors without altering
  933. // the API.
  934. // MODE startMonth startDay startDayOfWeek
  935. // DOW_IN_MONTH_MODE >=0 !=0 >0
  936. // DOM_MODE >=0 >0 ==0
  937. // DOW_GE_DOM_MODE >=0 >0 <0
  938. // DOW_LE_DOM_MODE >=0 <0 <0
  939. // (no DST) don't care ==0 don't care
  940. //
  941. // STREAMED REPRESENTATION
  942. // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
  943. // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
  944. // flag useDaylight. When we stream an object out, we translate into an
  945. // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
  946. // and used by 1.1 code. Following that, we write out the full
  947. // representation separately so that contemporary code can recognize and
  948. // parse it. The full representation is written in a "packed" format,
  949. // consisting of a version number, a length, and an array of bytes. Future
  950. // versions of this class may specify different versions. If they wish to
  951. // include additional data, they should do so by storing them after the
  952. // packed representation below.
  953. //----------------------------------------------------------------------
  954. /**
  955. * Given a set of encoded rules in startDay and startDayOfMonth, decode
  956. * them and set the startMode appropriately. Do the same for endDay and
  957. * endDayOfMonth. Upon entry, the day of week variables may be zero or
  958. * negative, in order to indicate special modes. The day of month
  959. * variables may also be negative. Upon exit, the mode variables will be
  960. * set, and the day of week and day of month variables will be positive.
  961. * This method also recognizes a startDay or endDay of zero as indicating
  962. * no DST.
  963. */
  964. private void decodeRules()
  965. {
  966. decodeStartRule();
  967. decodeEndRule();
  968. }
  969. /**
  970. * Decode the start rule and validate the parameters. The parameters are
  971. * expected to be in encoded form, which represents the various rule modes
  972. * by negating or zeroing certain values. Representation formats are:
  973. * <p>
  974. * <pre>
  975. * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
  976. * ------------ ----- -------- -------- ----------
  977. * month 0..11 same same same don't care
  978. * day -5..5 1..31 1..31 -1..-31 0
  979. * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
  980. * time 0..ONEDAY same same same don't care
  981. * </pre>
  982. * The range for month does not include UNDECIMBER since this class is
  983. * really specific to GregorianCalendar, which does not use that month.
  984. * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
  985. * end rule is an exclusive limit point. That is, the range of times that
  986. * are in DST include those >= the start and < the end. For this reason,
  987. * it should be possible to specify an end of ONEDAY in order to include the
  988. * entire day. Although this is equivalent to time 0 of the following day,
  989. * it's not always possible to specify that, for example, on December 31.
  990. * While arguably the start range should still be 0..ONEDAY-1, we keep
  991. * the start and end ranges the same for consistency.
  992. */
  993. private void decodeStartRule() {
  994. useDaylight = (startDay != 0) && (endDay != 0);
  995. if (startDay != 0) {
  996. if (startMonth < Calendar.JANUARY || startMonth > Calendar.DECEMBER) {
  997. throw new IllegalArgumentException(
  998. "Illegal start month " + startMonth);
  999. }
  1000. if (startTime < 0 || startTime > millisPerDay) {
  1001. throw new IllegalArgumentException(
  1002. "Illegal start time " + startTime);
  1003. }
  1004. if (startDayOfWeek == 0) {
  1005. startMode = DOM_MODE;
  1006. } else {
  1007. if (startDayOfWeek > 0) {
  1008. startMode = DOW_IN_MONTH_MODE;
  1009. } else {
  1010. startDayOfWeek = -startDayOfWeek;
  1011. if (startDay > 0) {
  1012. startMode = DOW_GE_DOM_MODE;
  1013. } else {
  1014. startDay = -startDay;
  1015. startMode = DOW_LE_DOM_MODE;
  1016. }
  1017. }
  1018. if (startDayOfWeek > Calendar.SATURDAY) {
  1019. throw new IllegalArgumentException(
  1020. "Illegal start day of week " + startDayOfWeek);
  1021. }
  1022. }
  1023. if (startMode == DOW_IN_MONTH_MODE) {
  1024. if (startDay < -5 || startDay > 5) {
  1025. throw new IllegalArgumentException(
  1026. "Illegal start day of week in month " + startDay);
  1027. }
  1028. } else if (startDay > staticMonthLength[startMonth]) {
  1029. throw new IllegalArgumentException(
  1030. "Illegal start day " + startDay);
  1031. }
  1032. }
  1033. }
  1034. /**
  1035. * Decode the end rule and validate the parameters. This method is exactly
  1036. * analogous to decodeStartRule().
  1037. * @see decodeStartRule
  1038. */
  1039. private void decodeEndRule() {
  1040. useDaylight = (startDay != 0) && (endDay != 0);
  1041. if (endDay != 0) {
  1042. if (endMonth < Calendar.JANUARY || endMonth > Calendar.DECEMBER) {
  1043. throw new IllegalArgumentException(
  1044. "Illegal end month " + endMonth);
  1045. }
  1046. if (endTime < 0 || endTime > millisPerDay) {
  1047. throw new IllegalArgumentException(
  1048. "Illegal end time " + endTime);
  1049. }
  1050. if (endDayOfWeek == 0) {
  1051. endMode = DOM_MODE;
  1052. } else {
  1053. if (endDayOfWeek > 0) {
  1054. endMode = DOW_IN_MONTH_MODE;
  1055. } else {
  1056. endDayOfWeek = -endDayOfWeek;
  1057. if (endDay > 0) {
  1058. endMode = DOW_GE_DOM_MODE;
  1059. } else {
  1060. endDay = -endDay;
  1061. endMode = DOW_LE_DOM_MODE;
  1062. }
  1063. }
  1064. if (endDayOfWeek > Calendar.SATURDAY) {
  1065. throw new IllegalArgumentException(
  1066. "Illegal end day of week " + endDayOfWeek);
  1067. }
  1068. }
  1069. if (endMode == DOW_IN_MONTH_MODE) {
  1070. if (endDay < -5 || endDay > 5) {
  1071. throw new IllegalArgumentException(
  1072. "Illegal end day of week in month " + endDay);
  1073. }
  1074. } else if (endDay > staticMonthLength[endMonth]) {
  1075. throw new IllegalArgumentException(
  1076. "Illegal end day " + endDay);
  1077. }
  1078. }
  1079. }
  1080. /**
  1081. * Make rules compatible to 1.1 FCS code. Since 1.1 FCS code only understands
  1082. * day-of-week-in-month rules, we must modify other modes of rules to their
  1083. * approximate equivalent in 1.1 FCS terms. This method is used when streaming
  1084. * out objects of this class. After it is called, the rules will be modified,
  1085. * with a possible loss of information. startMode and endMode will NOT be
  1086. * altered, even though semantically they should be set to DOW_IN_MONTH_MODE,
  1087. * since the rule modification is only intended to be temporary.
  1088. */
  1089. private void makeRulesCompatible()
  1090. {
  1091. switch (startMode)
  1092. {
  1093. case DOM_MODE:
  1094. startDay = 1 + (startDay / 7);
  1095. startDayOfWeek = Calendar.SUNDAY;
  1096. break;
  1097. case DOW_GE_DOM_MODE:
  1098. // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  1099. // that is, Sun>=1 == firstSun.
  1100. if (startDay != 1)
  1101. startDay = 1 + (startDay / 7);
  1102. break;
  1103. case DOW_LE_DOM_MODE:
  1104. if (startDay >= 30)
  1105. startDay = -1;
  1106. else
  1107. startDay = 1 + (startDay / 7);
  1108. break;
  1109. }
  1110. switch (endMode)
  1111. {
  1112. case DOM_MODE:
  1113. endDay = 1 + (endDay / 7);
  1114. endDayOfWeek = Calendar.SUNDAY;
  1115. break;
  1116. case DOW_GE_DOM_MODE:
  1117. // A day-of-month of 1 is equivalent to DOW_IN_MONTH_MODE
  1118. // that is, Sun>=1 == firstSun.
  1119. if (endDay != 1)
  1120. endDay = 1 + (endDay / 7);
  1121. break;
  1122. case DOW_LE_DOM_MODE:
  1123. if (endDay >= 30)
  1124. endDay = -1;
  1125. else
  1126. endDay = 1 + (endDay / 7);
  1127. break;
  1128. }
  1129. }
  1130. /**
  1131. * Determines if the given year is a leap year. Returns true if the
  1132. * given year is a leap year.
  1133. * @param year the given year.
  1134. * @return true if the given year is a leap year; false otherwise.
  1135. */
  1136. private boolean isLeapYear(int year) {
  1137. return year >= gregorianCutoverYear ?
  1138. ((year%4 == 0) && ((year%100 != 0) || (year%400 == 0))) : // Gregorian
  1139. (year%4 == 0); // Julian
  1140. }
  1141. /**
  1142. * Pack the start and end rules into an array of bytes. Only pack
  1143. * data which is not preserved by makeRulesCompatible.
  1144. */
  1145. private byte[] packRules()
  1146. {
  1147. byte[] rules = new byte[4];
  1148. rules[0] = (byte)startDay;
  1149. rules[1] = (byte)startDayOfWeek;
  1150. rules[2] = (byte)endDay;
  1151. rules[3] = (byte)endDayOfWeek;
  1152. return rules;
  1153. }
  1154. /**
  1155. * Given an array of bytes produced by packRules, interpret them
  1156. * as the start and end rules.
  1157. */
  1158. private void unpackRules(byte[] rules)
  1159. {
  1160. startDay = rules[0];
  1161. startDayOfWeek = rules[1];
  1162. endDay = rules[2];
  1163. endDayOfWeek = rules[3];
  1164. }
  1165. /**
  1166. * Save the state of this object to a stream (i.e., serialize it).
  1167. *
  1168. * @serialData We write out two formats, a JDK 1.1 compatible format, using
  1169. * <code>DOW_IN_MONTH_MODE</code> rules, in the required section, followed
  1170. * by the full rules, in packed format, in the optional section. The
  1171. * optional section will be ignored by JDK 1.1 code upon stream in.
  1172. * <p> Contents of the optional section: The length of a byte array is
  1173. * emitted (int); this is 4 as of this release. The byte array of the given
  1174. * length is emitted. The contents of the byte array are the true values of
  1175. * the fields <code>startDay</code>, <code>startDayOfWeek</code>,
  1176. * <code>endDay</code>, and <code>endDayOfWeek</code>. The values of these
  1177. * fields in the required section are approximate values suited to the rule
  1178. * mode <code>DOW_IN_MONTH_MODE</code>, which is the only mode recognized by
  1179. * JDK 1.1.
  1180. */
  1181. private void writeObject(ObjectOutputStream stream)
  1182. throws IOException
  1183. {
  1184. // Construct a binary rule
  1185. byte[] rules = packRules();
  1186. // Convert to 1.1 FCS rules. This step may cause us to lose information.
  1187. makeRulesCompatible();
  1188. // Write out the 1.1 FCS rules
  1189. stream.defaultWriteObject();
  1190. // Write out the binary rules in the optional data area of the stream.
  1191. stream.writeInt(rules.length);
  1192. stream.write(rules);
  1193. // Recover the original rules. This recovers the information lost
  1194. // by makeRulesCompatible.
  1195. unpackRules(rules);
  1196. }
  1197. /**
  1198. * Reconstitute this object from a stream (i.e., deserialize it).
  1199. *
  1200. * We handle both JDK 1.1
  1201. * binary formats and full formats with a packed byte array.
  1202. */
  1203. private void readObject(ObjectInputStream stream)
  1204. throws IOException, ClassNotFoundException
  1205. {
  1206. stream.defaultReadObject();
  1207. if (serialVersionOnStream < 1)
  1208. {
  1209. // Fix a bug in the 1.1 SimpleTimeZone code -- namely,
  1210. // startDayOfWeek and endDayOfWeek were usually uninitialized. We can't do
  1211. // too much, so we assume SUNDAY, which actually works most of the time.
  1212. if (startDayOfWeek == 0) startDayOfWeek = Calendar.SUNDAY;
  1213. if (endDayOfWeek == 0) endDayOfWeek = Calendar.SUNDAY;
  1214. // The variables dstSavings, startMode, and endMode are post-1.1, so they
  1215. // won't be present if we're reading from a 1.1 stream. Fix them up.
  1216. startMode = endMode = DOW_IN_MONTH_MODE;
  1217. dstSavings = millisPerHour;
  1218. }
  1219. else
  1220. {
  1221. // For 1.1.4, in addition to the 3 new instance variables, we also
  1222. // store the actual rules (which have not be made compatible with 1.1)
  1223. // in the optional area. Read them in here and parse them.
  1224. int length = stream.readInt();
  1225. byte[] rules = new byte[length];
  1226. stream.readFully(rules);
  1227. unpackRules(rules);
  1228. }
  1229. serialVersionOnStream = currentSerialVersion;
  1230. }
  1231. }
  1232. //eof