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 18 package java.util; 19 20 import java.io.IOException; 21 import java.io.ObjectInputStream; 22 import java.io.ObjectOutputStream; 23 import 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 */ 39 public 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 */ SimpleTimeZone(int offset, final String name)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 */ SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime)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 */ SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int daylightSavings)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 // TODO: do we need to set useDaylight is dstSavings != 0? 221 222 setStartRule(startMonth, startDay, startDayOfWeek, startTime); 223 setEndRule(endMonth, endDay, endDayOfWeek, endTime); 224 } 225 226 /** 227 * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT, 228 * time zone ID, times to start and end the daylight savings time including a 229 * mode specifier, the daylight savings time difference in milliseconds. 230 * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or 231 * {@link #UTC_TIME}. 232 * 233 * @param offset 234 * the given base time zone offset to GMT. 235 * @param name 236 * the time zone ID which is obtained from 237 * {@code TimeZone.getAvailableIDs}. 238 * @param startMonth 239 * the daylight savings starting month. The month indexing is 0-based. eg, 0 240 * for January. 241 * @param startDay 242 * the daylight savings starting day-of-week-in-month. Please see 243 * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 244 * @param startDayOfWeek 245 * the daylight savings starting day-of-week. Please see the 246 * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 247 * @param startTime 248 * the time of day in milliseconds on which daylight savings 249 * time starts, based on the {@code startTimeMode}. 250 * @param startTimeMode 251 * the mode (UTC, standard, or wall time) of the start time 252 * value. 253 * @param endDay 254 * the day of the week on which daylight savings time ends. 255 * @param endMonth 256 * the daylight savings ending month. The month indexing is 0-based. eg, 0 for 257 * January. 258 * @param endDayOfWeek 259 * the daylight savings ending day-of-week. Please see the description of 260 * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. 261 * @param endTime 262 * the time of day in milliseconds on which daylight savings 263 * time ends, based on the {@code endTimeMode}. 264 * @param endTimeMode 265 * the mode (UTC, standard, or wall time) of the end time value. 266 * @param daylightSavings 267 * the daylight savings time difference in milliseconds. 268 * @throws IllegalArgumentException 269 * if the month, day, dayOfWeek, or time parameters are out of 270 * range for the start or end rule. 271 */ SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, int endMonth, int endDay, int endDayOfWeek, int endTime, int endTimeMode, int daylightSavings)272 public SimpleTimeZone(int offset, String name, int startMonth, 273 int startDay, int startDayOfWeek, int startTime, int startTimeMode, 274 int endMonth, int endDay, int endDayOfWeek, int endTime, 275 int endTimeMode, int daylightSavings) { 276 277 this(offset, name, startMonth, startDay, startDayOfWeek, startTime, 278 endMonth, endDay, endDayOfWeek, endTime, daylightSavings); 279 startMode = startTimeMode; 280 endMode = endTimeMode; 281 } 282 283 /** 284 * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight 285 * savings time rules as this SimpleTimeZone. 286 * 287 * @return a shallow copy of this {@code SimpleTimeZone}. 288 * @see java.lang.Cloneable 289 */ 290 @Override clone()291 public Object clone() { 292 SimpleTimeZone zone = (SimpleTimeZone) super.clone(); 293 return zone; 294 } 295 296 /** 297 * Compares the specified object to this {@code SimpleTimeZone} and returns whether they 298 * are equal. The object must be an instance of {@code SimpleTimeZone} and have the 299 * same internal data. 300 * 301 * @param object 302 * the object to compare with this object. 303 * @return {@code true} if the specified object is equal to this 304 * {@code SimpleTimeZone}, {@code false} otherwise. 305 * @see #hashCode 306 */ 307 @Override equals(Object object)308 public boolean equals(Object object) { 309 if (!(object instanceof SimpleTimeZone)) { 310 return false; 311 } 312 SimpleTimeZone tz = (SimpleTimeZone) object; 313 return getID().equals(tz.getID()) 314 && rawOffset == tz.rawOffset 315 && useDaylight == tz.useDaylight 316 && (!useDaylight || (startYear == tz.startYear 317 && startMonth == tz.startMonth 318 && startDay == tz.startDay && startMode == tz.startMode 319 && startDayOfWeek == tz.startDayOfWeek 320 && startTime == tz.startTime && endMonth == tz.endMonth 321 && endDay == tz.endDay 322 && endDayOfWeek == tz.endDayOfWeek 323 && endTime == tz.endTime && endMode == tz.endMode && dstSavings == tz.dstSavings)); 324 } 325 326 @Override getDSTSavings()327 public int getDSTSavings() { 328 if (!useDaylight) { 329 return 0; 330 } 331 return dstSavings; 332 } 333 334 @Override getOffset(int era, int year, int month, int day, int dayOfWeek, int time)335 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int time) { 336 if (era != GregorianCalendar.BC && era != GregorianCalendar.AD) { 337 throw new IllegalArgumentException("Invalid era: " + era); 338 } 339 checkRange(month, dayOfWeek, time); 340 if (month != Calendar.FEBRUARY || day != 29 || !isLeapYear(year)) { 341 checkDay(month, day); 342 } 343 344 if (!useDaylightTime() || era != GregorianCalendar.AD || year < startYear) { 345 return rawOffset; 346 } 347 if (endMonth < startMonth) { 348 if (month > endMonth && month < startMonth) { 349 return rawOffset; 350 } 351 } else { 352 if (month < startMonth || month > endMonth) { 353 return rawOffset; 354 } 355 } 356 357 int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day); 358 if (month == startMonth) { 359 switch (startMode) { 360 case DOM_MODE: 361 ruleDay = startDay; 362 break; 363 case DOW_IN_MONTH_MODE: 364 if (startDay >= 0) { 365 ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1 366 + (startDay - 1) * 7; 367 } else { 368 daysInMonth = GregorianCalendar.DaysInMonth[startMonth]; 369 if (startMonth == Calendar.FEBRUARY && isLeapYear( 370 year)) { 371 daysInMonth += 1; 372 } 373 ruleDay = daysInMonth 374 + 1 375 + mod7(startDayOfWeek 376 - (firstDayOfMonth + daysInMonth)) 377 + startDay * 7; 378 } 379 break; 380 case DOW_GE_DOM_MODE: 381 ruleDay = startDay 382 + mod7(startDayOfWeek 383 - (firstDayOfMonth + startDay - 1)); 384 break; 385 case DOW_LE_DOM_MODE: 386 ruleDay = startDay 387 + mod7(startDayOfWeek 388 - (firstDayOfMonth + startDay - 1)); 389 if (ruleDay != startDay) { 390 ruleDay -= 7; 391 } 392 break; 393 } 394 if (ruleDay > day || ruleDay == day && time < startTime) { 395 return rawOffset; 396 } 397 } 398 399 int ruleTime = endTime - dstSavings; 400 int nextMonth = (month + 1) % 12; 401 if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) { 402 switch (endMode) { 403 case DOM_MODE: 404 ruleDay = endDay; 405 break; 406 case DOW_IN_MONTH_MODE: 407 if (endDay >= 0) { 408 ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1 409 + (endDay - 1) * 7; 410 } else { 411 daysInMonth = GregorianCalendar.DaysInMonth[endMonth]; 412 if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) { 413 daysInMonth++; 414 } 415 ruleDay = daysInMonth 416 + 1 417 + mod7(endDayOfWeek 418 - (firstDayOfMonth + daysInMonth)) + endDay 419 * 7; 420 } 421 break; 422 case DOW_GE_DOM_MODE: 423 ruleDay = endDay 424 + mod7( 425 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 426 break; 427 case DOW_LE_DOM_MODE: 428 ruleDay = endDay 429 + mod7( 430 endDayOfWeek - (firstDayOfMonth + endDay - 1)); 431 if (ruleDay != endDay) { 432 ruleDay -= 7; 433 } 434 break; 435 } 436 437 int ruleMonth = endMonth; 438 if (ruleTime < 0) { 439 int changeDays = 1 - (ruleTime / 86400000); 440 ruleTime = (ruleTime % 86400000) + 86400000; 441 ruleDay -= changeDays; 442 if (ruleDay <= 0) { 443 if (--ruleMonth < Calendar.JANUARY) { 444 ruleMonth = Calendar.DECEMBER; 445 } 446 ruleDay += GregorianCalendar.DaysInMonth[ruleMonth]; 447 if (ruleMonth == Calendar.FEBRUARY && isLeapYear(year)) { 448 ruleDay++; 449 } 450 } 451 } 452 453 if (month == ruleMonth) { 454 if (ruleDay < day || ruleDay == day && time >= ruleTime) { 455 return rawOffset; 456 } 457 } else if (nextMonth != ruleMonth) { 458 return rawOffset; 459 } 460 } 461 return rawOffset + dstSavings; 462 } 463 464 @Override getOffset(long time)465 public int getOffset(long time) { 466 // Simplified variant of the ICU4J code. 467 if (!useDaylightTime()) { 468 return rawOffset; 469 } 470 int[] fields = Grego.timeToFields(time + rawOffset, null); 471 return getOffset(GregorianCalendar.AD, fields[0], fields[1], fields[2], 472 fields[3], fields[5]); 473 } 474 475 @Override getRawOffset()476 public int getRawOffset() { 477 return rawOffset; 478 } 479 480 /** 481 * Returns an integer hash code for the receiver. Objects which are equal 482 * return the same value for this method. 483 * 484 * @return the receiver's hash. 485 * @see #equals 486 */ 487 @Override hashCode()488 public synchronized int hashCode() { 489 int hashCode = getID().hashCode() + rawOffset; 490 if (useDaylight) { 491 hashCode += startYear + startMonth + startDay + startDayOfWeek 492 + startTime + startMode + endMonth + endDay + endDayOfWeek 493 + endTime + endMode + dstSavings; 494 } 495 return hashCode; 496 } 497 498 @Override hasSameRules(TimeZone zone)499 public boolean hasSameRules(TimeZone zone) { 500 if (!(zone instanceof SimpleTimeZone)) { 501 return false; 502 } 503 SimpleTimeZone tz = (SimpleTimeZone) zone; 504 if (useDaylight != tz.useDaylight) { 505 return false; 506 } 507 if (!useDaylight) { 508 return rawOffset == tz.rawOffset; 509 } 510 return rawOffset == tz.rawOffset && dstSavings == tz.dstSavings 511 && startYear == tz.startYear && startMonth == tz.startMonth 512 && startDay == tz.startDay && startMode == tz.startMode 513 && startDayOfWeek == tz.startDayOfWeek 514 && startTime == tz.startTime && endMonth == tz.endMonth 515 && endDay == tz.endDay && endDayOfWeek == tz.endDayOfWeek 516 && endTime == tz.endTime && endMode == tz.endMode; 517 } 518 inDaylightTime(Date time)519 @Override public boolean inDaylightTime(Date time) { 520 return useDaylightTime() && getOffset(time.getTime()) != getRawOffset(); 521 } 522 isLeapYear(int year)523 private boolean isLeapYear(int year) { 524 if (year > 1582) { 525 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 526 } 527 return year % 4 == 0; 528 } 529 mod7(int num1)530 private int mod7(int num1) { 531 int rem = num1 % 7; 532 return (num1 < 0 && rem < 0) ? 7 + rem : rem; 533 } 534 535 /** 536 * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}. 537 * 538 * @param milliseconds 539 * the daylight savings offset in milliseconds. 540 */ setDSTSavings(int milliseconds)541 public void setDSTSavings(int milliseconds) { 542 if (milliseconds > 0) { 543 dstSavings = milliseconds; 544 } else { 545 throw new IllegalArgumentException(); 546 } 547 } 548 checkRange(int month, int dayOfWeek, int time)549 private void checkRange(int month, int dayOfWeek, int time) { 550 if (month < Calendar.JANUARY || month > Calendar.DECEMBER) { 551 throw new IllegalArgumentException("Invalid month: " + month); 552 } 553 if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { 554 throw new IllegalArgumentException("Invalid day of week: " + dayOfWeek); 555 } 556 if (time < 0 || time >= 24 * 3600000) { 557 throw new IllegalArgumentException("Invalid time: " + time); 558 } 559 } 560 checkDay(int month, int day)561 private void checkDay(int month, int day) { 562 if (day <= 0 || day > GregorianCalendar.DaysInMonth[month]) { 563 throw new IllegalArgumentException("Invalid day of month: " + day); 564 } 565 } 566 setEndMode()567 private void setEndMode() { 568 if (endDayOfWeek == 0) { 569 endMode = DOM_MODE; 570 } else if (endDayOfWeek < 0) { 571 endDayOfWeek = -endDayOfWeek; 572 if (endDay < 0) { 573 endDay = -endDay; 574 endMode = DOW_LE_DOM_MODE; 575 } else { 576 endMode = DOW_GE_DOM_MODE; 577 } 578 } else { 579 endMode = DOW_IN_MONTH_MODE; 580 } 581 useDaylight = startDay != 0 && endDay != 0; 582 if (endDay != 0) { 583 checkRange(endMonth, endMode == DOM_MODE ? 1 : endDayOfWeek, 584 endTime); 585 if (endMode != DOW_IN_MONTH_MODE) { 586 checkDay(endMonth, endDay); 587 } else { 588 if (endDay < -5 || endDay > 5) { 589 throw new IllegalArgumentException("Day of week in month: " + endDay); 590 } 591 } 592 } 593 if (endMode != DOM_MODE) { 594 endDayOfWeek--; 595 } 596 } 597 598 /** 599 * Sets the rule which specifies the end of daylight savings time. 600 * 601 * @param month 602 * the {@code Calendar} month in which daylight savings time ends. 603 * @param dayOfMonth 604 * the {@code Calendar} day of the month on which daylight savings time 605 * ends. 606 * @param time 607 * the time of day in milliseconds standard time on which 608 * daylight savings time ends. 609 */ setEndRule(int month, int dayOfMonth, int time)610 public void setEndRule(int month, int dayOfMonth, int time) { 611 endMonth = month; 612 endDay = dayOfMonth; 613 endDayOfWeek = 0; // Initialize this value for hasSameRules() 614 endTime = time; 615 setEndMode(); 616 } 617 618 /** 619 * Sets the rule which specifies the end of daylight savings time. 620 * 621 * @param month 622 * the {@code Calendar} month in which daylight savings time ends. 623 * @param day 624 * the occurrence of the day of the week on which daylight 625 * savings time ends. 626 * @param dayOfWeek 627 * the {@code Calendar} day of the week on which daylight savings time 628 * ends. 629 * @param time 630 * the time of day in milliseconds standard time on which 631 * daylight savings time ends. 632 */ setEndRule(int month, int day, int dayOfWeek, int time)633 public void setEndRule(int month, int day, int dayOfWeek, int time) { 634 endMonth = month; 635 endDay = day; 636 endDayOfWeek = dayOfWeek; 637 endTime = time; 638 setEndMode(); 639 } 640 641 /** 642 * Sets the rule which specifies the end of daylight savings time. 643 * 644 * @param month 645 * the {@code Calendar} month in which daylight savings time ends. 646 * @param day 647 * the {@code Calendar} day of the month. 648 * @param dayOfWeek 649 * the {@code Calendar} day of the week on which daylight savings time 650 * ends. 651 * @param time 652 * the time of day in milliseconds on which daylight savings time 653 * ends. 654 * @param after 655 * selects the day after or before the day of month. 656 */ setEndRule(int month, int day, int dayOfWeek, int time, boolean after)657 public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) { 658 endMonth = month; 659 endDay = after ? day : -day; 660 endDayOfWeek = -dayOfWeek; 661 endTime = time; 662 setEndMode(); 663 } 664 665 /** 666 * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}. 667 * 668 * @param offset 669 * the offset from GMT of standard time in milliseconds. 670 */ 671 @Override setRawOffset(int offset)672 public void setRawOffset(int offset) { 673 rawOffset = offset; 674 } 675 setStartMode()676 private void setStartMode() { 677 if (startDayOfWeek == 0) { 678 startMode = DOM_MODE; 679 } else if (startDayOfWeek < 0) { 680 startDayOfWeek = -startDayOfWeek; 681 if (startDay < 0) { 682 startDay = -startDay; 683 startMode = DOW_LE_DOM_MODE; 684 } else { 685 startMode = DOW_GE_DOM_MODE; 686 } 687 } else { 688 startMode = DOW_IN_MONTH_MODE; 689 } 690 useDaylight = startDay != 0 && endDay != 0; 691 if (startDay != 0) { 692 checkRange(startMonth, startMode == DOM_MODE ? 1 : startDayOfWeek, 693 startTime); 694 if (startMode != DOW_IN_MONTH_MODE) { 695 checkDay(startMonth, startDay); 696 } else { 697 if (startDay < -5 || startDay > 5) { 698 throw new IllegalArgumentException("Day of week in month: " + startDay); 699 } 700 } 701 } 702 if (startMode != DOM_MODE) { 703 startDayOfWeek--; 704 } 705 } 706 707 /** 708 * Sets the rule which specifies the start of daylight savings time. 709 * 710 * @param month 711 * the {@code Calendar} month in which daylight savings time starts. 712 * @param dayOfMonth 713 * the {@code Calendar} day of the month on which daylight savings time 714 * starts. 715 * @param time 716 * the time of day in milliseconds on which daylight savings time 717 * starts. 718 */ setStartRule(int month, int dayOfMonth, int time)719 public void setStartRule(int month, int dayOfMonth, int time) { 720 startMonth = month; 721 startDay = dayOfMonth; 722 startDayOfWeek = 0; // Initialize this value for hasSameRules() 723 startTime = time; 724 setStartMode(); 725 } 726 727 /** 728 * Sets the rule which specifies the start of daylight savings time. 729 * 730 * @param month 731 * the {@code Calendar} month in which daylight savings time starts. 732 * @param day 733 * the occurrence of the day of the week on which daylight 734 * savings time starts. 735 * @param dayOfWeek 736 * the {@code Calendar} day of the week on which daylight savings time 737 * starts. 738 * @param time 739 * the time of day in milliseconds on which daylight savings time 740 * starts. 741 */ setStartRule(int month, int day, int dayOfWeek, int time)742 public void setStartRule(int month, int day, int dayOfWeek, int time) { 743 startMonth = month; 744 startDay = day; 745 startDayOfWeek = dayOfWeek; 746 startTime = time; 747 setStartMode(); 748 } 749 750 /** 751 * Sets the rule which specifies the start of daylight savings time. 752 * 753 * @param month 754 * the {@code Calendar} month in which daylight savings time starts. 755 * @param day 756 * the {@code Calendar} day of the month. 757 * @param dayOfWeek 758 * the {@code Calendar} day of the week on which daylight savings time 759 * starts. 760 * @param time 761 * the time of day in milliseconds on which daylight savings time 762 * starts. 763 * @param after 764 * selects the day after or before the day of month. 765 */ setStartRule(int month, int day, int dayOfWeek, int time, boolean after)766 public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) { 767 startMonth = month; 768 startDay = after ? day : -day; 769 startDayOfWeek = -dayOfWeek; 770 startTime = time; 771 setStartMode(); 772 } 773 774 /** 775 * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}. 776 * Years before this start year will always be in standard time. 777 * 778 * @param year 779 * the starting year. 780 */ setStartYear(int year)781 public void setStartYear(int year) { 782 startYear = year; 783 useDaylight = true; 784 } 785 786 /** 787 * Returns the string representation of this {@code SimpleTimeZone}. 788 * 789 * @return the string representation of this {@code SimpleTimeZone}. 790 */ 791 @Override toString()792 public String toString() { 793 return getClass().getName() 794 + "[id=" 795 + getID() 796 + ",offset=" 797 + rawOffset 798 + ",dstSavings=" 799 + dstSavings 800 + ",useDaylight=" 801 + useDaylight 802 + ",startYear=" 803 + startYear 804 + ",startMode=" 805 + startMode 806 + ",startMonth=" 807 + startMonth 808 + ",startDay=" 809 + startDay 810 + ",startDayOfWeek=" 811 + (useDaylight && (startMode != DOM_MODE) ? startDayOfWeek + 1 812 : 0) + ",startTime=" + startTime + ",endMode=" 813 + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay 814 + ",endDayOfWeek=" 815 + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0) 816 + ",endTime=" + endTime + "]"; 817 } 818 819 @Override useDaylightTime()820 public boolean useDaylightTime() { 821 return useDaylight; 822 } 823 824 private static final ObjectStreamField[] serialPersistentFields = { 825 new ObjectStreamField("dstSavings", int.class), 826 new ObjectStreamField("endDay", int.class), 827 new ObjectStreamField("endDayOfWeek", int.class), 828 new ObjectStreamField("endMode", int.class), 829 new ObjectStreamField("endMonth", int.class), 830 new ObjectStreamField("endTime", int.class), 831 new ObjectStreamField("monthLength", byte[].class), 832 new ObjectStreamField("rawOffset", int.class), 833 new ObjectStreamField("serialVersionOnStream", int.class), 834 new ObjectStreamField("startDay", int.class), 835 new ObjectStreamField("startDayOfWeek", int.class), 836 new ObjectStreamField("startMode", int.class), 837 new ObjectStreamField("startMonth", int.class), 838 new ObjectStreamField("startTime", int.class), 839 new ObjectStreamField("startYear", int.class), 840 new ObjectStreamField("useDaylight", boolean.class), 841 }; 842 writeObject(ObjectOutputStream stream)843 private void writeObject(ObjectOutputStream stream) throws IOException { 844 int sEndDay = endDay, sEndDayOfWeek = endDayOfWeek + 1, sStartDay = startDay, sStartDayOfWeek = startDayOfWeek + 1; 845 if (useDaylight 846 && (startMode != DOW_IN_MONTH_MODE || endMode != DOW_IN_MONTH_MODE)) { 847 Calendar cal = new GregorianCalendar(this); 848 if (endMode != DOW_IN_MONTH_MODE) { 849 cal.set(Calendar.MONTH, endMonth); 850 cal.set(Calendar.DATE, endDay); 851 sEndDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 852 if (endMode == DOM_MODE) { 853 sEndDayOfWeek = cal.getFirstDayOfWeek(); 854 } 855 } 856 if (startMode != DOW_IN_MONTH_MODE) { 857 cal.set(Calendar.MONTH, startMonth); 858 cal.set(Calendar.DATE, startDay); 859 sStartDay = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH); 860 if (startMode == DOM_MODE) { 861 sStartDayOfWeek = cal.getFirstDayOfWeek(); 862 } 863 } 864 } 865 ObjectOutputStream.PutField fields = stream.putFields(); 866 fields.put("dstSavings", dstSavings); 867 fields.put("endDay", sEndDay); 868 fields.put("endDayOfWeek", sEndDayOfWeek); 869 fields.put("endMode", endMode); 870 fields.put("endMonth", endMonth); 871 fields.put("endTime", endTime); 872 fields.put("monthLength", GregorianCalendar.DaysInMonth); 873 fields.put("rawOffset", rawOffset); 874 fields.put("serialVersionOnStream", 1); 875 fields.put("startDay", sStartDay); 876 fields.put("startDayOfWeek", sStartDayOfWeek); 877 fields.put("startMode", startMode); 878 fields.put("startMonth", startMonth); 879 fields.put("startTime", startTime); 880 fields.put("startYear", startYear); 881 fields.put("useDaylight", useDaylight); 882 stream.writeFields(); 883 stream.writeInt(4); 884 byte[] values = new byte[4]; 885 values[0] = (byte) startDay; 886 values[1] = (byte) (startMode == DOM_MODE ? 0 : startDayOfWeek + 1); 887 values[2] = (byte) endDay; 888 values[3] = (byte) (endMode == DOM_MODE ? 0 : endDayOfWeek + 1); 889 stream.write(values); 890 } 891 readObject(ObjectInputStream stream)892 private void readObject(ObjectInputStream stream) throws IOException, 893 ClassNotFoundException { 894 ObjectInputStream.GetField fields = stream.readFields(); 895 rawOffset = fields.get("rawOffset", 0); 896 useDaylight = fields.get("useDaylight", false); 897 if (useDaylight) { 898 endMonth = fields.get("endMonth", 0); 899 endTime = fields.get("endTime", 0); 900 startMonth = fields.get("startMonth", 0); 901 startTime = fields.get("startTime", 0); 902 startYear = fields.get("startYear", 0); 903 } 904 if (fields.get("serialVersionOnStream", 0) == 0) { 905 if (useDaylight) { 906 startMode = endMode = DOW_IN_MONTH_MODE; 907 endDay = fields.get("endDay", 0); 908 endDayOfWeek = fields.get("endDayOfWeek", 0) - 1; 909 startDay = fields.get("startDay", 0); 910 startDayOfWeek = fields.get("startDayOfWeek", 0) - 1; 911 } 912 } else { 913 dstSavings = fields.get("dstSavings", 0); 914 if (useDaylight) { 915 endMode = fields.get("endMode", 0); 916 startMode = fields.get("startMode", 0); 917 int length = stream.readInt(); 918 byte[] values = new byte[length]; 919 stream.readFully(values); 920 if (length >= 4) { 921 startDay = values[0]; 922 startDayOfWeek = values[1]; 923 if (startMode != DOM_MODE) { 924 startDayOfWeek--; 925 } 926 endDay = values[2]; 927 endDayOfWeek = values[3]; 928 if (endMode != DOM_MODE) { 929 endDayOfWeek--; 930 } 931 } 932 } 933 } 934 } 935 936 } 937