1 /* java.util.SimpleTimeZone 2 Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 19 02111-1307 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package java.util; 40 41 /** 42 * This class represents a simple time zone offset and handles 43 * daylight savings. It can only handle one daylight savings rule, so 44 * it can't represent historical changes. 45 * 46 * This object is tightly bound to the Gregorian calendar. It assumes 47 * a regular seven days week, and the month lengths are that of the 48 * Gregorian Calendar. It can only handle daylight savings for years 49 * lying in the AD era. 50 * 51 * @see Calendar 52 * @see GregorianCalender 53 * @author Jochen Hoenicke 54 */ 55 public class SimpleTimeZone extends TimeZone 56 { 57 /** 58 * The raw time zone offset in milliseconds to GMT, ignoring 59 * daylight savings. 60 * @serial 61 */ 62 private int rawOffset; 63 64 /** 65 * True, if this timezone uses daylight savings, false otherwise. 66 * @serial 67 */ 68 private boolean useDaylight; 69 70 /** 71 * The daylight savings offset. This is a positive offset in 72 * milliseconds with respect to standard time. Typically this 73 * is one hour, but for some time zones this may be half an our. 74 * @serial 75 * @since JDK1.1.4 76 */ 77 private int dstSavings = 60 * 60 * 1000; 78 79 /** 80 * The first year, in which daylight savings rules applies. 81 * @serial 82 */ 83 private int startYear; 84 85 private static final int DOM_MODE = 1; 86 private static final int DOW_IN_MONTH_MODE = 2; 87 private static final int DOW_GE_DOM_MODE = 3; 88 private static final int DOW_LE_DOM_MODE = 4; 89 90 /** 91 * The mode of the start rule. This takes one of the following values: 92 * <dl> 93 * <dt>DOM_MODE (1)</dt> 94 * <dd> startDay contains the day in month of the start date, 95 * startDayOfWeek is unused. </dd> 96 * <dt>DOW_IN_MONTH_MODE (2)</dt> 97 * <dd> The startDay gives the day of week in month, and 98 * startDayOfWeek the day of week. For example startDay=2 and 99 * startDayOfWeek=Calender.SUNDAY specifies that the change is on 100 * the second sunday in that month. You must make sure, that this 101 * day always exists (ie. don't specify the 5th sunday). 102 * </dd> 103 * <dt>DOW_GE_DOM_MODE (3)</dt> 104 * <dd> The start is on the first startDayOfWeek on or after 105 * startDay. For example startDay=13 and 106 * startDayOfWeek=Calendar.FRIDAY specifies that the daylight 107 * savings start on the first FRIDAY on or after the 13th of that 108 * Month. Make sure that the change is always in the given month, or 109 * the result is undefined. 110 * </dd> 111 * <dt>DOW_LE_DOM_MONTH (4)</dt> 112 * <dd> The start is on the first startDayOfWeek on or before the 113 * startDay. Make sure that the change is always in the given 114 * month, or the result is undefined. 115 </dd> 116 * </dl> 117 * @serial */ 118 private int startMode; 119 120 /** 121 * The month in which daylight savings start. This is one of the 122 * constants Calendar.JANUARY, ..., Calendar.DECEMBER. 123 * @serial 124 */ 125 private int startMonth; 126 127 /** 128 * This variable can have different meanings. See startMode for details 129 * @see #startMode; 130 * @serial 131 */ 132 private int startDay; 133 134 /** 135 * This variable specifies the day of week the change takes place. If 136 * startMode == DOM_MODE, this is undefined. 137 * @serial 138 * @see #startMode; 139 */ 140 private int startDayOfWeek; 141 142 /** 143 * This variable specifies the time of change to daylight savings. 144 * This time is given in milliseconds after midnight local 145 * standard time. 146 * @serial 147 */ 148 private int startTime; 149 150 /** 151 * The month in which daylight savings ends. This is one of the 152 * constants Calendar.JANUARY, ..., Calendar.DECEMBER. 153 * @serial 154 */ 155 private int endMonth; 156 157 /** 158 * This variable gives the mode for the end of daylight savings rule. 159 * It can take the same values as startMode. 160 * @serial 161 * @see #startMode 162 */ 163 private int endMode; 164 165 /** 166 * This variable can have different meanings. See startMode for details 167 * @serial 168 * @see #startMode; 169 */ 170 private int endDay; 171 172 /** 173 * This variable specifies the day of week the change takes place. If 174 * endMode == DOM_MODE, this is undefined. 175 * @serial 176 * @see #startMode; 177 */ 178 private int endDayOfWeek; 179 180 /** 181 * This variable specifies the time of change back to standard time. 182 * This time is given in milliseconds after midnight local 183 * standard time. 184 * @serial 185 */ 186 private int endTime; 187 188 /** 189 * This variable points to a deprecated array from JDK 1.1. It is 190 * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. 191 * The array contains the lengths of the months in the year and is 192 * assigned from a private static final field to avoid allocating 193 * the array for every instance of the object. 194 * Note that static final fields are not serialized. 195 * @serial 196 */ 197 private byte[] monthLength = monthArr; 198 private static final byte[] monthArr = 199 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 200 201 /** 202 * The version of the serialized data on the stream. 203 * <dl> 204 * <dt>0 or not present on stream</dt> 205 * <dd> JDK 1.1.3 or earlier, only provides this fields: 206 * rawOffset, startDay, startDayOfWeek, startMonth, startTime, 207 * startYear, endDay, endDayOfWeek, endMonth, endTime 208 * </dd> 209 * <dd> JDK 1.1.4 or later. This includes three new fields, namely 210 * startMode, endMode and dstSavings. And there is a optional section 211 * as described in writeObject. 212 * </dd> 213 * 214 * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old 215 * version. 216 * 217 * When streaming out this class it is always written in the latest 218 * version. 219 * @serial 220 * @since JDK1.1.4 221 */ 222 private int serialVersionOnStream = 1; 223 224 private static final long serialVersionUID = -403250971215465050L; 225 226 /** 227 * Create a <code>SimpleTimeZone</code> with the given time offset 228 * from GMT and without daylight savings. 229 * @param rawOffset the time offset from GMT in milliseconds. 230 * @param id The identifier of this time zone. 231 */ SimpleTimeZone(int rawOffset, String id)232 public SimpleTimeZone(int rawOffset, String id) 233 { 234 this.rawOffset = rawOffset; 235 setID(id); 236 useDaylight = false; 237 startYear = 0; 238 } 239 240 /** 241 * Create a <code>SimpleTimeZone</code> with the given time offset 242 * from GMT and with daylight savings. The start/end parameters 243 * can have different meaning (replace WEEKDAY with a real day of 244 * week). Only the first two meanings were supported by earlier 245 * versions of jdk. 246 * 247 * <dl> 248 * <dt><code>day > 0, dayOfWeek = Calendar.WEEKDAY</code></dt> 249 * <dd>The start/end of daylight savings is on the <code>day</code>-th 250 * <code>WEEKDAY</code> in the given month. </dd> 251 * <dt><code>day < 0, dayOfWeek = Calendar.WEEKDAY</code></dt> 252 * <dd>The start/end of daylight savings is on the <code>-day</code>-th 253 * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd> 254 * <dt><code>day > 0, dayOfWeek = 0</code></dt> 255 * <dd>The start/end of daylight is on the <code>day</code>-th day of 256 * the month. </dd> 257 * <dt><code>day > 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> 258 * <dd>The start/end of daylight is on the first WEEKDAY on or after 259 * the <code>day</code>-th day of the month. You must make sure that 260 * this day lies in the same month. </dd> 261 * <dt><code>day < 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> 262 * <dd>The start/end of daylight is on the first WEEKDAY on or 263 * <i>before</i> the <code>-day</code>-th day of the month. You 264 * must make sure that this day lies in the same month. </dd> 265 * </dl> 266 * 267 * If you give a non existing month, a day that is zero, or too big, 268 * or a dayOfWeek that is too big, the result is undefined. 269 * 270 * The start rule must have a different month than the end rule. 271 * This restriction shouldn't hurt for all possible time zones. 272 * 273 * @param rawOffset The time offset from GMT in milliseconds. 274 * @param id The identifier of this time zone. 275 * @param startMonth The start month of daylight savings; use the 276 * constants in Calendar. 277 * @param startday A day in month or a day of week number, as 278 * described above. 279 * @param startDayOfWeek The start rule day of week; see above. 280 * @param startTime A time in millis in standard time. 281 * @param endMonth The end month of daylight savings; use the 282 * constants in Calendar. 283 * @param endday A day in month or a day of week number, as 284 * described above. 285 * @param endDayOfWeek The end rule day of week; see above. 286 * @param endTime A time in millis in standard time. */ SimpleTimeZone(int rawOffset, String id, int startMonth, int startDayOfWeekInMonth, int startDayOfWeek, int startTime, int endMonth, int endDayOfWeekInMonth, int endDayOfWeek, int endTime)287 public SimpleTimeZone(int rawOffset, String id, 288 int startMonth, int startDayOfWeekInMonth, 289 int startDayOfWeek, int startTime, 290 int endMonth, int endDayOfWeekInMonth, 291 int endDayOfWeek, int endTime) 292 { 293 this.rawOffset = rawOffset; 294 setID(id); 295 useDaylight = true; 296 297 setStartRule(startMonth, startDayOfWeekInMonth, 298 startDayOfWeek, startTime); 299 setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); 300 if (startMonth == endMonth) 301 throw new IllegalArgumentException 302 ("startMonth and endMonth must be different"); 303 this.startYear = 0; 304 } 305 306 /** 307 * This constructs a new SimpleTimeZone that supports a daylight savings 308 * rule. The parameter are the same as for the constructor above, except 309 * there is the additional dstSavaings parameter. 310 * 311 * @param dstSavings the amount of savings for daylight savings 312 * time in milliseconds. This must be positive. 313 */ SimpleTimeZone(int rawOffset, String id, int startMonth, int startDayOfWeekInMonth, int startDayOfWeek, int startTime, int endMonth, int endDayOfWeekInMonth, int endDayOfWeek, int endTime, int dstSavings)314 public SimpleTimeZone(int rawOffset, String id, 315 int startMonth, int startDayOfWeekInMonth, 316 int startDayOfWeek, int startTime, 317 int endMonth, int endDayOfWeekInMonth, 318 int endDayOfWeek, int endTime, int dstSavings) 319 { 320 this(rawOffset, id, 321 startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime, 322 endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); 323 324 this.dstSavings = dstSavings; 325 } 326 327 /** 328 * Sets the first year, where daylight savings applies. The daylight 329 * savings rule never apply for years in the BC era. Note that this 330 * is gregorian calendar specific. 331 * @param year the start year. 332 */ setStartYear(int year)333 public void setStartYear(int year) 334 { 335 startYear = year; 336 useDaylight = true; 337 } 338 339 /** 340 * Checks if the month, day, dayOfWeek arguments are in range and 341 * returns the mode of the rule. 342 * @param month the month parameter as in the constructor 343 * @param day the day parameter as in the constructor 344 * @param dayOfWeek the day of week parameter as in the constructor 345 * @return the mode of this rule see startMode. 346 * @exception IllegalArgumentException if parameters are out of range. 347 * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) 348 * @see #startMode 349 */ checkRule(int month, int day, int dayOfWeek)350 private int checkRule(int month, int day, int dayOfWeek) 351 { 352 int daysInMonth = getDaysInMonth(month, 1); 353 if (dayOfWeek == 0) 354 { 355 if (day <= 0 || day > daysInMonth) 356 throw new IllegalArgumentException("day out of range"); 357 return DOM_MODE; 358 } 359 else if (dayOfWeek > 0) 360 { 361 if (Math.abs(day) > (daysInMonth + 6) / 7) 362 throw new IllegalArgumentException("dayOfWeekInMonth out of range"); 363 if (dayOfWeek > Calendar.SATURDAY) 364 throw new IllegalArgumentException("dayOfWeek out of range"); 365 return DOW_IN_MONTH_MODE; 366 } 367 else 368 { 369 if (day == 0 || Math.abs(day) > daysInMonth) 370 throw new IllegalArgumentException("day out of range"); 371 if (dayOfWeek < -Calendar.SATURDAY) 372 throw new IllegalArgumentException("dayOfWeek out of range"); 373 if (day < 0) 374 return DOW_LE_DOM_MODE; 375 else 376 return DOW_GE_DOM_MODE; 377 } 378 } 379 380 381 /** 382 * Sets the daylight savings start rule. You must also set the 383 * end rule with <code>setEndRule</code> or the result of 384 * getOffset is undefined. For the parameters see the ten-argument 385 * constructor above. 386 * 387 * @param month The month where daylight savings start, zero 388 * based. You should use the constants in Calendar. 389 * @param day A day of month or day of week in month. 390 * @param dayOfWeek The day of week where daylight savings start. 391 * @param time The time in milliseconds standard time where daylight 392 * savings start. 393 * @see SimpleTimeZone 394 */ setStartRule(int month, int day, int dayOfWeek, int time)395 public void setStartRule(int month, int day, int dayOfWeek, int time) 396 { 397 this.startMode = checkRule(month, day, dayOfWeek); 398 this.startMonth = month; 399 // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations 400 // of this method. 401 this.startDay = Math.abs(day); 402 this.startDayOfWeek = Math.abs(dayOfWeek); 403 this.startTime = time; 404 useDaylight = true; 405 } 406 407 /** 408 * Sets the daylight savings end rule. You must also set the 409 * start rule with <code>setStartRule</code> or the result of 410 * getOffset is undefined. For the parameters see the ten-argument 411 * constructor above. 412 * 413 * @param rawOffset The time offset from GMT. 414 * @param id The identifier of this time zone. 415 * @param month The end month of daylight savings. 416 * @param day A day in month, or a day of week in month. 417 * @param dayOfWeek A day of week, when daylight savings ends. 418 * @param time A time in millis in standard time. 419 * @see #setStartRule 420 */ setEndRule(int month, int day, int dayOfWeek, int time)421 public void setEndRule(int month, int day, int dayOfWeek, int time) 422 { 423 this.endMode = checkRule(month, day, dayOfWeek); 424 this.endMonth = month; 425 // FIXME: XXX: JDK 1.2 allows negative values and has 2 new variations 426 // of this method. 427 this.endDay = Math.abs(day); 428 this.endDayOfWeek = Math.abs(dayOfWeek); 429 this.endTime = time; 430 useDaylight = true; 431 } 432 433 /** 434 * Gets the time zone offset, for current date, modified in case of 435 * daylight savings. This is the offset to add to UTC to get the local 436 * time. 437 * 438 * In the standard JDK the results given by this method may result in 439 * inaccurate results at the end of February or the beginning of March. 440 * To avoid this, you should use Calendar instead: 441 * <code>offset = cal.get(Calendar.ZONE_OFFSET) 442 * + cal.get(Calendar.DST_OFFSET);</code> 443 * 444 * You could also use in 445 * 446 * This version doesn't suffer this inaccuracy. 447 * 448 * @param era the era of the given date 449 * @param year the year of the given date 450 * @param month the month of the given date, 0 for January. 451 * @param day the day of month 452 * @param dayOfWeek the day of week; this must be matching the 453 * other fields. 454 * @param millis the millis in the day (in local standard time) 455 * @return the time zone offset in milliseconds. */ getOffset(int era, int year, int month, int day, int dayOfWeek, int millis)456 public int getOffset(int era, int year, int month, 457 int day, int dayOfWeek, int millis) 458 { 459 // This method is called by Calendar, so we mustn't use that class. 460 int daylightSavings = 0; 461 if (useDaylight && era == GregorianCalendar.AD && year >= startYear) 462 { 463 // This does only work for Gregorian calendars :-( 464 // This is mainly because setStartYear doesn't take an era. 465 466 boolean afterStart = !isBefore(year, month, day, dayOfWeek, millis, 467 startMode, startMonth, 468 startDay, startDayOfWeek, startTime); 469 boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis, 470 endMode, endMonth, 471 endDay, endDayOfWeek, endTime); 472 473 if (startMonth < endMonth) 474 { 475 // use daylight savings, if the date is after the start of 476 // savings, and before the end of savings. 477 daylightSavings = afterStart && beforeEnd ? dstSavings : 0; 478 } 479 else 480 { 481 // use daylight savings, if the date is before the end of 482 // savings, or after the start of savings. 483 daylightSavings = beforeEnd || afterStart ? dstSavings : 0; 484 } 485 } 486 return rawOffset + daylightSavings; 487 } 488 489 /** 490 * Returns the time zone offset to GMT in milliseconds, ignoring 491 * day light savings. 492 * @return the time zone offset. 493 */ getRawOffset()494 public int getRawOffset() 495 { 496 return rawOffset; 497 } 498 499 /** 500 * Sets the standard time zone offset to GMT. 501 * @param rawOffset The time offset from GMT in milliseconds. 502 */ setRawOffset(int rawOffset)503 public void setRawOffset(int rawOffset) 504 { 505 this.rawOffset = rawOffset; 506 } 507 508 /** 509 * Gets the daylight savings offset. This is a positive offset in 510 * milliseconds with respect to standard time. Typically this 511 * is one hour, but for some time zones this may be half an our. 512 * @return the daylight savings offset in milliseconds. 513 * 514 * @since 1.2 515 */ getDSTSavings()516 public int getDSTSavings() 517 { 518 return dstSavings; 519 } 520 521 /** 522 * Sets the daylight savings offset. This is a positive offset in 523 * milliseconds with respect to standard time. 524 * 525 * @param dstSavings the daylight savings offset in milliseconds. 526 * 527 * @since 1.2 528 */ setDSTSavings(int dstSavings)529 public void setDSTSavings(int dstSavings) 530 { 531 if (dstSavings <= 0) 532 throw new IllegalArgumentException("illegal value for dstSavings"); 533 534 this.dstSavings = dstSavings; 535 } 536 537 /** 538 * Returns if this time zone uses daylight savings time. 539 * @return true, if we use daylight savings time, false otherwise. 540 */ useDaylightTime()541 public boolean useDaylightTime() 542 { 543 return useDaylight; 544 } 545 546 /** 547 * Returns the number of days in the given month. It does always 548 * use the Gregorian leap year rule. 549 * @param month The month, zero based; use one of the Calendar constants. 550 * @param year The year. 551 */ getDaysInMonth(int month, int year)552 private int getDaysInMonth(int month, int year) 553 { 554 // Most of this is copied from GregorianCalendar.getActualMaximum() 555 if (month == Calendar.FEBRUARY) 556 { 557 return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) 558 ? 29 : 28; 559 } 560 else if (month < Calendar.AUGUST) 561 return 31 - (month & 1); 562 else 563 return 30 + (month & 1); 564 } 565 566 /** 567 * Checks if the date given in calXXXX, is before the change between 568 * dst and standard time. 569 * @param calYear the year of the date to check (for leap day cheking). 570 * @param calMonth the month of the date to check. 571 * @param calDay the day of month of the date to check. 572 * @param calDayOfWeek the day of week of the date to check. 573 * @param calMillis the millis of day of the date to check (standard time). 574 * @param mode the change mode; same semantic as startMode. 575 * @param month the change month; same semantic as startMonth. 576 * @param day the change day; same semantic as startDay. 577 * @param dayOfWeek the change day of week; 578 * @param millis the change time in millis since midnight standard time. 579 * same semantic as startDayOfWeek. 580 * @return true, if cal is before the change, false if cal is on 581 * or after the change. 582 */ isBefore(int calYear, int calMonth, int calDayOfMonth, int calDayOfWeek, int calMillis, int mode, int month, int day, int dayOfWeek, int millis)583 private boolean isBefore(int calYear, 584 int calMonth, int calDayOfMonth, int calDayOfWeek, 585 int calMillis, int mode, int month, 586 int day, int dayOfWeek, int millis) 587 { 588 589 // This method is called by Calendar, so we mustn't use that class. 590 // We have to do all calculations by hand. 591 592 // check the months: 593 594 // XXX - this is not correct: 595 // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may 596 // be in a different month. 597 if (calMonth != month) 598 return calMonth < month; 599 600 // check the day: 601 switch (mode) 602 { 603 case DOM_MODE: 604 if (calDayOfMonth != day) 605 return calDayOfMonth < day; 606 break; 607 case DOW_IN_MONTH_MODE: 608 { 609 // This computes the day of month of the day of type 610 // "dayOfWeek" that lies in the same (sunday based) week as cal. 611 calDayOfMonth += (dayOfWeek - calDayOfWeek); 612 613 // Now we convert it to 7 based number (to get a one based offset 614 // after dividing by 7). If we count from the end of the 615 // month, we get want a -7 based number counting the days from 616 // the end: 617 618 if (day < 0) 619 calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; 620 else 621 calDayOfMonth += 6; 622 623 // day > 0 day < 0 624 // S M T W T F S S M T W T F S 625 // 7 8 9 10 11 12 -36-35-34-33-32-31 626 // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 627 // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 628 // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 629 // 34 35 36 -9 -8 -7 630 631 // Now we calculate the day of week in month: 632 int week = calDayOfMonth / 7; 633 // day > 0 day < 0 634 // S M T W T F S S M T W T F S 635 // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 636 // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 637 // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 638 // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 639 // 4 5 5 -1 -1 -1 640 641 if (week != day) 642 return week < day; 643 644 if (calDayOfWeek != dayOfWeek) 645 return calDayOfWeek < dayOfWeek; 646 647 // daylight savings starts/ends on the given day. 648 break; 649 } 650 651 case DOW_LE_DOM_MODE: 652 // The greatest sunday before or equal December, 12 653 // is the same as smallest sunday after or equal December, 6. 654 day -= 6; 655 656 case DOW_GE_DOM_MODE: 657 658 // Calculate the day of month of the day of type 659 // "dayOfWeek" that lies before (or on) the given date. 660 calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) 661 + calDayOfWeek - dayOfWeek; 662 if (calDayOfMonth < day) 663 return true; 664 if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) 665 return false; 666 // now we have the same day 667 break; 668 } 669 // the millis decides: 670 return (calMillis < millis); 671 } 672 673 /** 674 * Determines if the given date is in daylight savings time. 675 * @return true, if it is in daylight savings time, false otherwise. 676 */ inDaylightTime(Date date)677 public boolean inDaylightTime(Date date) 678 { 679 Calendar cal = Calendar.getInstance(this); 680 cal.setTime(date); 681 return (cal.get(Calendar.DST_OFFSET) != 0); 682 } 683 684 /** 685 * Generates the hashCode for the SimpleDateFormat object. It is 686 * the rawOffset, possibly, if useDaylightSavings is true, xored 687 * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. 688 */ hashCode()689 public synchronized int hashCode() 690 { 691 return rawOffset ^ 692 (useDaylight ? 693 startMonth ^ startDay ^ startDayOfWeek ^ startTime 694 ^ endMonth ^ endDay ^ endDayOfWeek ^ endTime : 0); 695 } 696 equals(Object o)697 public synchronized boolean equals(Object o) 698 { 699 if (this == o) 700 return true; 701 if (!(o instanceof SimpleTimeZone)) 702 return false; 703 SimpleTimeZone zone = (SimpleTimeZone) o; 704 if (zone.hashCode() != hashCode() 705 || !getID().equals(zone.getID()) 706 || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) 707 return false; 708 if (!useDaylight) 709 return true; 710 return (startYear == zone.startYear 711 && startMonth == zone.startMonth 712 && startDay == zone.startDay 713 && startDayOfWeek == zone.startDayOfWeek 714 && startTime == zone.startTime 715 && endMonth == zone.endMonth 716 && endDay == zone.endDay 717 && endDayOfWeek == zone.endDayOfWeek 718 && endTime == zone.endTime); 719 } 720 721 /** 722 * Test if the other time zone uses the same rule and only 723 * possibly differs in ID. This implementation for this particular 724 * class will return true if the other object is a SimpleTimeZone, 725 * the raw offsets and useDaylight are identical and if useDaylight 726 * is true, also the start and end datas are identical. 727 * @return true if this zone uses the same rule. 728 */ hasSameRules(TimeZone other)729 public boolean hasSameRules(TimeZone other) 730 { 731 if (this == other) 732 return true; 733 if (!(other instanceof SimpleTimeZone)) 734 return false; 735 SimpleTimeZone zone = (SimpleTimeZone) other; 736 if (zone.hashCode() != hashCode() 737 || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) 738 return false; 739 if (!useDaylight) 740 return true; 741 return (startYear == zone.startYear 742 && startMonth == zone.startMonth 743 && startDay == zone.startDay 744 && startDayOfWeek == zone.startDayOfWeek 745 && startTime == zone.startTime 746 && endMonth == zone.endMonth 747 && endDay == zone.endDay 748 && endDayOfWeek == zone.endDayOfWeek && endTime == zone.endTime); 749 } 750 751 /** 752 * Returns a string representation of this SimpleTimeZone object. 753 * @return a string representation of this SimpleTimeZone object. 754 */ toString()755 public String toString() 756 { 757 // the test for useDaylight is an incompatibility to jdk1.2, but 758 // I think this shouldn't hurt. 759 return getClass().getName() + "[" 760 + "id=" + getID() 761 + ",offset=" + rawOffset 762 + ",dstSavings=" + dstSavings 763 + ",useDaylight=" + useDaylight 764 + (useDaylight ? 765 ",startYear=" + startYear 766 + ",startMode=" + startMode 767 + ",startMonth=" + startMonth 768 + ",startDay=" + startDay 769 + ",startDayOfWeek=" + startDayOfWeek 770 + ",startTime=" + startTime 771 + ",endMode=" + endMode 772 + ",endMonth=" + endMonth 773 + ",endDay=" + endDay 774 + ",endDayOfWeek=" + endDayOfWeek 775 + ",endTime=" + endTime : "") + "]"; 776 } 777 778 /** 779 * Reads a serialized simple time zone from stream. 780 * @see #writeObject 781 */ readObject(java.io.ObjectInputStream input)782 private void readObject(java.io.ObjectInputStream input) 783 throws java.io.IOException, ClassNotFoundException 784 { 785 input.defaultReadObject(); 786 if (serialVersionOnStream == 0) 787 { 788 // initialize the new fields to default values. 789 dstSavings = 60 * 60 * 1000; 790 endMode = DOW_IN_MONTH_MODE; 791 startMode = DOW_IN_MONTH_MODE; 792 serialVersionOnStream = 1; 793 } 794 else 795 { 796 int length = input.readInt(); 797 byte[] byteArray = new byte[length]; 798 input.read(byteArray, 0, length); 799 if (length >= 4) 800 { 801 // Lets hope that Sun does extensions to the serialized 802 // form in a sane manner. 803 startDay = byteArray[0]; 804 startDayOfWeek = byteArray[1]; 805 endDay = byteArray[2]; 806 endDayOfWeek = byteArray[3]; 807 } 808 } 809 } 810 811 /** 812 * Serializes this object to a stream. @serialdata The object is 813 * first written in the old JDK 1.1 format, so that it can be read 814 * by by the old classes. This means, that the 815 * <code>start/endDay(OfWeek)</code>-Fields are written in the 816 * DOW_IN_MONTH_MODE rule, since this was the only supported rule 817 * in 1.1. 818 * 819 * In the optional section, we write first the length of an byte 820 * array as int and afterwards the byte array itself. The byte 821 * array contains in this release four elements, namely the real 822 * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. 823 * These fields are needed, because for compatibility reasons only 824 * approximative values are written to the required section, as 825 * described above. 826 */ writeObject(java.io.ObjectOutputStream output)827 private void writeObject(java.io.ObjectOutputStream output) 828 throws java.io.IOException 829 { 830 byte[] byteArray = new byte[] 831 { 832 (byte) startDay, (byte) startDayOfWeek, 833 (byte) endDay, (byte) endDayOfWeek}; 834 835 /* calculate the approximation for JDK 1.1 */ 836 switch (startMode) 837 { 838 case DOM_MODE: 839 startDayOfWeek = Calendar.SUNDAY; // random day of week 840 // fall through 841 case DOW_GE_DOM_MODE: 842 case DOW_LE_DOM_MODE: 843 startDay = (startDay + 6) / 7; 844 } 845 switch (endMode) 846 { 847 case DOM_MODE: 848 endDayOfWeek = Calendar.SUNDAY; 849 // fall through 850 case DOW_GE_DOM_MODE: 851 case DOW_LE_DOM_MODE: 852 endDay = (endDay + 6) / 7; 853 } 854 855 // the required part: 856 output.defaultWriteObject(); 857 // the optional part: 858 output.writeInt(byteArray.length); 859 output.write(byteArray, 0, byteArray.length); 860 } 861 } 862