Frames | No Frames |
1: /* Calendar.java -- 2: Copyright (C) 1998, 1999, 2000, 2001, 2002, 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: import java.io.IOException; 42: import java.io.ObjectInputStream; 43: import java.io.ObjectOutputStream; 44: import java.io.Serializable; 45: import java.lang.reflect.Constructor; 46: import java.lang.reflect.InvocationTargetException; 47: 48: /** 49: * This class is an abstract base class for Calendars, which can be 50: * used to convert between <code>Date</code> objects and a set of 51: * integer fields which represent <code>YEAR</code>, 52: * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code> 53: * object represents a time in milliseconds since the Epoch. <br> 54: * 55: * This class is locale sensitive. To get the Object matching the 56: * current locale you can use <code>getInstance</code>. You can even provide 57: * a locale or a timezone. <code>getInstance</code> returns currently 58: * a <code>GregorianCalendar</code> for the current date. <br> 59: * 60: * If you want to convert a date from the Year, Month, Day, DayOfWeek, 61: * etc. Representation to a <code>Date</code>-Object, you can create 62: * a new Calendar with <code>getInstance()</code>, 63: * <code>clear()</code> all fields, <code>set(int,int)</code> the 64: * fields you need and convert it with <code>getTime()</code>. <br> 65: * 66: * If you want to convert a <code>Date</code>-object to the Calendar 67: * representation, create a new Calendar, assign the 68: * <code>Date</code>-Object with <code>setTime()</code>, and read the 69: * fields with <code>get(int)</code>. <br> 70: * 71: * When computing the date from time fields, it may happen, that there 72: * are either two few fields set, or some fields are inconsistent. This 73: * cases will handled in a calendar specific way. Missing fields are 74: * replaced by the fields of the epoch: 1970 January 1 00:00. <br> 75: * 76: * To understand, how the day of year is computed out of the fields 77: * look at the following table. It is traversed from top to bottom, 78: * and for the first line all fields are set, that line is used to 79: * compute the day. <br> 80: * 81: * 82: <pre>month + day_of_month 83: month + week_of_month + day_of_week 84: month + day_of_week_of_month + day_of_week 85: day_of_year 86: day_of_week + week_of_year</pre> 87: * 88: * The hour_of_day-field takes precedence over the ampm and 89: * hour_of_ampm fields. <br> 90: * 91: * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br> 92: * 93: * To convert a calendar to a human readable form and vice versa, use 94: * the <code>java.text.DateFormat</code> class. <br> 95: * 96: * Other useful things you can do with an calendar, is 97: * <code>roll</code>ing fields (that means increase/decrease a 98: * specific field by one, propagating overflows), or 99: * <code>add</code>ing/substracting a fixed amount to a field. 100: * 101: * @see Date 102: * @see GregorianCalendar 103: * @see TimeZone 104: * @see java.text.DateFormat 105: */ 106: public abstract class Calendar implements Serializable, Cloneable 107: { 108: /** 109: * Constant representing the era time field. 110: */ 111: public static final int ERA = 0; 112: 113: /** 114: * Constant representing the year time field. 115: */ 116: public static final int YEAR = 1; 117: 118: /** 119: * Constant representing the month time field. This field 120: * should contain one of the JANUARY,...,DECEMBER constants below. 121: */ 122: public static final int MONTH = 2; 123: 124: /** 125: * Constant representing the week of the year field. 126: * @see #setFirstDayOfWeek(int) 127: */ 128: public static final int WEEK_OF_YEAR = 3; 129: 130: /** 131: * Constant representing the week of the month time field. 132: * @see #setFirstDayOfWeek(int) 133: */ 134: public static final int WEEK_OF_MONTH = 4; 135: 136: /** 137: * Constant representing the day time field, synonym for DAY_OF_MONTH. 138: */ 139: public static final int DATE = 5; 140: 141: /** 142: * Constant representing the day time field. 143: */ 144: public static final int DAY_OF_MONTH = 5; 145: 146: /** 147: * Constant representing the day of year time field. This is 148: * 1 for the first day in month. 149: */ 150: public static final int DAY_OF_YEAR = 6; 151: 152: /** 153: * Constant representing the day of week time field. This field 154: * should contain one of the SUNDAY,...,SATURDAY constants below. 155: */ 156: public static final int DAY_OF_WEEK = 7; 157: 158: /** 159: * Constant representing the day-of-week-in-month field. For 160: * instance this field contains 2 for the second thursday in a 161: * month. If you give a negative number here, the day will count 162: * from the end of the month. 163: */ 164: public static final int DAY_OF_WEEK_IN_MONTH = 8; 165: 166: /** 167: * Constant representing the part of the day for 12-hour clock. This 168: * should be one of AM or PM. 169: */ 170: public static final int AM_PM = 9; 171: 172: /** 173: * Constant representing the hour time field for 12-hour clock. 174: */ 175: public static final int HOUR = 10; 176: 177: /** 178: * Constant representing the hour of day time field for 24-hour clock. 179: */ 180: public static final int HOUR_OF_DAY = 11; 181: 182: /** 183: * Constant representing the minute of hour time field. 184: */ 185: public static final int MINUTE = 12; 186: 187: /** 188: * Constant representing the second time field. 189: */ 190: public static final int SECOND = 13; 191: 192: /** 193: * Constant representing the millisecond time field. 194: */ 195: public static final int MILLISECOND = 14; 196: 197: /** 198: * Constant representing the time zone offset time field for the 199: * time given in the other fields. It is measured in 200: * milliseconds. The default is the offset of the time zone. 201: */ 202: public static final int ZONE_OFFSET = 15; 203: 204: /** 205: * Constant representing the daylight saving time offset in 206: * milliseconds. The default is the value given by the time zone. 207: */ 208: public static final int DST_OFFSET = 16; 209: 210: /** 211: * Number of time fields. 212: */ 213: public static final int FIELD_COUNT = 17; 214: 215: /** 216: * Constant representing Sunday. 217: */ 218: public static final int SUNDAY = 1; 219: 220: /** 221: * Constant representing Monday. 222: */ 223: public static final int MONDAY = 2; 224: 225: /** 226: * Constant representing Tuesday. 227: */ 228: public static final int TUESDAY = 3; 229: 230: /** 231: * Constant representing Wednesday. 232: */ 233: public static final int WEDNESDAY = 4; 234: 235: /** 236: * Constant representing Thursday. 237: */ 238: public static final int THURSDAY = 5; 239: 240: /** 241: * Constant representing Friday. 242: */ 243: public static final int FRIDAY = 6; 244: 245: /** 246: * Constant representing Saturday. 247: */ 248: public static final int SATURDAY = 7; 249: 250: /** 251: * Constant representing January. 252: */ 253: public static final int JANUARY = 0; 254: 255: /** 256: * Constant representing February. 257: */ 258: public static final int FEBRUARY = 1; 259: 260: /** 261: * Constant representing March. 262: */ 263: public static final int MARCH = 2; 264: 265: /** 266: * Constant representing April. 267: */ 268: public static final int APRIL = 3; 269: 270: /** 271: * Constant representing May. 272: */ 273: public static final int MAY = 4; 274: 275: /** 276: * Constant representing June. 277: */ 278: public static final int JUNE = 5; 279: 280: /** 281: * Constant representing July. 282: */ 283: public static final int JULY = 6; 284: 285: /** 286: * Constant representing August. 287: */ 288: public static final int AUGUST = 7; 289: 290: /** 291: * Constant representing September. 292: */ 293: public static final int SEPTEMBER = 8; 294: 295: /** 296: * Constant representing October. 297: */ 298: public static final int OCTOBER = 9; 299: 300: /** 301: * Constant representing November. 302: */ 303: public static final int NOVEMBER = 10; 304: 305: /** 306: * Constant representing December. 307: */ 308: public static final int DECEMBER = 11; 309: 310: /** 311: * Constant representing Undecimber. This is an artificial name useful 312: * for lunar calendars. 313: */ 314: public static final int UNDECIMBER = 12; 315: 316: /** 317: * Useful constant for 12-hour clock. 318: */ 319: public static final int AM = 0; 320: 321: /** 322: * Useful constant for 12-hour clock. 323: */ 324: public static final int PM = 1; 325: 326: /** 327: * The time fields. The array is indexed by the constants YEAR to 328: * DST_OFFSET. 329: * @serial 330: */ 331: protected int[] fields = new int[FIELD_COUNT]; 332: 333: /** 334: * The flags which tell if the fields above have a value. 335: * @serial 336: */ 337: protected boolean[] isSet = new boolean[FIELD_COUNT]; 338: 339: /** 340: * The time in milliseconds since the epoch. 341: * @serial 342: */ 343: protected long time; 344: 345: /** 346: * Tells if the above field has a valid value. 347: * @serial 348: */ 349: protected boolean isTimeSet; 350: 351: /** 352: * Tells if the fields have a valid value. This superseeds the isSet 353: * array. 354: * @serial 355: */ 356: protected boolean areFieldsSet; 357: 358: /** 359: * The time zone of this calendar. Used by sub classes to do UTC / local 360: * time conversion. Sub classes can access this field with getTimeZone(). 361: * @serial 362: */ 363: private TimeZone zone; 364: 365: /** 366: * Specifies if the date/time interpretation should be lenient. 367: * If the flag is set, a date such as "February 30, 1996" will be 368: * treated as the 29th day after the February 1. If this flag 369: * is false, such dates will cause an exception. 370: * @serial 371: */ 372: private boolean lenient; 373: 374: /** 375: * Sets what the first day of week is. This is used for 376: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 377: * @serial 378: */ 379: private int firstDayOfWeek; 380: 381: /** 382: * Sets how many days are required in the first week of the year. 383: * If the first day of the year should be the first week you should 384: * set this value to 1. If the first week must be a full week, set 385: * it to 7. 386: * @serial 387: */ 388: private int minimalDaysInFirstWeek; 389: 390: /** 391: * Is set to true if DST_OFFSET is explicitly set. In that case 392: * it's value overrides the value computed from the current 393: * time and the timezone. 394: */ 395: private boolean explicitDSTOffset = false; 396: 397: /** 398: * The version of the serialized data on the stream. 399: * <dl><dt>0 or not present</dt> 400: * <dd> JDK 1.1.5 or later.</dd> 401: * <dt>1</dt> 402: * <dd>JDK 1.1.6 or later. This always writes a correct `time' value 403: * on the stream, as well as the other fields, to be compatible with 404: * earlier versions</dd></dl> 405: * @since JDK1.1.6 406: * @serial 407: */ 408: private int serialVersionOnStream = 1; 409: 410: /** 411: * XXX - I have not checked the compatibility. The documentation of 412: * the serialized-form is quite hairy... 413: */ 414: static final long serialVersionUID = -1807547505821590642L; 415: 416: /** 417: * The name of the resource bundle. Used only by getBundle() 418: */ 419: private static final String bundleName = "gnu.java.locale.Calendar"; 420: 421: /** 422: * get resource bundle: 423: * The resources should be loaded via this method only. Iff an application 424: * uses this method, the resourcebundle is required. 425: */ 426: private static ResourceBundle getBundle(Locale locale) 427: { 428: return ResourceBundle.getBundle(bundleName, locale, 429: ClassLoader.getSystemClassLoader()); 430: } 431: 432: /** 433: * Constructs a new Calendar with the default time zone and the default 434: * locale. 435: */ 436: protected Calendar() 437: { 438: this(TimeZone.getDefault(), Locale.getDefault()); 439: } 440: 441: /** 442: * Constructs a new Calendar with the given time zone and the given 443: * locale. 444: * @param zone a time zone. 445: * @param locale a locale. 446: */ 447: protected Calendar(TimeZone zone, Locale locale) 448: { 449: this.zone = zone; 450: lenient = true; 451: 452: ResourceBundle rb = getBundle(locale); 453: 454: firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue(); 455: minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek")) 456: .intValue(); 457: clear(); 458: } 459: 460: /** 461: * Creates a calendar representing the actual time, using the default 462: * time zone and locale. 463: */ 464: public static synchronized Calendar getInstance() 465: { 466: return getInstance(TimeZone.getDefault(), Locale.getDefault()); 467: } 468: 469: /** 470: * Creates a calendar representing the actual time, using the given 471: * time zone and the default locale. 472: * @param zone a time zone. 473: */ 474: public static synchronized Calendar getInstance(TimeZone zone) 475: { 476: return getInstance(zone, Locale.getDefault()); 477: } 478: 479: /** 480: * Creates a calendar representing the actual time, using the default 481: * time zone and the given locale. 482: * @param locale a locale. 483: */ 484: public static synchronized Calendar getInstance(Locale locale) 485: { 486: return getInstance(TimeZone.getDefault(), locale); 487: } 488: 489: /** 490: * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle 491: * lookup for every getInstance call. 492: */ 493: private static HashMap cache = new HashMap(); 494: 495: /** Preset argument types for calendar-class constructor lookup. */ 496: private static Class[] ctorArgTypes = new Class[] 497: { 498: TimeZone.class, Locale.class 499: }; 500: 501: /** 502: * Creates a calendar representing the actual time, using the given 503: * time zone and locale. 504: * @param zone a time zone. 505: * @param locale a locale. 506: */ 507: public static synchronized Calendar getInstance(TimeZone zone, Locale locale) 508: { 509: Class calendarClass = (Class) cache.get(locale); 510: Throwable exception = null; 511: 512: try 513: { 514: if (calendarClass == null) 515: { 516: ResourceBundle rb = getBundle(locale); 517: String calendarClassName = rb.getString("calendarClass"); 518: 519: if (calendarClassName != null) 520: { 521: calendarClass = Class.forName(calendarClassName); 522: if (Calendar.class.isAssignableFrom(calendarClass)) 523: cache.put(locale, calendarClass); 524: } 525: } 526: 527: // GregorianCalendar is by far the most common case. Optimize by 528: // avoiding reflection. 529: if (calendarClass == GregorianCalendar.class) 530: return new GregorianCalendar(zone, locale); 531: 532: if (Calendar.class.isAssignableFrom(calendarClass)) 533: { 534: Constructor ctor = calendarClass.getConstructor(ctorArgTypes); 535: return (Calendar) ctor.newInstance(new Object[] { zone, locale }); 536: } 537: } 538: catch (ClassNotFoundException ex) 539: { 540: exception = ex; 541: } 542: catch (IllegalAccessException ex) 543: { 544: exception = ex; 545: } 546: catch (NoSuchMethodException ex) 547: { 548: exception = ex; 549: } 550: catch (InstantiationException ex) 551: { 552: exception = ex; 553: } 554: catch (InvocationTargetException ex) 555: { 556: exception = ex; 557: } 558: 559: throw new RuntimeException("Error instantiating calendar for locale " 560: + locale, exception); 561: } 562: 563: /** 564: * Gets the set of locales for which a Calendar is available. 565: * @exception MissingResourceException if locale data couldn't be found. 566: * @return the set of locales. 567: */ 568: public static synchronized Locale[] getAvailableLocales() 569: { 570: ResourceBundle rb = getBundle(new Locale("", "")); 571: return (Locale[]) rb.getObject("availableLocales"); 572: } 573: 574: /** 575: * Converts the time field values (<code>fields</code>) to 576: * milliseconds since the epoch UTC (<code>time</code>). Override 577: * this method if you write your own Calendar. */ 578: protected abstract void computeTime(); 579: 580: /** 581: * Converts the milliseconds since the epoch UTC 582: * (<code>time</code>) to time fields 583: * (<code>fields</code>). Override this method if you write your 584: * own Calendar. 585: */ 586: protected abstract void computeFields(); 587: 588: /** 589: * Converts the time represented by this object to a 590: * <code>Date</code>-Object. 591: * @return the Date. 592: */ 593: public final Date getTime() 594: { 595: if (! isTimeSet) 596: computeTime(); 597: return new Date(time); 598: } 599: 600: /** 601: * Sets this Calendar's time to the given Date. All time fields 602: * are invalidated by this method. 603: */ 604: public final void setTime(Date date) 605: { 606: setTimeInMillis(date.getTime()); 607: } 608: 609: /** 610: * Returns the time represented by this Calendar. 611: * @return the time in milliseconds since the epoch. 612: * @specnote This was made public in 1.4. 613: */ 614: public long getTimeInMillis() 615: { 616: if (! isTimeSet) 617: computeTime(); 618: return time; 619: } 620: 621: /** 622: * Sets this Calendar's time to the given Time. All time fields 623: * are invalidated by this method. 624: * @param time the time in milliseconds since the epoch 625: * @specnote This was made public in 1.4. 626: */ 627: public void setTimeInMillis(long time) 628: { 629: clear(); 630: this.time = time; 631: isTimeSet = true; 632: computeFields(); 633: } 634: 635: /** 636: * Gets the value of the specified field. They are recomputed 637: * if they are invalid. 638: * @param field the time field. One of the time field constants. 639: * @return the value of the specified field 640: * @throws ArrayIndexOutOfBoundsException if the field is outside 641: * the valid range. The value of field must be >= 0 and 642: * <= <code>FIELD_COUNT</code>. 643: * @specnote Not final since JDK 1.4 644: */ 645: public int get(int field) 646: { 647: // If the requested field is invalid, force all fields to be recomputed. 648: if (! isSet[field]) 649: areFieldsSet = false; 650: complete(); 651: return fields[field]; 652: } 653: 654: /** 655: * Gets the value of the specified field. This method doesn't 656: * recompute the fields, if they are invalid. 657: * @param field the time field. One of the time field constants. 658: * @return the value of the specified field, undefined if 659: * <code>areFieldsSet</code> or <code>isSet[field]</code> is false. 660: * @throws ArrayIndexOutOfBoundsException if the field is outside 661: * the valid range. The value of field must be >= 0 and 662: * <= <code>FIELD_COUNT</code>. 663: */ 664: protected final int internalGet(int field) 665: { 666: return fields[field]; 667: } 668: 669: /** 670: * Sets the time field with the given value. This does invalidate 671: * the time in milliseconds. 672: * @param field the time field. One of the time field constants 673: * @param value the value to be set. 674: * @throws ArrayIndexOutOfBoundsException if field is outside 675: * the valid range. The value of field must be >= 0 and 676: * <= <code>FIELD_COUNT</code>. 677: * @specnote Not final since JDK 1.4 678: */ 679: public void set(int field, int value) 680: { 681: if (isTimeSet) 682: for (int i = 0; i < FIELD_COUNT; i++) 683: isSet[i] = false; 684: isTimeSet = false; 685: fields[field] = value; 686: isSet[field] = true; 687: 688: // The five valid date patterns, in order of priority 689: // 1 YEAR + MONTH + DAY_OF_MONTH 690: // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK 691: // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK 692: // 4 YEAR + DAY_OF_YEAR 693: // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR 694: switch (field) 695: { 696: case MONTH: // pattern 1,2 or 3 697: isSet[DAY_OF_YEAR] = false; 698: isSet[WEEK_OF_YEAR] = false; 699: break; 700: case DAY_OF_MONTH: // pattern 1 701: isSet[YEAR] = true; 702: isSet[MONTH] = true; 703: isSet[WEEK_OF_MONTH] = true; 704: isSet[DAY_OF_WEEK] = false; 705: isSet[DAY_OF_WEEK_IN_MONTH] = false; 706: isSet[DAY_OF_YEAR] = false; 707: isSet[WEEK_OF_YEAR] = false; 708: break; 709: case WEEK_OF_MONTH: // pattern 2 710: if (! isSet[DAY_OF_WEEK]) 711: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 712: isSet[YEAR] = true; 713: isSet[MONTH] = true; 714: isSet[DAY_OF_WEEK] = true; 715: isSet[DAY_OF_MONTH] = false; 716: isSet[DAY_OF_WEEK_IN_MONTH] = false; 717: isSet[DAY_OF_YEAR] = false; 718: isSet[WEEK_OF_YEAR] = false; 719: break; 720: case DAY_OF_WEEK_IN_MONTH: // pattern 3 721: if (! isSet[DAY_OF_WEEK]) 722: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 723: isSet[YEAR] = true; 724: isSet[MONTH] = true; 725: isSet[DAY_OF_WEEK] = true; 726: isSet[DAY_OF_YEAR] = false; 727: isSet[DAY_OF_MONTH] = false; 728: isSet[WEEK_OF_MONTH] = false; 729: isSet[WEEK_OF_YEAR] = false; 730: break; 731: case DAY_OF_YEAR: // pattern 4 732: isSet[YEAR] = true; 733: isSet[MONTH] = false; 734: isSet[WEEK_OF_MONTH] = false; 735: isSet[DAY_OF_MONTH] = false; 736: isSet[DAY_OF_WEEK] = false; 737: isSet[WEEK_OF_YEAR] = false; 738: isSet[DAY_OF_WEEK_IN_MONTH] = false; 739: break; 740: case WEEK_OF_YEAR: // pattern 5 741: if (! isSet[DAY_OF_WEEK]) 742: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 743: isSet[YEAR] = true; 744: isSet[DAY_OF_WEEK] = true; 745: isSet[MONTH] = false; 746: isSet[DAY_OF_MONTH] = false; 747: isSet[WEEK_OF_MONTH] = false; 748: isSet[DAY_OF_YEAR] = false; 749: isSet[DAY_OF_WEEK_IN_MONTH] = false; 750: break; 751: case AM_PM: 752: isSet[HOUR] = true; 753: isSet[HOUR_OF_DAY] = false; 754: break; 755: case HOUR_OF_DAY: 756: isSet[AM_PM] = false; 757: isSet[HOUR] = false; 758: break; 759: case HOUR: 760: isSet[AM_PM] = true; 761: isSet[HOUR_OF_DAY] = false; 762: break; 763: case DST_OFFSET: 764: explicitDSTOffset = true; 765: } 766: 767: // May have crossed over a DST boundary. 768: if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET)) 769: isSet[DST_OFFSET] = false; 770: } 771: 772: /** 773: * Sets the fields for year, month, and date 774: * @param year the year. 775: * @param month the month, one of the constants JANUARY..UNDICEMBER. 776: * @param date the day of the month 777: */ 778: public final void set(int year, int month, int date) 779: { 780: isTimeSet = false; 781: fields[YEAR] = year; 782: fields[MONTH] = month; 783: fields[DATE] = date; 784: isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; 785: isSet[WEEK_OF_YEAR] = false; 786: isSet[DAY_OF_YEAR] = false; 787: isSet[WEEK_OF_MONTH] = false; 788: isSet[DAY_OF_WEEK] = false; 789: isSet[DAY_OF_WEEK_IN_MONTH] = false; 790: isSet[ERA] = false; 791: 792: if (! explicitDSTOffset) 793: isSet[DST_OFFSET] = false; // May have crossed a DST boundary. 794: } 795: 796: /** 797: * Sets the fields for year, month, date, hour, and minute 798: * @param year the year. 799: * @param month the month, one of the constants JANUARY..UNDICEMBER. 800: * @param date the day of the month 801: * @param hour the hour of day. 802: * @param minute the minute. 803: */ 804: public final void set(int year, int month, int date, int hour, int minute) 805: { 806: set(year, month, date); 807: fields[HOUR_OF_DAY] = hour; 808: fields[MINUTE] = minute; 809: isSet[HOUR_OF_DAY] = isSet[MINUTE] = true; 810: isSet[AM_PM] = false; 811: isSet[HOUR] = false; 812: } 813: 814: /** 815: * Sets the fields for year, month, date, hour, and minute 816: * @param year the year. 817: * @param month the month, one of the constants JANUARY..UNDICEMBER. 818: * @param date the day of the month 819: * @param hour the hour of day. 820: * @param minute the minute. 821: * @param second the second. 822: */ 823: public final void set(int year, int month, int date, int hour, int minute, 824: int second) 825: { 826: set(year, month, date, hour, minute); 827: fields[SECOND] = second; 828: isSet[SECOND] = true; 829: } 830: 831: /** 832: * Clears the values of all the time fields. 833: */ 834: public final void clear() 835: { 836: isTimeSet = false; 837: areFieldsSet = false; 838: int zoneOffs = zone.getRawOffset(); 839: int[] tempFields = 840: { 841: 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, 842: 0, 0, zoneOffs, 0 843: }; 844: fields = tempFields; 845: for (int i = 0; i < FIELD_COUNT; i++) 846: isSet[i] = false; 847: } 848: 849: /** 850: * Clears the values of the specified time field. 851: * @param field the time field. One of the time field constants. 852: * @throws ArrayIndexOutOfBoundsException if field is outside 853: * the valid range. The value of field must be >= 0 and 854: * <= <code>FIELD_COUNT</code>. 855: */ 856: public final void clear(int field) 857: { 858: int[] tempFields = 859: { 860: 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, 861: 0, 0, zone.getRawOffset(), 0 862: }; 863: isTimeSet = false; 864: areFieldsSet = false; 865: isSet[field] = false; 866: fields[field] = tempFields[field]; 867: } 868: 869: /** 870: * Determines if the specified field has a valid value. 871: * @return true if the specified field has a value. 872: * @throws ArrayIndexOutOfBoundsException if the field is outside 873: * the valid range. The value of field must be >= 0 and 874: * <= <code>FIELD_COUNT</code>. 875: */ 876: public final boolean isSet(int field) 877: { 878: return isSet[field]; 879: } 880: 881: /** 882: * Fills any unset fields in the time field list 883: */ 884: protected void complete() 885: { 886: if (! isTimeSet) 887: computeTime(); 888: if (! areFieldsSet) 889: computeFields(); 890: } 891: 892: /** 893: * Compares the given calendar with this. 894: * @param o the object to that we should compare. 895: * @return true, if the given object is a calendar, that represents 896: * the same time (but doesn't necessary have the same fields). 897: */ 898: public boolean equals(Object o) 899: { 900: if (! (o instanceof Calendar)) 901: return false; 902: Calendar cal = (Calendar) o; 903: if (getTimeInMillis() == ((Calendar) o).getTimeInMillis() 904: && cal.getFirstDayOfWeek() == getFirstDayOfWeek() 905: && cal.isLenient() == isLenient() 906: && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek()) 907: { 908: TimeZone self = getTimeZone(); 909: TimeZone oth = cal.getTimeZone(); 910: return self == null ? oth == null : self.equals(oth); 911: } 912: return false; 913: } 914: 915: /** 916: * Returns a hash code for this calendar. 917: * @return a hash code, which fullfits the general contract of 918: * <code>hashCode()</code> 919: */ 920: public int hashCode() 921: { 922: long time = getTimeInMillis(); 923: int val = (int) ((time & 0xffffffffL) ^ (time >> 32)); 924: val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237) 925: + getMinimalDaysInFirstWeek()); 926: TimeZone self = getTimeZone(); 927: if (self != null) 928: val ^= self.hashCode(); 929: return val; 930: } 931: 932: /** 933: * Compares the given calendar with this. 934: * @param o the object to that we should compare. 935: * @return true, if the given object is a calendar, and this calendar 936: * represents a smaller time than the calendar o. 937: * @exception ClassCastException if o is not an calendar. 938: * @since JDK1.2 you don't need to override this method 939: */ 940: public boolean before(Object o) 941: { 942: return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); 943: } 944: 945: /** 946: * Compares the given calendar with this. 947: * @param o the object to that we should compare. 948: * @return true, if the given object is a calendar, and this calendar 949: * represents a bigger time than the calendar o. 950: * @exception ClassCastException if o is not an calendar. 951: * @since JDK1.2 you don't need to override this method 952: */ 953: public boolean after(Object o) 954: { 955: return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); 956: } 957: 958: /** 959: * Adds the specified amount of time to the given time field. The 960: * amount may be negative to subtract the time. If the field overflows 961: * it does what you expect: Jan, 25 + 10 Days is Feb, 4. 962: * @param field the time field. One of the time field constants. 963: * @param amount the amount of time. 964: * @throws ArrayIndexOutOfBoundsException if the field is outside 965: * the valid range. The value of field must be >= 0 and 966: * <= <code>FIELD_COUNT</code>. 967: */ 968: public abstract void add(int field, int amount); 969: 970: /** 971: * Rolls the specified time field up or down. This means add one 972: * to the specified field, but don't change the other fields. If 973: * the maximum for this field is reached, start over with the 974: * minimum value. <br> 975: * 976: * <strong>Note:</strong> There may be situation, where the other 977: * fields must be changed, e.g rolling the month on May, 31. 978: * The date June, 31 is automatically converted to July, 1. 979: * @param field the time field. One of the time field constants. 980: * @param up the direction, true for up, false for down. 981: * @throws ArrayIndexOutOfBoundsException if the field is outside 982: * the valid range. The value of field must be >= 0 and 983: * <= <code>FIELD_COUNT</code>. 984: */ 985: public abstract void roll(int field, boolean up); 986: 987: /** 988: * Rolls up or down the specified time field by the given amount. 989: * A negative amount rolls down. The default implementation is 990: * call <code>roll(int, boolean)</code> for the specified amount. 991: * 992: * Subclasses should override this method to do more intuitiv things. 993: * 994: * @param field the time field. One of the time field constants. 995: * @param amount the amount to roll by, positive for rolling up, 996: * negative for rolling down. 997: * @throws ArrayIndexOutOfBoundsException if the field is outside 998: * the valid range. The value of field must be >= 0 and 999: * <= <code>FIELD_COUNT</code>. 1000: * @since JDK1.2 1001: */ 1002: public void roll(int field, int amount) 1003: { 1004: while (amount > 0) 1005: { 1006: roll(field, true); 1007: amount--; 1008: } 1009: while (amount < 0) 1010: { 1011: roll(field, false); 1012: amount++; 1013: } 1014: } 1015: 1016: /** 1017: * Sets the time zone to the specified value. 1018: * @param zone the new time zone 1019: */ 1020: public void setTimeZone(TimeZone zone) 1021: { 1022: this.zone = zone; 1023: } 1024: 1025: /** 1026: * Gets the time zone of this calendar 1027: * @return the current time zone. 1028: */ 1029: public TimeZone getTimeZone() 1030: { 1031: return zone; 1032: } 1033: 1034: /** 1035: * Specifies if the date/time interpretation should be lenient. 1036: * If the flag is set, a date such as "February 30, 1996" will be 1037: * treated as the 29th day after the February 1. If this flag 1038: * is false, such dates will cause an exception. 1039: * @param lenient true, if the date should be interpreted linient, 1040: * false if it should be interpreted strict. 1041: */ 1042: public void setLenient(boolean lenient) 1043: { 1044: this.lenient = lenient; 1045: } 1046: 1047: /** 1048: * Tells if the date/time interpretation is lenient. 1049: * @return true, if the date should be interpreted linient, 1050: * false if it should be interpreted strict. 1051: */ 1052: public boolean isLenient() 1053: { 1054: return lenient; 1055: } 1056: 1057: /** 1058: * Sets what the first day of week is. This is used for 1059: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 1060: * @param value the first day of week. One of SUNDAY to SATURDAY. 1061: */ 1062: public void setFirstDayOfWeek(int value) 1063: { 1064: firstDayOfWeek = value; 1065: } 1066: 1067: /** 1068: * Gets what the first day of week is. This is used for 1069: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 1070: * @return the first day of week. One of SUNDAY to SATURDAY. 1071: */ 1072: public int getFirstDayOfWeek() 1073: { 1074: return firstDayOfWeek; 1075: } 1076: 1077: /** 1078: * Sets how many days are required in the first week of the year. 1079: * If the first day of the year should be the first week you should 1080: * set this value to 1. If the first week must be a full week, set 1081: * it to 7. 1082: * @param value the minimal days required in the first week. 1083: */ 1084: public void setMinimalDaysInFirstWeek(int value) 1085: { 1086: minimalDaysInFirstWeek = value; 1087: } 1088: 1089: /** 1090: * Gets how many days are required in the first week of the year. 1091: * @return the minimal days required in the first week. 1092: * @see #setMinimalDaysInFirstWeek 1093: */ 1094: public int getMinimalDaysInFirstWeek() 1095: { 1096: return minimalDaysInFirstWeek; 1097: } 1098: 1099: /** 1100: * Gets the smallest value that is allowed for the specified field. 1101: * @param field the time field. One of the time field constants. 1102: * @return the smallest value. 1103: */ 1104: public abstract int getMinimum(int field); 1105: 1106: /** 1107: * Gets the biggest value that is allowed for the specified field. 1108: * @param field the time field. One of the time field constants. 1109: * @return the biggest value. 1110: */ 1111: public abstract int getMaximum(int field); 1112: 1113: /** 1114: * Gets the greatest minimum value that is allowed for the specified field. 1115: * @param field the time field. One of the time field constants. 1116: * @return the greatest minimum value. 1117: */ 1118: public abstract int getGreatestMinimum(int field); 1119: 1120: /** 1121: * Gets the smallest maximum value that is allowed for the 1122: * specified field. For example this is 28 for DAY_OF_MONTH. 1123: * @param field the time field. One of the time field constants. 1124: * @return the least maximum value. 1125: */ 1126: public abstract int getLeastMaximum(int field); 1127: 1128: /** 1129: * Gets the actual minimum value that is allowed for the specified field. 1130: * This value is dependent on the values of the other fields. 1131: * @param field the time field. One of the time field constants. 1132: * @return the actual minimum value. 1133: * @throws ArrayIndexOutOfBoundsException if the field is outside 1134: * the valid range. The value of field must be >= 0 and 1135: * <= <code>FIELD_COUNT</code>. 1136: * @since jdk1.2 1137: */ 1138: public int getActualMinimum(int field) 1139: { 1140: Calendar tmp = (Calendar) clone(); // To avoid restoring state 1141: int min = tmp.getGreatestMinimum(field); 1142: int end = tmp.getMinimum(field); 1143: tmp.set(field, min); 1144: for (; min > end; min--) 1145: { 1146: tmp.add(field, -1); // Try to get smaller 1147: if (tmp.get(field) != min - 1) 1148: break; // Done if not successful 1149: } 1150: return min; 1151: } 1152: 1153: /** 1154: * Gets the actual maximum value that is allowed for the specified field. 1155: * This value is dependent on the values of the other fields. 1156: * @param field the time field. One of the time field constants. 1157: * @return the actual maximum value. 1158: * @throws ArrayIndexOutOfBoundsException if the field is outside 1159: * the valid range. The value of field must be >= 0 and 1160: * <= <code>FIELD_COUNT</code>. 1161: * @since jdk1.2 1162: */ 1163: public int getActualMaximum(int field) 1164: { 1165: Calendar tmp = (Calendar) clone(); // To avoid restoring state 1166: int max = tmp.getLeastMaximum(field); 1167: int end = tmp.getMaximum(field); 1168: tmp.set(field, max); 1169: for (; max < end; max++) 1170: { 1171: tmp.add(field, 1); 1172: if (tmp.get(field) != max + 1) 1173: break; 1174: } 1175: return max; 1176: } 1177: 1178: /** 1179: * Return a clone of this object. 1180: */ 1181: public Object clone() 1182: { 1183: try 1184: { 1185: Calendar cal = (Calendar) super.clone(); 1186: cal.fields = (int[]) fields.clone(); 1187: cal.isSet = (boolean[]) isSet.clone(); 1188: return cal; 1189: } 1190: catch (CloneNotSupportedException ex) 1191: { 1192: return null; 1193: } 1194: } 1195: 1196: private static final String[] fieldNames = 1197: { 1198: ",ERA=", ",YEAR=", ",MONTH=", 1199: ",WEEK_OF_YEAR=", 1200: ",WEEK_OF_MONTH=", 1201: ",DAY_OF_MONTH=", 1202: ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", 1203: ",DAY_OF_WEEK_IN_MONTH=", 1204: ",AM_PM=", ",HOUR=", 1205: ",HOUR_OF_DAY=", ",MINUTE=", 1206: ",SECOND=", ",MILLISECOND=", 1207: ",ZONE_OFFSET=", ",DST_OFFSET=" 1208: }; 1209: 1210: /** 1211: * Returns a string representation of this object. It is mainly 1212: * for debugging purposes and its content is implementation 1213: * specific. 1214: */ 1215: public String toString() 1216: { 1217: StringBuffer sb = new StringBuffer(); 1218: sb.append(getClass().getName()).append('['); 1219: sb.append("time="); 1220: if (isTimeSet) 1221: sb.append(time); 1222: else 1223: sb.append("?"); 1224: sb.append(",zone=" + zone); 1225: sb.append(",areFieldsSet=" + areFieldsSet); 1226: for (int i = 0; i < FIELD_COUNT; i++) 1227: { 1228: sb.append(fieldNames[i]); 1229: if (isSet[i]) 1230: sb.append(fields[i]); 1231: else 1232: sb.append("?"); 1233: } 1234: sb.append(",lenient=").append(lenient); 1235: sb.append(",firstDayOfWeek=").append(firstDayOfWeek); 1236: sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); 1237: sb.append("]"); 1238: return sb.toString(); 1239: } 1240: 1241: /** 1242: * Saves the state of the object to the stream. Ideally we would 1243: * only write the time field, but we need to be compatible with 1244: * earlier versions. <br> 1245: * 1246: * This doesn't write the JDK1.1 field nextStamp to the stream, as 1247: * I don't know what it is good for, and because the documentation 1248: * says, that it could be omitted. */ 1249: private void writeObject(ObjectOutputStream stream) throws IOException 1250: { 1251: if (! isTimeSet) 1252: computeTime(); 1253: stream.defaultWriteObject(); 1254: } 1255: 1256: /** 1257: * Reads the object back from stream (deserialization). 1258: */ 1259: private void readObject(ObjectInputStream stream) 1260: throws IOException, ClassNotFoundException 1261: { 1262: stream.defaultReadObject(); 1263: if (! isTimeSet) 1264: computeTime(); 1265: 1266: if (serialVersionOnStream > 1) 1267: { 1268: // This is my interpretation of the serial number: 1269: // Sun wants to remove all fields from the stream someday 1270: // and will then increase the serialVersion number again. 1271: // We prepare to be compatible. 1272: fields = new int[FIELD_COUNT]; 1273: isSet = new boolean[FIELD_COUNT]; 1274: areFieldsSet = false; 1275: } 1276: } 1277: }