GNU Classpath (0.20) | |
Frames | No Frames |
1: /* java.util.SimpleTimeZone 2: Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 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: /** 43: * This class represents a simple time zone offset and handles 44: * daylight savings. It can only handle one daylight savings rule, so 45: * it can't represent historical changes. 46: * 47: * This object is tightly bound to the Gregorian calendar. It assumes 48: * a regular seven days week, and the month lengths are that of the 49: * Gregorian Calendar. It can only handle daylight savings for years 50: * lying in the AD era. 51: * 52: * @see Calendar 53: * @see GregorianCalendar 54: * @author Jochen Hoenicke 55: */ 56: public class SimpleTimeZone extends TimeZone 57: { 58: /** 59: * The raw time zone offset in milliseconds to GMT, ignoring 60: * daylight savings. 61: * @serial 62: */ 63: private int rawOffset; 64: 65: /** 66: * True, if this timezone uses daylight savings, false otherwise. 67: * @serial 68: */ 69: private boolean useDaylight; 70: 71: /** 72: * The daylight savings offset. This is a positive offset in 73: * milliseconds with respect to standard time. Typically this 74: * is one hour, but for some time zones this may be half an hour. 75: * @serial 76: * @since JDK1.1.4 77: */ 78: private int dstSavings = 60 * 60 * 1000; 79: 80: /** 81: * The first year, in which daylight savings rules applies. 82: * @serial 83: */ 84: private int startYear; 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: * This variable specifies the mode that startTime is specified in. By 152: * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. For 153: * startTime, STANDARD_TIME and WALL_TIME are equivalent. 154: * @serial 155: */ 156: private int startTimeMode = WALL_TIME; 157: 158: /** 159: * The month in which daylight savings ends. This is one of the 160: * constants Calendar.JANUARY, ..., Calendar.DECEMBER. 161: * @serial 162: */ 163: private int endMonth; 164: 165: /** 166: * This variable gives the mode for the end of daylight savings rule. 167: * It can take the same values as startMode. 168: * @serial 169: * @see #startMode 170: */ 171: private int endMode; 172: 173: /** 174: * This variable can have different meanings. See startMode for details 175: * @serial 176: * @see #startMode 177: */ 178: private int endDay; 179: 180: /** 181: * This variable specifies the day of week the change takes place. If 182: * endMode == DOM_MODE, this is undefined. 183: * @serial 184: * @see #startMode 185: */ 186: private int endDayOfWeek; 187: 188: /** 189: * This variable specifies the time of change back to standard time. 190: * This time is given in milliseconds after midnight local 191: * standard time. 192: * @serial 193: */ 194: private int endTime; 195: 196: /** 197: * This variable specifies the mode that endTime is specified in. By 198: * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. 199: * @serial 200: */ 201: private int endTimeMode = WALL_TIME; 202: 203: /** 204: * This variable points to a deprecated array from JDK 1.1. It is 205: * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. 206: * The array contains the lengths of the months in the year and is 207: * assigned from a private static final field to avoid allocating 208: * the array for every instance of the object. 209: * Note that static final fields are not serialized. 210: * @serial 211: */ 212: private byte[] monthLength = monthArr; 213: private static final byte[] monthArr = 214: { 215: 31, 28, 31, 30, 31, 30, 31, 31, 30, 216: 31, 30, 31 217: }; 218: 219: /** 220: * The version of the serialized data on the stream. 221: * <dl> 222: * <dt>0 or not present on stream</dt> 223: * <dd> JDK 1.1.3 or earlier, only provides this fields: 224: * rawOffset, startDay, startDayOfWeek, startMonth, startTime, 225: * startYear, endDay, endDayOfWeek, endMonth, endTime 226: * </dd> 227: * <dd> JDK 1.1.4 or later. This includes three new fields, namely 228: * startMode, endMode and dstSavings. And there is a optional section 229: * as described in writeObject. 230: * </dd> 231: * </dl> 232: * 233: * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old 234: * version. 235: * 236: * When streaming out this class it is always written in the latest 237: * version. 238: * @serial 239: * @since JDK1.1.4 240: */ 241: private int serialVersionOnStream = 2; 242: private static final long serialVersionUID = -403250971215465050L; 243: 244: /** 245: * Constant to indicate that start and end times are specified in standard 246: * time, without adjusting for daylight savings. 247: */ 248: public static final int STANDARD_TIME = 1; 249: 250: /** 251: * Constant to indicate that start and end times are specified in wall 252: * time, adjusting for daylight savings. This is the default. 253: */ 254: public static final int WALL_TIME = 0; 255: 256: /** 257: * Constant to indicate that start and end times are specified in UTC. 258: */ 259: public static final int UTC_TIME = 2; 260: 261: /** 262: * Create a <code>SimpleTimeZone</code> with the given time offset 263: * from GMT and without daylight savings. 264: * @param rawOffset the time offset from GMT in milliseconds. 265: * @param id The identifier of this time zone. 266: */ 267: public SimpleTimeZone(int rawOffset, String id) 268: { 269: this.rawOffset = rawOffset; 270: setID(id); 271: useDaylight = false; 272: startYear = 0; 273: } 274: 275: /** 276: * Create a <code>SimpleTimeZone</code> with the given time offset 277: * from GMT and with daylight savings. The start/end parameters 278: * can have different meaning (replace WEEKDAY with a real day of 279: * week). Only the first two meanings were supported by earlier 280: * versions of jdk. 281: * 282: * <dl> 283: * <dt><code>day > 0, dayOfWeek = Calendar.WEEKDAY</code></dt> 284: * <dd>The start/end of daylight savings is on the <code>day</code>-th 285: * <code>WEEKDAY</code> in the given month. </dd> 286: * <dt><code>day < 0, dayOfWeek = Calendar.WEEKDAY</code></dt> 287: * <dd>The start/end of daylight savings is on the <code>-day</code>-th 288: * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd> 289: * <dt><code>day > 0, dayOfWeek = 0</code></dt> 290: * <dd>The start/end of daylight is on the <code>day</code>-th day of 291: * the month. </dd> 292: * <dt><code>day > 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> 293: * <dd>The start/end of daylight is on the first WEEKDAY on or after 294: * the <code>day</code>-th day of the month. You must make sure that 295: * this day lies in the same month. </dd> 296: * <dt><code>day < 0, dayOfWeek = -Calendar.WEEKDAY</code></dt> 297: * <dd>The start/end of daylight is on the first WEEKDAY on or 298: * <i>before</i> the <code>-day</code>-th day of the month. You 299: * must make sure that this day lies in the same month. </dd> 300: * </dl> 301: * 302: * If you give a non existing month, a day that is zero, or too big, 303: * or a dayOfWeek that is too big, the result is undefined. 304: * 305: * The start rule must have a different month than the end rule. 306: * This restriction shouldn't hurt for all possible time zones. 307: * 308: * @param rawOffset The time offset from GMT in milliseconds. 309: * @param id The identifier of this time zone. 310: * @param startMonth The start month of daylight savings; use the 311: * constants in Calendar. 312: * @param startDayOfWeekInMonth A day in month or a day of week number, as 313: * described above. 314: * @param startDayOfWeek The start rule day of week; see above. 315: * @param startTime A time in millis in standard time. 316: * @param endMonth The end month of daylight savings; use the 317: * constants in Calendar. 318: * @param endDayOfWeekInMonth A day in month or a day of week number, as 319: * described above. 320: * @param endDayOfWeek The end rule day of week; see above. 321: * @param endTime A time in millis in standard time. 322: * @throws IllegalArgumentException if parameters are invalid or out of 323: * range. 324: */ 325: public SimpleTimeZone(int rawOffset, String id, int startMonth, 326: int startDayOfWeekInMonth, int startDayOfWeek, 327: int startTime, int endMonth, int endDayOfWeekInMonth, 328: int endDayOfWeek, int endTime) 329: { 330: this.rawOffset = rawOffset; 331: setID(id); 332: useDaylight = true; 333: 334: setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); 335: setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); 336: if (startMonth == endMonth) 337: throw new IllegalArgumentException("startMonth and endMonth must be different"); 338: this.startYear = 0; 339: } 340: 341: /** 342: * This constructs a new SimpleTimeZone that supports a daylight savings 343: * rule. The parameter are the same as for the constructor above, except 344: * there is the additional dstSavaings parameter. 345: * 346: * @param dstSavings the amount of savings for daylight savings 347: * time in milliseconds. This must be positive. 348: * @since 1.2 349: */ 350: public SimpleTimeZone(int rawOffset, String id, int startMonth, 351: int startDayOfWeekInMonth, int startDayOfWeek, 352: int startTime, int endMonth, int endDayOfWeekInMonth, 353: int endDayOfWeek, int endTime, int dstSavings) 354: { 355: this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, 356: startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); 357: 358: this.dstSavings = dstSavings; 359: } 360: 361: /** 362: * This constructs a new SimpleTimeZone that supports a daylight savings 363: * rule. The parameter are the same as for the constructor above, except 364: * there are the additional startTimeMode, endTimeMode, and dstSavings 365: * parameters. 366: * 367: * @param startTimeMode the mode that start times are specified in. One of 368: * WALL_TIME, STANDARD_TIME, or UTC_TIME. 369: * @param endTimeMode the mode that end times are specified in. One of 370: * WALL_TIME, STANDARD_TIME, or UTC_TIME. 371: * @param dstSavings the amount of savings for daylight savings 372: * time in milliseconds. This must be positive. 373: * @throws IllegalArgumentException if parameters are invalid or out of 374: * range. 375: * @since 1.4 376: */ 377: public SimpleTimeZone(int rawOffset, String id, int startMonth, 378: int startDayOfWeekInMonth, int startDayOfWeek, 379: int startTime, int startTimeMode, int endMonth, 380: int endDayOfWeekInMonth, int endDayOfWeek, 381: int endTime, int endTimeMode, int dstSavings) 382: { 383: this.rawOffset = rawOffset; 384: setID(id); 385: useDaylight = true; 386: 387: if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) 388: throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); 389: if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) 390: throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); 391: this.startTimeMode = startTimeMode; 392: this.endTimeMode = endTimeMode; 393: 394: setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); 395: setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); 396: if (startMonth == endMonth) 397: throw new IllegalArgumentException("startMonth and endMonth must be different"); 398: this.startYear = 0; 399: 400: this.dstSavings = dstSavings; 401: } 402: 403: /** 404: * Sets the first year, where daylight savings applies. The daylight 405: * savings rule never apply for years in the BC era. Note that this 406: * is gregorian calendar specific. 407: * @param year the start year. 408: */ 409: public void setStartYear(int year) 410: { 411: startYear = year; 412: useDaylight = true; 413: } 414: 415: /** 416: * Checks if the month, day, dayOfWeek arguments are in range and 417: * returns the mode of the rule. 418: * @param month the month parameter as in the constructor 419: * @param day the day parameter as in the constructor 420: * @param dayOfWeek the day of week parameter as in the constructor 421: * @return the mode of this rule see startMode. 422: * @exception IllegalArgumentException if parameters are out of range. 423: * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) 424: * @see #startMode 425: */ 426: private int checkRule(int month, int day, int dayOfWeek) 427: { 428: if (month < 0 || month > 11) 429: throw new IllegalArgumentException("month out of range"); 430: 431: int daysInMonth = getDaysInMonth(month, 1); 432: if (dayOfWeek == 0) 433: { 434: if (day <= 0 || day > daysInMonth) 435: throw new IllegalArgumentException("day out of range"); 436: return DOM_MODE; 437: } 438: else if (dayOfWeek > 0) 439: { 440: if (Math.abs(day) > (daysInMonth + 6) / 7) 441: throw new IllegalArgumentException("dayOfWeekInMonth out of range"); 442: if (dayOfWeek > Calendar.SATURDAY) 443: throw new IllegalArgumentException("dayOfWeek out of range"); 444: return DOW_IN_MONTH_MODE; 445: } 446: else 447: { 448: if (day == 0 || Math.abs(day) > daysInMonth) 449: throw new IllegalArgumentException("day out of range"); 450: if (dayOfWeek < -Calendar.SATURDAY) 451: throw new IllegalArgumentException("dayOfWeek out of range"); 452: if (day < 0) 453: return DOW_LE_DOM_MODE; 454: else 455: return DOW_GE_DOM_MODE; 456: } 457: } 458: 459: /** 460: * Sets the daylight savings start rule. You must also set the 461: * end rule with <code>setEndRule</code> or the result of 462: * getOffset is undefined. For the parameters see the ten-argument 463: * constructor above. 464: * 465: * @param month The month where daylight savings start, zero 466: * based. You should use the constants in Calendar. 467: * @param day A day of month or day of week in month. 468: * @param dayOfWeek The day of week where daylight savings start. 469: * @param time The time in milliseconds standard time where daylight 470: * savings start. 471: * @exception IllegalArgumentException if parameters are out of range. 472: * @see SimpleTimeZone 473: */ 474: public void setStartRule(int month, int day, int dayOfWeek, int time) 475: { 476: this.startMode = checkRule(month, day, dayOfWeek); 477: this.startMonth = month; 478: this.startDay = day; 479: this.startDayOfWeek = Math.abs(dayOfWeek); 480: if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) 481: this.startTime = time; 482: else 483: // Convert from UTC to STANDARD 484: this.startTime = time + this.rawOffset; 485: useDaylight = true; 486: } 487: 488: /** 489: * Sets the daylight savings start rule. You must also set the 490: * end rule with <code>setEndRule</code> or the result of 491: * getOffset is undefined. For the parameters see the ten-argument 492: * constructor above. 493: * 494: * Note that this API isn't incredibly well specified. It appears that the 495: * after flag must override the parameters, since normally, the day and 496: * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or 497: * before mode is chosen. But if after == true, this implementation 498: * overrides the signs of the other arguments. And if dayOfWeek == 0, it 499: * falls back to the behavior in the other APIs. I guess this should be 500: * checked against Sun's implementation. 501: * 502: * @param month The month where daylight savings start, zero 503: * based. You should use the constants in Calendar. 504: * @param day A day of month or day of week in month. 505: * @param dayOfWeek The day of week where daylight savings start. 506: * @param time The time in milliseconds standard time where daylight 507: * savings start. 508: * @param after If true, day and dayOfWeek specify first day of week on or 509: * after day, else first day of week on or before. 510: * @since 1.2 511: * @see SimpleTimeZone 512: */ 513: public void setStartRule(int month, int day, int dayOfWeek, int time, 514: boolean after) 515: { 516: // FIXME: XXX: Validate that checkRule and offset processing work with on 517: // or before mode. 518: this.startDay = after ? Math.abs(day) : -Math.abs(day); 519: this.startDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); 520: this.startMode = (dayOfWeek != 0) 521: ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) 522: : checkRule(month, day, dayOfWeek); 523: this.startDay = Math.abs(this.startDay); 524: this.startDayOfWeek = Math.abs(this.startDayOfWeek); 525: 526: this.startMonth = month; 527: 528: if (this.startTimeMode == WALL_TIME || this.startTimeMode == STANDARD_TIME) 529: this.startTime = time; 530: else 531: // Convert from UTC to STANDARD 532: this.startTime = time + this.rawOffset; 533: useDaylight = true; 534: } 535: 536: /** 537: * Sets the daylight savings start rule. You must also set the 538: * end rule with <code>setEndRule</code> or the result of 539: * getOffset is undefined. For the parameters see the ten-argument 540: * constructor above. 541: * 542: * @param month The month where daylight savings start, zero 543: * based. You should use the constants in Calendar. 544: * @param day A day of month or day of week in month. 545: * @param time The time in milliseconds standard time where daylight 546: * savings start. 547: * @see SimpleTimeZone 548: * @since 1.2 549: */ 550: public void setStartRule(int month, int day, int time) 551: { 552: setStartRule(month, day, 0, time); 553: } 554: 555: /** 556: * Sets the daylight savings end rule. You must also set the 557: * start rule with <code>setStartRule</code> or the result of 558: * getOffset is undefined. For the parameters see the ten-argument 559: * constructor above. 560: * 561: * @param month The end month of daylight savings. 562: * @param day A day in month, or a day of week in month. 563: * @param dayOfWeek A day of week, when daylight savings ends. 564: * @param time A time in millis in standard time. 565: * @see #setStartRule(int, int, int, int) 566: */ 567: public void setEndRule(int month, int day, int dayOfWeek, int time) 568: { 569: this.endMode = checkRule(month, day, dayOfWeek); 570: this.endMonth = month; 571: this.endDay = day; 572: this.endDayOfWeek = Math.abs(dayOfWeek); 573: if (this.endTimeMode == WALL_TIME) 574: this.endTime = time; 575: else if (this.endTimeMode == STANDARD_TIME) 576: // Convert from STANDARD to DST 577: this.endTime = time + this.dstSavings; 578: else 579: // Convert from UTC to DST 580: this.endTime = time + this.rawOffset + this.dstSavings; 581: useDaylight = true; 582: } 583: 584: /** 585: * Sets the daylight savings end rule. You must also set the 586: * start rule with <code>setStartRule</code> or the result of 587: * getOffset is undefined. For the parameters see the ten-argument 588: * constructor above. 589: * 590: * Note that this API isn't incredibly well specified. It appears that the 591: * after flag must override the parameters, since normally, the day and 592: * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or 593: * before mode is chosen. But if after == true, this implementation 594: * overrides the signs of the other arguments. And if dayOfWeek == 0, it 595: * falls back to the behavior in the other APIs. I guess this should be 596: * checked against Sun's implementation. 597: * 598: * @param month The end month of daylight savings. 599: * @param day A day in month, or a day of week in month. 600: * @param dayOfWeek A day of week, when daylight savings ends. 601: * @param time A time in millis in standard time. 602: * @param after If true, day and dayOfWeek specify first day of week on or 603: * after day, else first day of week on or before. 604: * @since 1.2 605: * @see #setStartRule(int, int, int, int, boolean) 606: */ 607: public void setEndRule(int month, int day, int dayOfWeek, int time, 608: boolean after) 609: { 610: // FIXME: XXX: Validate that checkRule and offset processing work with on 611: // or before mode. 612: this.endDay = after ? Math.abs(day) : -Math.abs(day); 613: this.endDayOfWeek = after ? Math.abs(dayOfWeek) : -Math.abs(dayOfWeek); 614: this.endMode = (dayOfWeek != 0) 615: ? (after ? DOW_GE_DOM_MODE : DOW_LE_DOM_MODE) 616: : checkRule(month, day, dayOfWeek); 617: this.endDay = Math.abs(this.endDay); 618: this.endDayOfWeek = Math.abs(endDayOfWeek); 619: 620: this.endMonth = month; 621: 622: if (this.endTimeMode == WALL_TIME) 623: this.endTime = time; 624: else if (this.endTimeMode == STANDARD_TIME) 625: // Convert from STANDARD to DST 626: this.endTime = time + this.dstSavings; 627: else 628: // Convert from UTC to DST 629: this.endTime = time + this.rawOffset + this.dstSavings; 630: useDaylight = true; 631: } 632: 633: /** 634: * Sets the daylight savings end rule. You must also set the 635: * start rule with <code>setStartRule</code> or the result of 636: * getOffset is undefined. For the parameters see the ten-argument 637: * constructor above. 638: * 639: * @param month The end month of daylight savings. 640: * @param day A day in month, or a day of week in month. 641: * @param time A time in millis in standard time. 642: * @see #setStartRule(int, int, int) 643: */ 644: public void setEndRule(int month, int day, int time) 645: { 646: setEndRule(month, day, 0, time); 647: } 648: 649: /** 650: * Gets the time zone offset, for current date, modified in case of 651: * daylight savings. This is the offset to add to UTC to get the local 652: * time. 653: * 654: * In the standard JDK the results given by this method may result in 655: * inaccurate results at the end of February or the beginning of March. 656: * To avoid this, you should use Calendar instead: 657: * <code>offset = cal.get(Calendar.ZONE_OFFSET) 658: * + cal.get(Calendar.DST_OFFSET);</code> 659: * 660: * This version doesn't suffer this inaccuracy. 661: * 662: * The arguments don't follow the approach for setting start and end rules. 663: * The day must be a positive number and dayOfWeek must be a positive value 664: * from Calendar. dayOfWeek is redundant, but must match the other values 665: * or an inaccurate result may be returned. 666: * 667: * @param era the era of the given date 668: * @param year the year of the given date 669: * @param month the month of the given date, 0 for January. 670: * @param day the day of month 671: * @param dayOfWeek the day of week; this must match the other fields. 672: * @param millis the millis in the day (in local standard time) 673: * @return the time zone offset in milliseconds. 674: * @throws IllegalArgumentException if arguments are incorrect. 675: */ 676: public int getOffset(int era, int year, int month, int day, int dayOfWeek, 677: int millis) 678: { 679: int daysInMonth = getDaysInMonth(month, year); 680: if (day < 1 || day > daysInMonth) 681: throw new IllegalArgumentException("day out of range"); 682: if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) 683: throw new IllegalArgumentException("dayOfWeek out of range"); 684: if (month < Calendar.JANUARY || month > Calendar.DECEMBER) 685: throw new IllegalArgumentException("month out of range:" + month); 686: 687: // This method is called by Calendar, so we mustn't use that class. 688: int daylightSavings = 0; 689: if (useDaylight && era == GregorianCalendar.AD && year >= startYear) 690: { 691: // This does only work for Gregorian calendars :-( 692: // This is mainly because setStartYear doesn't take an era. 693: boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, 694: startMode, startMonth, startDay, 695: startDayOfWeek, startTime); 696: boolean beforeEnd = isBefore(year, month, day, dayOfWeek, 697: millis + dstSavings, 698: endMode, endMonth, endDay, endDayOfWeek, 699: endTime); 700: 701: if (startMonth < endMonth) 702: // use daylight savings, if the date is after the start of 703: // savings, and before the end of savings. 704: daylightSavings = afterStart && beforeEnd ? dstSavings : 0; 705: else 706: // use daylight savings, if the date is before the end of 707: // savings, or after the start of savings. 708: daylightSavings = beforeEnd || afterStart ? dstSavings : 0; 709: } 710: return rawOffset + daylightSavings; 711: } 712: 713: /** 714: * Returns the time zone offset to GMT in milliseconds, ignoring 715: * day light savings. 716: * @return the time zone offset. 717: */ 718: public int getRawOffset() 719: { 720: return rawOffset; 721: } 722: 723: /** 724: * Sets the standard time zone offset to GMT. 725: * @param rawOffset The time offset from GMT in milliseconds. 726: */ 727: public void setRawOffset(int rawOffset) 728: { 729: this.rawOffset = rawOffset; 730: } 731: 732: /** 733: * Gets the daylight savings offset. This is a positive offset in 734: * milliseconds with respect to standard time. Typically this 735: * is one hour, but for some time zones this may be half an our. 736: * @return the daylight savings offset in milliseconds. 737: * 738: * @since 1.2 739: */ 740: public int getDSTSavings() 741: { 742: return dstSavings; 743: } 744: 745: /** 746: * Sets the daylight savings offset. This is a positive offset in 747: * milliseconds with respect to standard time. 748: * 749: * @param dstSavings the daylight savings offset in milliseconds. 750: * 751: * @since 1.2 752: */ 753: public void setDSTSavings(int dstSavings) 754: { 755: if (dstSavings <= 0) 756: throw new IllegalArgumentException("illegal value for dstSavings"); 757: 758: this.dstSavings = dstSavings; 759: } 760: 761: /** 762: * Returns if this time zone uses daylight savings time. 763: * @return true, if we use daylight savings time, false otherwise. 764: */ 765: public boolean useDaylightTime() 766: { 767: return useDaylight; 768: } 769: 770: /** 771: * Returns the number of days in the given month. 772: * Uses gregorian rules prior to 1582 (The default and earliest cutover) 773: * @param month The month, zero based; use one of the Calendar constants. 774: * @param year The year. 775: */ 776: private int getDaysInMonth(int month, int year) 777: { 778: if (month == Calendar.FEBRUARY) 779: { 780: if ((year & 3) != 0) 781: return 28; 782: 783: // Assume default Gregorian cutover, 784: // all years prior to this must be Julian 785: if (year < 1582) 786: return 29; 787: 788: // Gregorian rules 789: return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28; 790: } 791: else 792: return monthArr[month]; 793: } 794: 795: /** 796: * Checks if the date given in calXXXX, is before the change between 797: * dst and standard time. 798: * @param calYear the year of the date to check (for leap day checking). 799: * @param calMonth the month of the date to check. 800: * @param calDayOfMonth the day of month of the date to check. 801: * @param calDayOfWeek the day of week of the date to check. 802: * @param calMillis the millis of day of the date to check (standard time). 803: * @param mode the change mode; same semantic as startMode. 804: * @param month the change month; same semantic as startMonth. 805: * @param day the change day; same semantic as startDay. 806: * @param dayOfWeek the change day of week; 807: * @param millis the change time in millis since midnight standard time. 808: * same semantic as startDayOfWeek. 809: * @return true, if cal is before the change, false if cal is on 810: * or after the change. 811: */ 812: private boolean isBefore(int calYear, int calMonth, int calDayOfMonth, 813: int calDayOfWeek, int calMillis, int mode, 814: int month, int day, int dayOfWeek, int millis) 815: { 816: // This method is called by Calendar, so we mustn't use that class. 817: // We have to do all calculations by hand. 818: // check the months: 819: // XXX - this is not correct: 820: // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may 821: // be in a different month. 822: if (calMonth != month) 823: return calMonth < month; 824: 825: // check the day: 826: switch (mode) 827: { 828: case DOM_MODE: 829: if (calDayOfMonth != day) 830: return calDayOfMonth < day; 831: break; 832: case DOW_IN_MONTH_MODE: 833: { 834: // This computes the day of month of the day of type 835: // "dayOfWeek" that lies in the same (sunday based) week as cal. 836: calDayOfMonth += (dayOfWeek - calDayOfWeek); 837: 838: // Now we convert it to 7 based number (to get a one based offset 839: // after dividing by 7). If we count from the end of the 840: // month, we get want a -7 based number counting the days from 841: // the end: 842: if (day < 0) 843: calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; 844: else 845: calDayOfMonth += 6; 846: 847: // day > 0 day < 0 848: // S M T W T F S S M T W T F S 849: // 7 8 9 10 11 12 -36-35-34-33-32-31 850: // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 851: // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 852: // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 853: // 34 35 36 -9 -8 -7 854: // Now we calculate the day of week in month: 855: int week = calDayOfMonth / 7; 856: 857: // day > 0 day < 0 858: // S M T W T F S S M T W T F S 859: // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 860: // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 861: // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 862: // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 863: // 4 5 5 -1 -1 -1 864: if (week != day) 865: return week < day; 866: 867: if (calDayOfWeek != dayOfWeek) 868: return calDayOfWeek < dayOfWeek; 869: 870: // daylight savings starts/ends on the given day. 871: break; 872: } 873: case DOW_LE_DOM_MODE: 874: // The greatest sunday before or equal December, 12 875: // is the same as smallest sunday after or equal December, 6. 876: day = Math.abs(day) - 6; 877: case DOW_GE_DOM_MODE: 878: // Calculate the day of month of the day of type 879: // "dayOfWeek" that lies before (or on) the given date. 880: calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek 881: - dayOfWeek; 882: if (calDayOfMonth < day) 883: return true; 884: if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) 885: return false; 886: 887: // now we have the same day 888: break; 889: } 890: 891: // the millis decides: 892: return (calMillis < millis); 893: } 894: 895: /** 896: * Determines if the given date is in daylight savings time. 897: * @return true, if it is in daylight savings time, false otherwise. 898: */ 899: public boolean inDaylightTime(Date date) 900: { 901: Calendar cal = Calendar.getInstance(this); 902: cal.setTime(date); 903: return (cal.get(Calendar.DST_OFFSET) != 0); 904: } 905: 906: /** 907: * Generates the hashCode for the SimpleDateFormat object. It is 908: * the rawOffset, possibly, if useDaylightSavings is true, xored 909: * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. 910: */ 911: public synchronized int hashCode() 912: { 913: return rawOffset 914: ^ (useDaylight 915: ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth 916: ^ endDay ^ endDayOfWeek ^ endTime : 0); 917: } 918: 919: public synchronized boolean equals(Object o) 920: { 921: if (this == o) 922: return true; 923: if (! (o instanceof SimpleTimeZone)) 924: return false; 925: SimpleTimeZone zone = (SimpleTimeZone) o; 926: if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID()) 927: || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) 928: return false; 929: if (! useDaylight) 930: return true; 931: return (startYear == zone.startYear && startMonth == zone.startMonth 932: && startDay == zone.startDay 933: && startDayOfWeek == zone.startDayOfWeek 934: && startTime == zone.startTime 935: && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth 936: && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek 937: && endTime == zone.endTime && endTimeMode == zone.endTimeMode); 938: } 939: 940: /** 941: * Test if the other time zone uses the same rule and only 942: * possibly differs in ID. This implementation for this particular 943: * class will return true if the other object is a SimpleTimeZone, 944: * the raw offsets and useDaylight are identical and if useDaylight 945: * is true, also the start and end datas are identical. 946: * @return true if this zone uses the same rule. 947: */ 948: public boolean hasSameRules(TimeZone other) 949: { 950: if (this == other) 951: return true; 952: if (! (other instanceof SimpleTimeZone)) 953: return false; 954: SimpleTimeZone zone = (SimpleTimeZone) other; 955: if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset 956: || useDaylight != zone.useDaylight) 957: return false; 958: if (! useDaylight) 959: return true; 960: return (startYear == zone.startYear && startMonth == zone.startMonth 961: && startDay == zone.startDay 962: && startDayOfWeek == zone.startDayOfWeek 963: && startTime == zone.startTime 964: && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth 965: && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek 966: && endTime == zone.endTime && endTimeMode == zone.endTimeMode); 967: } 968: 969: /** 970: * Returns a string representation of this SimpleTimeZone object. 971: * @return a string representation of this SimpleTimeZone object. 972: */ 973: public String toString() 974: { 975: // the test for useDaylight is an incompatibility to jdk1.2, but 976: // I think this shouldn't hurt. 977: return getClass().getName() + "[" + "id=" + getID() + ",offset=" 978: + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight=" 979: + useDaylight 980: + (useDaylight 981: ? ",startYear=" + startYear + ",startMode=" + startMode 982: + ",startMonth=" + startMonth + ",startDay=" + startDay 983: + ",startDayOfWeek=" + startDayOfWeek + ",startTime=" 984: + startTime + ",startTimeMode=" + startTimeMode + ",endMode=" 985: + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay 986: + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime 987: + ",endTimeMode=" + endTimeMode : "") + "]"; 988: } 989: 990: /** 991: * Reads a serialized simple time zone from stream. 992: * @see #writeObject 993: */ 994: private void readObject(java.io.ObjectInputStream input) 995: throws java.io.IOException, ClassNotFoundException 996: { 997: input.defaultReadObject(); 998: if (serialVersionOnStream == 0) 999: { 1000: // initialize the new fields to default values. 1001: dstSavings = 60 * 60 * 1000; 1002: endMode = DOW_IN_MONTH_MODE; 1003: startMode = DOW_IN_MONTH_MODE; 1004: startTimeMode = WALL_TIME; 1005: endTimeMode = WALL_TIME; 1006: serialVersionOnStream = 2; 1007: } 1008: else 1009: { 1010: int length = input.readInt(); 1011: byte[] byteArray = new byte[length]; 1012: input.read(byteArray, 0, length); 1013: if (length >= 4) 1014: { 1015: // Lets hope that Sun does extensions to the serialized 1016: // form in a sane manner. 1017: startDay = byteArray[0]; 1018: startDayOfWeek = byteArray[1]; 1019: endDay = byteArray[2]; 1020: endDayOfWeek = byteArray[3]; 1021: } 1022: } 1023: } 1024: 1025: /** 1026: * Serializes this object to a stream. @serialdata The object is 1027: * first written in the old JDK 1.1 format, so that it can be read 1028: * by by the old classes. This means, that the 1029: * <code>start/endDay(OfWeek)</code>-Fields are written in the 1030: * DOW_IN_MONTH_MODE rule, since this was the only supported rule 1031: * in 1.1. 1032: * 1033: * In the optional section, we write first the length of an byte 1034: * array as int and afterwards the byte array itself. The byte 1035: * array contains in this release four elements, namely the real 1036: * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. 1037: * These fields are needed, because for compatibility reasons only 1038: * approximative values are written to the required section, as 1039: * described above. 1040: */ 1041: private void writeObject(java.io.ObjectOutputStream output) 1042: throws java.io.IOException 1043: { 1044: byte[] byteArray = new byte[] 1045: { 1046: (byte) startDay, (byte) startDayOfWeek, (byte) endDay, 1047: (byte) endDayOfWeek 1048: }; 1049: 1050: /* calculate the approximation for JDK 1.1 */ 1051: switch (startMode) 1052: { 1053: case DOM_MODE: 1054: startDayOfWeek = Calendar.SUNDAY; // random day of week 1055: 1056: // fall through 1057: case DOW_GE_DOM_MODE: 1058: case DOW_LE_DOM_MODE: 1059: startDay = (startDay + 6) / 7; 1060: } 1061: switch (endMode) 1062: { 1063: case DOM_MODE: 1064: endDayOfWeek = Calendar.SUNDAY; 1065: 1066: // fall through 1067: case DOW_GE_DOM_MODE: 1068: case DOW_LE_DOM_MODE: 1069: endDay = (endDay + 6) / 7; 1070: } 1071: 1072: // the required part: 1073: output.defaultWriteObject(); 1074: // the optional part: 1075: output.writeInt(byteArray.length); 1076: output.write(byteArray, 0, byteArray.length); 1077: } 1078: }
GNU Classpath (0.20) |