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