1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.util; 19 20import java.io.IOException; 21import java.io.ObjectInputStream; 22import java.io.ObjectOutputStream; 23import java.io.ObjectStreamField; 24 25/** 26 * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone} 27 * that represents a time zone for use with a Gregorian calendar. This class 28 * does not handle historical changes. 29 * <p> 30 * Use a negative value for {@code dayOfWeekInMonth} to indicate that 31 * {@code SimpleTimeZone} should count from the end of the month 32 * backwards. For example, Daylight Savings Time ends at the last 33 * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time. 34 * 35 * @see Calendar 36 * @see GregorianCalendar 37 * @see TimeZone 38 */ 39public class SimpleTimeZone extends TimeZone { 40 41 private static final long serialVersionUID = -403250971215465050L; 42 43 private int rawOffset; 44 45 private int startYear, startMonth, startDay, startDayOfWeek, startTime; 46 47 private int endMonth, endDay, endDayOfWeek, endTime; 48 49 private int startMode, endMode; 50 51 private static final int DOM_MODE = 1, DOW_IN_MONTH_MODE = 2, 52 DOW_GE_DOM_MODE = 3, DOW_LE_DOM_MODE = 4; 53 54 /** 55 * The constant for representing a start or end time in GMT time mode. 56 */ 57 public static final int UTC_TIME = 2; 58 59 /** 60 * The constant for representing a start or end time in standard local time mode, 61 * based on timezone's raw offset from GMT; does not include Daylight 62 * savings. 63 */ 64 public static final int STANDARD_TIME = 1; 65 66 /** 67 * The constant for representing a start or end time in local wall clock time 68 * mode, based on timezone's adjusted offset from GMT; includes 69 * Daylight savings. 70 */ 71 public static final int WALL_TIME = 0; 72 73 private boolean useDaylight; 74 75 private int dstSavings = 3600000; 76 77 /** 78 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT 79 * and time zone ID. Timezone IDs can be obtained from 80 * {@code TimeZone.getAvailableIDs}. Normally you should use {@code TimeZone.getDefault} to 81 * construct a {@code TimeZone}. 82 * 83 * @param offset 84 * the given base time zone offset to GMT. 85 * @param name 86 * the time zone ID which is obtained from 87 * {@code TimeZone.getAvailableIDs}. 88 */ 89 public SimpleTimeZone(int offset, final String name) { 90 setID(name); 91 rawOffset = offset; 92 } 93 94 /** 95 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT, 96 * time zone ID, and times to start and end the daylight savings time. Timezone IDs can 97 * be obtained from {@code TimeZone.getAvailableIDs}. Normally you should use 98 * {@code TimeZone.getDefault} to create a {@code TimeZone}. For a time zone that does not 99 * use daylight saving time, do not use this constructor; instead you should 100 * use {@code SimpleTimeZone(rawOffset, ID)}. 101 * <p> 102 * By default, this constructor specifies day-of-week-in-month rules. That 103 * is, if the {@code startDay} is 1, and the {@code startDayOfWeek} is {@code SUNDAY}, then this 104 * indicates the first Sunday in the {@code startMonth}. A {@code startDay} of -1 likewise 105 * indicates the last Sunday. However, by using negative or zero values for 106 * certain parameters, other types of rules can be specified. 107 * <p> 108 * Day of month: To specify an exact day of the month, such as March 1, set 109 * {@code startDayOfWeek} to zero. 110 * <p> 111 * Day of week after day of month: To specify the first day of the week 112 * occurring on or after an exact day of the month, make the day of the week 113 * negative. For example, if {@code startDay} is 5 and {@code startDayOfWeek} is {@code -MONDAY}, 114 * this indicates the first Monday on or after the 5th day of the 115 * {@code startMonth}. 116 * <p> 117 * Day of week before day of month: To specify the last day of the week 118 * occurring on or before an exact day of the month, make the day of the 119 * week and the day of the month negative. For example, if {@code startDay} is {@code -21} 120 * and {@code startDayOfWeek} is {@code -WEDNESDAY}, this indicates the last Wednesday on or 121 * before the 21st of the {@code startMonth}. 122 * <p> 123 * The above examples refer to the {@code startMonth}, {@code startDay}, and {@code startDayOfWeek}; 124 * the same applies for the {@code endMonth}, {@code endDay}, and {@code endDayOfWeek}. 125 * <p> 126 * The daylight savings time difference is set to the default value: one hour. 127 * 128 * @param offset 129 * the given base time zone offset to GMT. 130 * @param name 131 * the time zone ID which is obtained from 132 * {@code TimeZone.getAvailableIDs}. 133 * @param startMonth 134 * the daylight savings starting month. The month indexing is 0-based. eg, 0 135 * for January. 136 * @param startDay 137 * the daylight savings starting day-of-week-in-month. Please see 138 * the member description for an example. 139 * @param startDayOfWeek 140 * the daylight savings starting day-of-week. Please see the 141 * member description for an example. 142 * @param startTime 143 * the daylight savings starting time in local wall time, which 144 * is standard time in this case. Please see the member 145 * description for an example. 146 * @param endMonth 147 * the daylight savings ending month. The month indexing is 0-based. eg, 0 for 148 * January. 149 * @param endDay 150 * the daylight savings ending day-of-week-in-month. Please see 151 * the member description for an example. 152 * @param endDayOfWeek 153 * the daylight savings ending day-of-week. Please see the member 154 * description for an example. 155 * @param endTime 156 * the daylight savings ending time in local wall time, which is 157 * daylight time in this case. Please see the member description 158 * for an example. 159 * @throws IllegalArgumentException 160 * if the month, day, dayOfWeek, or time parameters are out of 161 * range for the start or end rule. 162 */ 163 public SimpleTimeZone(int offset, String name, int startMonth, 164 int startDay, int startDayOfWeek, int startTime, int endMonth, 165 int endDay, int endDayOfWeek, int endTime) { 166 this(offset, name, startMonth, startDay, startDayOfWeek, startTime, 167 endMonth, endDay, endDayOfWeek, endTime, 3600000); 168 } 169 170 /** 171 * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT, 172 * time zone ID, times to start and end the daylight savings time, and 173 * the daylight savings time difference in milliseconds. 174 * 175 * @param offset 176 * the given base time zone offset to GMT. 177 * @param name 178 * the time zone ID which is obtained from 179 * {@code TimeZone.getAvailableIDs}. 180 * @param startMonth 181 * the daylight savings starting month. Month is 0-based. eg, 0 182 * for January. 183 * @param startDay 184 * the daylight savings starting day-of-week-in-month. Please see 185 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 186 * @param startDayOfWeek 187 * the daylight savings starting day-of-week. Please see the 188 * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 189 * @param startTime 190 * The daylight savings starting time in local wall time, which 191 * is standard time in this case. Please see the description of 192 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 193 * @param endMonth 194 * the daylight savings ending month. Month is 0-based. eg, 0 for 195 * January. 196 * @param endDay 197 * the daylight savings ending day-of-week-in-month. Please see 198 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 199 * @param endDayOfWeek 200 * the daylight savings ending day-of-week. Please see the description of 201 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 202 * @param endTime 203 * the daylight savings ending time in local wall time, which is 204 * daylight time in this case. Please see the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} 205 * for an example. 206 * @param daylightSavings 207 * the daylight savings time difference in milliseconds. 208 * @throws IllegalArgumentException 209 * if the month, day, dayOfWeek, or time parameters are out of 210 * range for the start or end rule. 211 */ 212 public SimpleTimeZone(int offset, String name, int startMonth, 213 int startDay, int startDayOfWeek, int startTime, int endMonth, 214 int endDay, int endDayOfWeek, int endTime, int daylightSavings) { 215 this(offset, name); 216 if (daylightSavings <= 0) { 217 throw new IllegalArgumentException("Invalid daylightSavings: " + daylightSavings); 218 } 219 dstSavings = daylightSavings; 220 221 this.startMonth = startMonth; 222 this.startDay = startDay; 223 this.startDayOfWeek = startDayOfWeek; 224 this.startTime = startTime; 225 setStartMode(); 226 this.endMonth = endMonth; 227 this.endDay = endDay; 228 this.endDayOfWeek = endDayOfWeek; 229 this.endTime = endTime; 230 setEndMode(); 231 } 232 233 /** 234 * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT, 235 * time zone ID, times to start and end the daylight savings time including a 236 * mode specifier, the daylight savings time difference in milliseconds. 237 * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or 238 * {@link #UTC_TIME}. 239 * 240 * @param offset 241 * the given base time zone offset to GMT. 242 * @param name 243 * the time zone ID which is obtained from 244 * {@code TimeZone.getAvailableIDs}. 245 * @param startMonth 246 * the daylight savings starting month. The month indexing is 0-based. eg, 0 247 * for January. 248 * @param startDay 249 * the daylight savings starting day-of-week-in-month. Please see 250 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 251 * @param startDayOfWeek 252 * the daylight savings starting day-of-week. Please see the 253 * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 254 * @param startTime 255 * the time of day in milliseconds on which daylight savings 256 * time starts, based on the {@code startTimeMode}. 257 * @param startTimeMode 258 * the mode (UTC, standard, or wall time) of the start time 259 * value. 260 * @param endDay 261 * the day of the week on which daylight savings time ends. 262 * @param endMonth 263 * the daylight savings ending month. The month indexing is 0-based. eg, 0 for 264 * January. 265 * @param endDayOfWeek 266 * the daylight savings ending day-of-week. Please see the description of 267 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 268 * @param endTime 269 * the time of day in milliseconds on which daylight savings 270 * time ends, based on the {@code endTimeMode}. 271 * @param endTimeMode 272 * the mode (UTC, standard, or wall time) of the end time value. 273 * @param daylightSavings 274 * the daylight savings time difference in milliseconds. 275 * @throws IllegalArgumentException 276 * if the month, day, dayOfWeek, or time parameters are out of 277 * range for the start or end rule. 278 */ 279 public SimpleTimeZone(int offset, String name, int startMonth, 280 int startDay, int startDayOfWeek, int startTime, int startTimeMode, 281 int endMonth, int endDay, int endDayOfWeek, int endTime, 282 int endTimeMode, int daylightSavings) { 283 284 this(offset, name, startMonth, startDay, startDayOfWeek, startTime, 285 endMonth, endDay, endDayOfWeek, endTime, daylightSavings); 286 startMode = startTimeMode; 287 endMode = endTimeMode; 288 } 289 290 /** 291 * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight 292 * savings time rules as this SimpleTimeZone. 293 * 294 * @return a shallow copy of this {@code SimpleTimeZone}. 295 * @see java.lang.Cloneable 296 */ 297 @Override 298 public Object clone() { 299 SimpleTimeZone zone = (SimpleTimeZone) super.clone(); 300 return zone; 301 } 302 303 /** 304 * Compares the specified object to this {@code SimpleTimeZone} and returns whether they 305 * are equal. The object must be an instance of {@code SimpleTimeZone} and have the 306 * same internal data. 307 * 308 * @param object 309 * the object to compare with this object. 310 * @return {@code true} if the specified object is equal to this 311 * {@code SimpleTimeZone}, {@code false} otherwise. 312 * @see #hashCode 313 */ 314 @Override 315 public boolean equals(Object object) { 316 if (!(object instanceof SimpleTimeZone)) { 317 return false; 318 } 319 SimpleTimeZone tz = (SimpleTimeZone) object; 320 return getID().equals(tz.getID()) 321 && rawOffset == tz.rawOffset 322 && useDaylight == tz.useDaylight 323 && (!useDaylight || (startYear == tz.startYear 324 && startMonth == tz.startMonth 325 && startDay == tz.startDay && startMode == tz.startMode 326 && startDayOfWeek == tz.startDayOfWeek 327 && startTime == tz.startTime && endMonth == tz.endMonth 328 && endDay == tz.endDay 329 && endDayOfWeek == tz.endDayOfWeek 330 && endTime == tz.endTime && endMode == tz.endMode && dstSavings == tz.dstSavings)); 331 } 332 333 @Override 334 public int getDSTSavings() { 335 if (!useDaylight) { 336 return 0; 337 } 338 return dstSavings; 339 } 340 341 @Override 342 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int time) { 343 if (era != GregorianCalendar.BC && era != GregorianCalendar.AD) { 344 throw new IllegalArgumentException("Invalid era: " + era); 345 } 346 checkRange(month, dayOfWeek, time); 347 if (month != Calendar.FEBRUARY || day != 29 || !isLeapYear(year)) { 348 checkDay(month, day); 349 } 350 351 if (!useDaylightTime() || era != GregorianCalendar.AD || year < startYear) { 352 return rawOffset; 353 } 354 if (endMonth < startMonth) { 355 if (month > endMonth && month < startMonth) { 356 return rawOffset; 357 } 358 } else { 359 if (month < startMonth || month > endMonth) { 360 return rawOffset; 361 } 362 } 363 364 int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day); 365 if (month == startMonth) { 366 switch (startMode) { 367 case DOM_MODE: 368 ruleDay = startDay; 369 break; 370 case DOW_IN_MONTH_MODE: 371 if (startDay >= 0) { 372 ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1 373 + (startDay - 1) * 7; 374 } else { 375 daysInMonth = GregorianCalendar.DaysInMonth[startMonth]; 376 if (startMonth == Calendar.FEBRUARY && isLeapYear( 377 year)) { 378 daysInMonth += 1; 379 } 380 ruleDay = daysInMonth 381 + 1 382 + mod7(startDayOfWeek 383 - (firstDayOfMonth + daysInMonth)) 384 + startDay * 7; 385 } 386 break; 387 case DOW_GE_DOM_MODE: 388 ruleDay = startDay 389 + mod7(startDayOfWeek 390 - (firstDayOfMonth + startDay - 1)); 391 break; 392 case DOW_LE_DOM_MODE: 393 ruleDay = startDay 394 + mod7(startDayOfWeek 395 - (firstDayOfMonth + startDay - 1)); 396 if (ruleDay != startDay) { 397 ruleDay -= 7; 398 } 399 break; 400 } 401 if (ruleDay > day || ruleDay == day && time < startTime) { 402 return rawOffset; 403 } 404 } 405 406 int ruleTime = endTime - dstSavings; 407 int nextMonth = (month + 1) % 12; 408 if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) { 409 switch (endMode) { 410 case DOM_MODE: 411 ruleDay = endDay; 412 break; 413 case DOW_IN_MONTH_MODE: 414 if (endDay >= 0) { 415 ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1 416 + (endDay - 1) * 7; 417 } else { 418 daysInMonth = GregorianCalendar.DaysInMonth[endMonth]; 419 if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) { 420 daysInMonth++; 421 } 422 ruleDay = daysInMonth 423 + 1 424 + mod7(endDayOfWeek 425 - (firstDayOfMonth + daysInMonth)) + endDay 426 * 7; 427 } 428 break; 429 case DOW_GE_DOM_MODE: 430 ruleDay = endDay 431 + mod7( 432 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 433 break; 434 case DOW_LE_DOM_MODE: 435 ruleDay = endDay 436 + mod7( 437 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 438 if (ruleDay != endDay) { 439 ruleDay -= 7; 440 } 441 break; 442 } 443 444 int ruleMonth = endMonth; 445 if (ruleTime < 0) { 446 int changeDays = 1 - (ruleTime / 86400000); 447 ruleTime = (ruleTime % 86400000) + 86400000; 448 ruleDay -= changeDays; 449 if (ruleDay <= 0) { 450 if (--ruleMonth < Calendar.JANUARY) { 451 ruleMonth = Calendar.DECEMBER; 452 } 453 ruleDay += GregorianCalendar.DaysInMonth[ruleMonth]; 454 if (ruleMonth == Calendar.FEBRUARY && isLeapYear(year)) { 455 ruleDay++; 456 } 457 } 458 } 459 460 if (month == ruleMonth) { 461 if (ruleDay < day || ruleDay == day && time >= ruleTime) { 462 return rawOffset; 463 } 464 } else if (nextMonth != ruleMonth) { 465 return rawOffset; 466 } 467 } 468 return rawOffset + dstSavings; 469 } 470 471 @Override 472 public int getOffset(long time) { 473 // Simplified variant of the ICU4J code. 474 if (!useDaylightTime()) { 475 return rawOffset; 476 } 477 int[] fields = Grego.timeToFields(time + rawOffset, null); 478 return getOffset(GregorianCalendar.AD, fields[0], fields[1], fields[2], 479 fields[3], fields[5]); 480 } 481 482 @Override 483 public int getRawOffset() { 484 return rawOffset; 485 } 486 487 /** 488 * Returns an integer hash code for the receiver. Objects which are equal 489 * return the same value for this method. 490 * 491 * @return the receiver's hash. 492 * @see #equals 493 */ 494 @Override 495 public synchronized int hashCode() { 496 int hashCode = getID().hashCode() + rawOffset; 497 if (useDaylight) { 498 hashCode += startYear + startMonth + startDay + startDayOfWeek 499 + startTime + startMode + endMonth + endDay + endDayOfWeek 500 + endTime + endMode + dstSavings; 501 } 502 return hashCode; 503 } 504 505 @Override 506 public boolean hasSameRules(TimeZone zone) { 507 if (!(zone instanceof SimpleTimeZone)) { 508 return false; 509 } 510 SimpleTimeZone tz = (SimpleTimeZone) zone; 511 if (useDaylight != tz.useDaylight) { 512 return false; 513 } 514 if (!useDaylight) { 515 return rawOffset == tz.rawOffset; 516 } 517 return rawOffset == tz.rawOffset && dstSavings == tz.dstSavings 518 && startYear == tz.startYear && startMonth == tz.startMonth 519 && startDay == tz.startDay && startMode == tz.startMode 520 && startDayOfWeek == tz.startDayOfWeek 521 && startTime == tz.startTime && endMonth == tz.endMonth 522 && endDay == tz.endDay && endDayOfWeek == tz.endDayOfWeek 523 && endTime == tz.endTime && endMode == tz.endMode; 524 } 525 526 @Override public boolean inDaylightTime(Date time) { 527 return useDaylightTime() && getOffset(time.getTime()) != getRawOffset(); 528 } 529 530 private boolean isLeapYear(int year) { 531 if (year > 1582) { 532 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 533 } 534 return year % 4 == 0; 535 } 536 537 private int mod7(int num1) { 538 int rem = num1 % 7; 539 return (num1 < 0 && rem < 0) ? 7 + rem : rem; 540 } 541 542 /** 543 * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}. 544 * 545 * @param milliseconds 546 * the daylight savings offset in milliseconds. 547 */ 548 public void setDSTSavings(int milliseconds) { 549 if (milliseconds > 0) { 550 dstSavings = milliseconds; 551 } else { 552 throw new IllegalArgumentException(); 553 } 554 } 555 556 private void checkRange(int month, int dayOfWeek, int time) { 557 if (month < Calendar.JANUARY || month > Calendar.DECEMBER) { 558 throw new IllegalArgumentException("Invalid month: " + month); 559 } 560 if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { 561 throw new IllegalArgumentException("Invalid day of week: " + dayOfWeek); 562 } 563 if (time < 0 || time >= 24 * 3600000) { 564 throw new IllegalArgumentException("Invalid time: " + time); 565 } 566 } 567 568 private void checkDay(int month, int day) { 569 if (day <= 0 || day > GregorianCalendar.DaysInMonth[month]) { 570 throw new IllegalArgumentException("Invalid day of month: " + day); 571 } 572 } 573 574 private void setEndMode() { 575 if (endDayOfWeek == 0) { 576 endMode = DOM_MODE; 577 } else if (endDayOfWeek < 0) { 578 endDayOfWeek = -endDayOfWeek; 579 if (endDay < 0) { 580 endDay = -endDay; 581 endMode = DOW_LE_DOM_MODE; 582 } else { 583 endMode = DOW_GE_DOM_MODE; 584 } 585 } else { 586 endMode = DOW_IN_MONTH_MODE; 587 } 588 useDaylight = startDay != 0 && endDay != 0; 589 if (endDay != 0) { 590 checkRange(endMonth, endMode == DOM_MODE ? 1 : endDayOfWeek, 591 endTime); 592 if (endMode != DOW_IN_MONTH_MODE) { 593 checkDay(endMonth, endDay); 594 } else { 595 if (endDay < -5 || endDay > 5) { 596 throw new IllegalArgumentException("Day of week in month: " + endDay); 597 } 598 } 599 } 600 if (endMode != DOM_MODE) { 601 endDayOfWeek--; 602 } 603 } 604 605 /** 606 * Sets the rule which specifies the end of daylight savings time. 607 * 608 * @param month 609 * the {@code Calendar} month in which daylight savings time ends. 610 * @param dayOfMonth 611 * the {@code Calendar} day of the month on which daylight savings time 612 * ends. 613 * @param time 614 * the time of day in milliseconds standard time on which 615 * daylight savings time ends. 616 */ 617 public void setEndRule(int month, int dayOfMonth, int time) { 618 endMonth = month; 619 endDay = dayOfMonth; 620 endDayOfWeek = 0; // Initialize this value for hasSameRules() 621 endTime = time; 622 setEndMode(); 623 } 624 625 /** 626 * Sets the rule which specifies the end of daylight savings time. 627 * 628 * @param month 629 * the {@code Calendar} month in which daylight savings time ends. 630 * @param day 631 * the occurrence of the day of the week on which daylight 632 * savings time ends. 633 * @param dayOfWeek 634 * the {@code Calendar} day of the week on which daylight savings time 635 * ends. 636 * @param time 637 * the time of day in milliseconds standard time on which 638 * daylight savings time ends. 639 */ 640 public void setEndRule(int month, int day, int dayOfWeek, int time) { 641 endMonth = month; 642 endDay = day; 643 endDayOfWeek = dayOfWeek; 644 endTime = time; 645 setEndMode(); 646 } 647 648 /** 649 * Sets the rule which specifies the end of daylight savings time. 650 * 651 * @param month 652 * the {@code Calendar} month in which daylight savings time ends. 653 * @param day 654 * the {@code Calendar} day of the month. 655 * @param dayOfWeek 656 * the {@code Calendar} day of the week on which daylight savings time 657 * ends. 658 * @param time 659 * the time of day in milliseconds on which daylight savings time 660 * ends. 661 * @param after 662 * selects the day after or before the day of month. 663 */ 664 public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) { 665 endMonth = month; 666 endDay = after ? day : -day; 667 endDayOfWeek = -dayOfWeek; 668 endTime = time; 669 setEndMode(); 670 } 671 672 /** 673 * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}. 674 * 675 * @param offset 676 * the offset from GMT of standard time in milliseconds. 677 */ 678 @Override 679 public void setRawOffset(int offset) { 680 rawOffset = offset; 681 } 682 683 private void setStartMode() { 684 if (startDayOfWeek == 0) { 685 startMode = DOM_MODE; 686 } else if (startDayOfWeek < 0) { 687 startDayOfWeek = -startDayOfWeek; 688 if (startDay < 0) { 689 startDay = -startDay; 690 startMode = DOW_LE_DOM_MODE; 691 } else { 692 startMode = DOW_GE_DOM_MODE; 693 } 694 } else { 695 startMode = DOW_IN_MONTH_MODE; 696 } 697 useDaylight = startDay != 0 && endDay != 0; 698 if (startDay != 0) { 699 checkRange(startMonth, startMode == DOM_MODE ? 1 : startDayOfWeek, 700 startTime); 701 if (startMode != DOW_IN_MONTH_MODE) { 702 checkDay(startMonth, startDay); 703 } else { 704 if (startDay < -5 || startDay > 5) { 705 throw new IllegalArgumentException("Day of week in month: " + startDay); 706 } 707 } 708 } 709 if (startMode != DOM_MODE) { 710 startDayOfWeek--; 711 } 712 } 713 714 /** 715 * Sets the rule which specifies the start of daylight savings time. 716 * 717 * @param month 718 * the {@code Calendar} month in which daylight savings time starts. 719 * @param dayOfMonth 720 * the {@code Calendar} day of the month on which daylight savings time 721 * starts. 722 * @param time 723 * the time of day in milliseconds on which daylight savings time 724 * starts. 725 */ 726 public void setStartRule(int month, int dayOfMonth, int time) { 727 startMonth = month; 728 startDay = dayOfMonth; 729 startDayOfWeek = 0; // Initialize this value for hasSameRules() 730 startTime = time; 731 setStartMode(); 732 } 733 734 /** 735 * Sets the rule which specifies the start of daylight savings time. 736 * 737 * @param month 738 * the {@code Calendar} month in which daylight savings time starts. 739 * @param day 740 * the occurrence of the day of the week on which daylight 741 * savings time starts. 742 * @param dayOfWeek 743 * the {@code Calendar} day of the week on which daylight savings time 744 * starts. 745 * @param time 746 * the time of day in milliseconds on which daylight savings time 747 * starts. 748 */ 749 public void setStartRule(int month, int day, int dayOfWeek, int time) { 750 startMonth = month; 751 startDay = day; 752 startDayOfWeek = dayOfWeek; 753 startTime = time; 754 setStartMode(); 755 } 756 757 /** 758 * Sets the rule which specifies the start of daylight savings time. 759 * 760 * @param month 761 * the {@code Calendar} month in which daylight savings time starts. 762 * @param day 763 * the {@code Calendar} day of the month. 764 * @param dayOfWeek 765 * the {@code Calendar} day of the week on which daylight savings time 766 * starts. 767 * @param time 768 * the time of day in milliseconds on which daylight savings time 769 * starts. 770 * @param after 771 * selects the day after or before the day of month. 772 */ 773 public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) { 774 startMonth = month; 775 startDay = after ? day : -day; 776 startDayOfWeek = -dayOfWeek; 777 startTime = time; 778 setStartMode(); 779 } 780 781 /** 782 * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}. 783 * Years before this start year will always be in standard time. 784 * 785 * @param year 786 * the starting year. 787 */ 788 public void setStartYear(int year) { 789 startYear = year; 790 useDaylight = true; 791 } 792 793 /** 794 * Returns the string representation of this {@code SimpleTimeZone}. 795 * 796 * @return the string representation of this {@code SimpleTimeZone}. 797 */ 798 @Override 799 public String toString() { 800 return getClass().getName() 801 + "[id=" 802 + getID() 803 + ",offset=" 804 + rawOffset 805 + ",dstSavings=" 806 + dstSavings 807 + ",useDaylight=" 808 + useDaylight 809 + ",startYear=" 810 + startYear 811 + ",startMode=" 812 + startMode 813 + ",startMonth=" 814 + startMonth 815 + ",startDay=" 816 + startDay 817 + ",startDayOfWeek=" 818 + (useDaylight && (startMode != DOM_MODE) ? startDayOfWeek + 1 819 : 0) + ",startTime=" + startTime + ",endMode=" 820 + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay 821 + ",endDayOfWeek=" 822 + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0) 823 + ",endTime=" + endTime + "]"; 824 } 825 826 @Override 827 public boolean useDaylightTime() { 828 return useDaylight; 829 } 830 831 private static final ObjectStreamField[] serialPersistentFields = { 832 new ObjectStreamField("dstSavings", int.class), 833 new ObjectStreamField("endDay", int.class), 834 new ObjectStreamField("endDayOfWeek", int.class), 835 new ObjectStreamField("endMode", int.class), 836 new ObjectStreamField("endMonth", int.class), 837 new ObjectStreamField("endTime", int.class), 838 new ObjectStreamField("monthLength", byte[].class), 839 new ObjectStreamField("rawOffset", int.class), 840 new ObjectStreamField("serialVersionOnStream", int.class), 841 new ObjectStreamField("startDay", int.class), 842 new ObjectStreamField("startDayOfWeek", int.class), 843 new ObjectStreamField("startMode", int.class), 844 new ObjectStreamField("startMonth", int.class), 845 new ObjectStreamField("startTime", int.class), 846 new ObjectStreamField("startYear", int.class), 847 new ObjectStreamField("useDaylight", boolean.class), 848 }; 849 850 private void writeObject(ObjectOutputStream stream) throws IOException { 851 int sEndDay = endDay, sEndDayOfWeek = endDayOfWeek + 1, sStartDay = startDay, sStartDayOfWeek = startDayOfWeek + 1; 852 if (useDaylight 853 && (startMode != DOW_IN_MONTH_MODE || endMode != DOW_IN_MONTH_MODE)) { 854 Calendar cal = new GregorianCalendar(this); 855 if (endMode != DOW_IN_MONTH_MODE) { 856 cal.set(Calendar.MONTH, endMonth); 857 cal.set(Calendar.DATE, endDay); 858 sEndDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 859 if (endMode == DOM_MODE) { 860 sEndDayOfWeek = cal.getFirstDayOfWeek(); 861 } 862 } 863 if (startMode != DOW_IN_MONTH_MODE) { 864 cal.set(Calendar.MONTH, startMonth); 865 cal.set(Calendar.DATE, startDay); 866 sStartDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 867 if (startMode == DOM_MODE) { 868 sStartDayOfWeek = cal.getFirstDayOfWeek(); 869 } 870 } 871 } 872 ObjectOutputStream.PutField fields = stream.putFields(); 873 fields.put("dstSavings", dstSavings); 874 fields.put("endDay", sEndDay); 875 fields.put("endDayOfWeek", sEndDayOfWeek); 876 fields.put("endMode", endMode); 877 fields.put("endMonth", endMonth); 878 fields.put("endTime", endTime); 879 fields.put("monthLength", GregorianCalendar.DaysInMonth); 880 fields.put("rawOffset", rawOffset); 881 fields.put("serialVersionOnStream", 1); 882 fields.put("startDay", sStartDay); 883 fields.put("startDayOfWeek", sStartDayOfWeek); 884 fields.put("startMode", startMode); 885 fields.put("startMonth", startMonth); 886 fields.put("startTime", startTime); 887 fields.put("startYear", startYear); 888 fields.put("useDaylight", useDaylight); 889 stream.writeFields(); 890 stream.writeInt(4); 891 byte[] values = new byte[4]; 892 values[0] = (byte) startDay; 893 values[1] = (byte) (startMode == DOM_MODE ? 0 : startDayOfWeek + 1); 894 values[2] = (byte) endDay; 895 values[3] = (byte) (endMode == DOM_MODE ? 0 : endDayOfWeek + 1); 896 stream.write(values); 897 } 898 899 private void readObject(ObjectInputStream stream) throws IOException, 900 ClassNotFoundException { 901 ObjectInputStream.GetField fields = stream.readFields(); 902 rawOffset = fields.get("rawOffset", 0); 903 useDaylight = fields.get("useDaylight", false); 904 if (useDaylight) { 905 endMonth = fields.get("endMonth", 0); 906 endTime = fields.get("endTime", 0); 907 startMonth = fields.get("startMonth", 0); 908 startTime = fields.get("startTime", 0); 909 startYear = fields.get("startYear", 0); 910 } 911 if (fields.get("serialVersionOnStream", 0) == 0) { 912 if (useDaylight) { 913 startMode = endMode = DOW_IN_MONTH_MODE; 914 endDay = fields.get("endDay", 0); 915 endDayOfWeek = fields.get("endDayOfWeek", 0) - 1; 916 startDay = fields.get("startDay", 0); 917 startDayOfWeek = fields.get("startDayOfWeek", 0) - 1; 918 } 919 } else { 920 dstSavings = fields.get("dstSavings", 0); 921 if (useDaylight) { 922 endMode = fields.get("endMode", 0); 923 startMode = fields.get("startMode", 0); 924 int length = stream.readInt(); 925 byte[] values = new byte[length]; 926 stream.readFully(values); 927 if (length >= 4) { 928 startDay = values[0]; 929 startDayOfWeek = values[1]; 930 if (startMode != DOM_MODE) { 931 startDayOfWeek--; 932 } 933 endDay = values[2]; 934 endDayOfWeek = values[3]; 935 if (endMode != DOM_MODE) { 936 endDayOfWeek--; 937 } 938 } 939 } 940 } 941 } 942 943} 944