GNU Classpath (0.17) | ||
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: * This is the default calendar class, that is returned on 367: * java.util.Calendar.getInstance(). 368: * XXX - this isn't localized anywhere, is it? 369: * @see java.util.Calendar#getInstance() 370: */ 371: private static final String calendarClassName = "java.util.GregorianCalendar"; 372: 373: /** 374: * Specifies if the date/time interpretation should be lenient. 375: * If the flag is set, a date such as "February 30, 1996" will be 376: * treated as the 29th day after the February 1. If this flag 377: * is false, such dates will cause an exception. 378: * @serial 379: */ 380: private boolean lenient; 381: 382: /** 383: * Sets what the first day of week is. This is used for 384: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 385: * @serial 386: */ 387: private int firstDayOfWeek; 388: 389: /** 390: * Sets how many days are required in the first week of the year. 391: * If the first day of the year should be the first week you should 392: * set this value to 1. If the first week must be a full week, set 393: * it to 7. 394: * @serial 395: */ 396: private int minimalDaysInFirstWeek; 397: 398: /** 399: * Is set to true if DST_OFFSET is explicitly set. In that case 400: * it's value overrides the value computed from the current 401: * time and the timezone. 402: */ 403: private boolean explicitDSTOffset = false; 404: 405: /** 406: * The version of the serialized data on the stream. 407: * <dl><dt>0 or not present</dt> 408: * <dd> JDK 1.1.5 or later.</dd> 409: * <dt>1</dt> 410: * <dd>JDK 1.1.6 or later. This always writes a correct `time' value 411: * on the stream, as well as the other fields, to be compatible with 412: * earlier versions</dd></dl> 413: * @since JDK1.1.6 414: * @serial 415: */ 416: private int serialVersionOnStream = 1; 417: 418: /** 419: * XXX - I have not checked the compatibility. The documentation of 420: * the serialized-form is quite hairy... 421: */ 422: static final long serialVersionUID = -1807547505821590642L; 423: 424: /** 425: * The name of the resource bundle. Used only by getBundle() 426: */ 427: private static final String bundleName = "gnu.java.locale.LocaleInformation"; 428: 429: /** 430: * get resource bundle: 431: * The resources should be loaded via this method only. Iff an application 432: * uses this method, the resourcebundle is required. 433: */ 434: private static ResourceBundle getBundle(Locale locale) 435: { 436: return ResourceBundle.getBundle(bundleName, locale, 437: ClassLoader.getSystemClassLoader()); 438: } 439: 440: /** 441: * Constructs a new Calendar with the default time zone and the default 442: * locale. 443: */ 444: protected Calendar() 445: { 446: this(TimeZone.getDefault(), Locale.getDefault()); 447: } 448: 449: /** 450: * Constructs a new Calendar with the given time zone and the given 451: * locale. 452: * @param zone a time zone. 453: * @param locale a locale. 454: */ 455: protected Calendar(TimeZone zone, Locale locale) 456: { 457: this.zone = zone; 458: lenient = true; 459: String[] days = { "", "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; 460: 461: ResourceBundle rb = getBundle(locale); 462: String min = (String) rb.getObject("minNumberOfDaysInFirstWeek"); 463: String first = (String) rb.getObject("firstDayOfWeek"); 464: try 465: { 466: if (min != null) 467: minimalDaysInFirstWeek = Integer.parseInt(min); 468: } 469: catch (NumberFormatException ex) 470: { 471: minimalDaysInFirstWeek = 1; 472: } 473: 474: firstDayOfWeek = 1; 475: if (first != null) 476: for (int i = 0; i < 8; i++) 477: if (days[i].equals(first)) 478: firstDayOfWeek = i; 479: 480: clear(); 481: } 482: 483: /** 484: * Creates a calendar representing the actual time, using the default 485: * time zone and locale. 486: */ 487: public static synchronized Calendar getInstance() 488: { 489: return getInstance(TimeZone.getDefault(), Locale.getDefault()); 490: } 491: 492: /** 493: * Creates a calendar representing the actual time, using the given 494: * time zone and the default locale. 495: * @param zone a time zone. 496: */ 497: public static synchronized Calendar getInstance(TimeZone zone) 498: { 499: return getInstance(zone, Locale.getDefault()); 500: } 501: 502: /** 503: * Creates a calendar representing the actual time, using the default 504: * time zone and the given locale. 505: * @param locale a locale. 506: */ 507: public static synchronized Calendar getInstance(Locale locale) 508: { 509: return getInstance(TimeZone.getDefault(), locale); 510: } 511: 512: /** 513: * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle 514: * lookup for every getInstance call. 515: */ 516: private static HashMap cache = new HashMap(); 517: 518: /** Preset argument types for calendar-class constructor lookup. */ 519: private static Class[] ctorArgTypes = new Class[] 520: { 521: TimeZone.class, Locale.class 522: }; 523: 524: /** 525: * Creates a calendar representing the actual time, using the given 526: * time zone and locale. 527: * @param zone a time zone. 528: * @param locale a locale. 529: */ 530: public static synchronized Calendar getInstance(TimeZone zone, Locale locale) 531: { 532: Class calendarClass = (Class) cache.get(locale); 533: Throwable exception = null; 534: 535: try 536: { 537: if (calendarClass == null) 538: { 539: calendarClass = Class.forName(calendarClassName); 540: if (Calendar.class.isAssignableFrom(calendarClass)) 541: cache.put(locale, calendarClass); 542: } 543: 544: // GregorianCalendar is by far the most common case. Optimize by 545: // avoiding reflection. 546: if (calendarClass == GregorianCalendar.class) 547: return new GregorianCalendar(zone, locale); 548: 549: if (Calendar.class.isAssignableFrom(calendarClass)) 550: { 551: Constructor ctor = calendarClass.getConstructor(ctorArgTypes); 552: return (Calendar) ctor.newInstance(new Object[] { zone, locale }); 553: } 554: } 555: catch (ClassNotFoundException ex) 556: { 557: exception = ex; 558: } 559: catch (IllegalAccessException ex) 560: { 561: exception = ex; 562: } 563: catch (NoSuchMethodException ex) 564: { 565: exception = ex; 566: } 567: catch (InstantiationException ex) 568: { 569: exception = ex; 570: } 571: catch (InvocationTargetException ex) 572: { 573: exception = ex; 574: } 575: 576: throw new RuntimeException("Error instantiating calendar for locale " 577: + locale, exception); 578: } 579: 580: /** 581: * Gets the set of locales for which a Calendar is available. 582: * @exception MissingResourceException if locale data couldn't be found. 583: * @return the set of locales. 584: */ 585: public static synchronized Locale[] getAvailableLocales() 586: { 587: ResourceBundle rb = getBundle(new Locale("", "")); 588: return (Locale[]) rb.getObject("availableLocales"); 589: } 590: 591: /** 592: * Converts the time field values (<code>fields</code>) to 593: * milliseconds since the epoch UTC (<code>time</code>). Override 594: * this method if you write your own Calendar. */ 595: protected abstract void computeTime(); 596: 597: /** 598: * Converts the milliseconds since the epoch UTC 599: * (<code>time</code>) to time fields 600: * (<code>fields</code>). Override this method if you write your 601: * own Calendar. 602: */ 603: protected abstract void computeFields(); 604: 605: /** 606: * Converts the time represented by this object to a 607: * <code>Date</code>-Object. 608: * @return the Date. 609: */ 610: public final Date getTime() 611: { 612: if (! isTimeSet) 613: computeTime(); 614: return new Date(time); 615: } 616: 617: /** 618: * Sets this Calendar's time to the given Date. All time fields 619: * are invalidated by this method. 620: */ 621: public final void setTime(Date date) 622: { 623: setTimeInMillis(date.getTime()); 624: } 625: 626: /** 627: * Returns the time represented by this Calendar. 628: * @return the time in milliseconds since the epoch. 629: * @specnote This was made public in 1.4. 630: */ 631: public long getTimeInMillis() 632: { 633: if (! isTimeSet) 634: computeTime(); 635: return time; 636: } 637: 638: /** 639: * Sets this Calendar's time to the given Time. All time fields 640: * are invalidated by this method. 641: * @param time the time in milliseconds since the epoch 642: * @specnote This was made public in 1.4. 643: */ 644: public void setTimeInMillis(long time) 645: { 646: clear(); 647: this.time = time; 648: isTimeSet = true; 649: computeFields(); 650: } 651: 652: /** 653: * Gets the value of the specified field. They are recomputed 654: * if they are invalid. 655: * @param field the time field. One of the time field constants. 656: * @return the value of the specified field 657: * @throws ArrayIndexOutOfBoundsException if the field is outside 658: * the valid range. The value of field must be >= 0 and 659: * <= <code>FIELD_COUNT</code>. 660: * @specnote Not final since JDK 1.4 661: */ 662: public int get(int field) 663: { 664: // If the requested field is invalid, force all fields to be recomputed. 665: if (! isSet[field]) 666: areFieldsSet = false; 667: complete(); 668: return fields[field]; 669: } 670: 671: /** 672: * Gets the value of the specified field. This method doesn't 673: * recompute the fields, if they are invalid. 674: * @param field the time field. One of the time field constants. 675: * @return the value of the specified field, undefined if 676: * <code>areFieldsSet</code> or <code>isSet[field]</code> is false. 677: * @throws ArrayIndexOutOfBoundsException if the field is outside 678: * the valid range. The value of field must be >= 0 and 679: * <= <code>FIELD_COUNT</code>. 680: */ 681: protected final int internalGet(int field) 682: { 683: return fields[field]; 684: } 685: 686: /** 687: * Sets the time field with the given value. This does invalidate 688: * the time in milliseconds. 689: * @param field the time field. One of the time field constants 690: * @param value the value to be set. 691: * @throws ArrayIndexOutOfBoundsException if field is outside 692: * the valid range. The value of field must be >= 0 and 693: * <= <code>FIELD_COUNT</code>. 694: * @specnote Not final since JDK 1.4 695: */ 696: public void set(int field, int value) 697: { 698: if (isTimeSet) 699: for (int i = 0; i < FIELD_COUNT; i++) 700: isSet[i] = false; 701: isTimeSet = false; 702: fields[field] = value; 703: isSet[field] = true; 704: 705: // The five valid date patterns, in order of priority 706: // 1 YEAR + MONTH + DAY_OF_MONTH 707: // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK 708: // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK 709: // 4 YEAR + DAY_OF_YEAR 710: // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR 711: switch (field) 712: { 713: case MONTH: // pattern 1,2 or 3 714: isSet[DAY_OF_YEAR] = false; 715: isSet[WEEK_OF_YEAR] = false; 716: break; 717: case DAY_OF_MONTH: // pattern 1 718: isSet[YEAR] = true; 719: isSet[MONTH] = true; 720: isSet[WEEK_OF_MONTH] = true; 721: isSet[DAY_OF_WEEK] = false; 722: isSet[DAY_OF_WEEK_IN_MONTH] = false; 723: isSet[DAY_OF_YEAR] = false; 724: isSet[WEEK_OF_YEAR] = false; 725: break; 726: case WEEK_OF_MONTH: // pattern 2 727: if (! isSet[DAY_OF_WEEK]) 728: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 729: isSet[YEAR] = true; 730: isSet[MONTH] = true; 731: isSet[DAY_OF_WEEK] = true; 732: isSet[DAY_OF_MONTH] = false; 733: isSet[DAY_OF_WEEK_IN_MONTH] = false; 734: isSet[DAY_OF_YEAR] = false; 735: isSet[WEEK_OF_YEAR] = false; 736: break; 737: case DAY_OF_WEEK_IN_MONTH: // pattern 3 738: if (! isSet[DAY_OF_WEEK]) 739: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 740: isSet[YEAR] = true; 741: isSet[MONTH] = true; 742: isSet[DAY_OF_WEEK] = true; 743: isSet[DAY_OF_YEAR] = false; 744: isSet[DAY_OF_MONTH] = false; 745: isSet[WEEK_OF_MONTH] = false; 746: isSet[WEEK_OF_YEAR] = false; 747: break; 748: case DAY_OF_YEAR: // pattern 4 749: isSet[YEAR] = true; 750: isSet[MONTH] = false; 751: isSet[WEEK_OF_MONTH] = false; 752: isSet[DAY_OF_MONTH] = false; 753: isSet[DAY_OF_WEEK] = false; 754: isSet[WEEK_OF_YEAR] = false; 755: isSet[DAY_OF_WEEK_IN_MONTH] = false; 756: break; 757: case WEEK_OF_YEAR: // pattern 5 758: if (! isSet[DAY_OF_WEEK]) 759: fields[DAY_OF_WEEK] = getFirstDayOfWeek(); 760: isSet[YEAR] = true; 761: isSet[DAY_OF_WEEK] = true; 762: isSet[MONTH] = false; 763: isSet[DAY_OF_MONTH] = false; 764: isSet[WEEK_OF_MONTH] = false; 765: isSet[DAY_OF_YEAR] = false; 766: isSet[DAY_OF_WEEK_IN_MONTH] = false; 767: break; 768: case AM_PM: 769: isSet[HOUR] = true; 770: isSet[HOUR_OF_DAY] = false; 771: break; 772: case HOUR_OF_DAY: 773: isSet[AM_PM] = false; 774: isSet[HOUR] = false; 775: break; 776: case HOUR: 777: isSet[AM_PM] = true; 778: isSet[HOUR_OF_DAY] = false; 779: break; 780: case DST_OFFSET: 781: explicitDSTOffset = true; 782: } 783: 784: // May have crossed over a DST boundary. 785: if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET)) 786: isSet[DST_OFFSET] = false; 787: } 788: 789: /** 790: * Sets the fields for year, month, and date 791: * @param year the year. 792: * @param month the month, one of the constants JANUARY..UNDICEMBER. 793: * @param date the day of the month 794: */ 795: public final void set(int year, int month, int date) 796: { 797: isTimeSet = false; 798: fields[YEAR] = year; 799: fields[MONTH] = month; 800: fields[DATE] = date; 801: isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; 802: isSet[WEEK_OF_YEAR] = false; 803: isSet[DAY_OF_YEAR] = false; 804: isSet[WEEK_OF_MONTH] = false; 805: isSet[DAY_OF_WEEK] = false; 806: isSet[DAY_OF_WEEK_IN_MONTH] = false; 807: isSet[ERA] = false; 808: 809: if (! explicitDSTOffset) 810: isSet[DST_OFFSET] = false; // May have crossed a DST boundary. 811: } 812: 813: /** 814: * Sets the fields for year, month, date, hour, and minute 815: * @param year the year. 816: * @param month the month, one of the constants JANUARY..UNDICEMBER. 817: * @param date the day of the month 818: * @param hour the hour of day. 819: * @param minute the minute. 820: */ 821: public final void set(int year, int month, int date, int hour, int minute) 822: { 823: set(year, month, date); 824: fields[HOUR_OF_DAY] = hour; 825: fields[MINUTE] = minute; 826: isSet[HOUR_OF_DAY] = isSet[MINUTE] = true; 827: isSet[AM_PM] = false; 828: isSet[HOUR] = false; 829: } 830: 831: /** 832: * Sets the fields for year, month, date, hour, and minute 833: * @param year the year. 834: * @param month the month, one of the constants JANUARY..UNDICEMBER. 835: * @param date the day of the month 836: * @param hour the hour of day. 837: * @param minute the minute. 838: * @param second the second. 839: */ 840: public final void set(int year, int month, int date, int hour, int minute, 841: int second) 842: { 843: set(year, month, date, hour, minute); 844: fields[SECOND] = second; 845: isSet[SECOND] = true; 846: } 847: 848: /** 849: * Clears the values of all the time fields. 850: */ 851: public final void clear() 852: { 853: isTimeSet = false; 854: areFieldsSet = false; 855: int zoneOffs = zone.getRawOffset(); 856: int[] tempFields = 857: { 858: 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, 859: 0, 0, zoneOffs, 0 860: }; 861: fields = tempFields; 862: for (int i = 0; i < FIELD_COUNT; i++) 863: isSet[i] = false; 864: } 865: 866: /** 867: * Clears the values of the specified time field. 868: * @param field the time field. One of the time field constants. 869: * @throws ArrayIndexOutOfBoundsException if field is outside 870: * the valid range. The value of field must be >= 0 and 871: * <= <code>FIELD_COUNT</code>. 872: */ 873: public final void clear(int field) 874: { 875: int[] tempFields = 876: { 877: 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, 878: 0, 0, zone.getRawOffset(), 0 879: }; 880: isTimeSet = false; 881: areFieldsSet = false; 882: isSet[field] = false; 883: fields[field] = tempFields[field]; 884: } 885: 886: /** 887: * Determines if the specified field has a valid value. 888: * @return true if the specified field has a value. 889: * @throws ArrayIndexOutOfBoundsException if the field is outside 890: * the valid range. The value of field must be >= 0 and 891: * <= <code>FIELD_COUNT</code>. 892: */ 893: public final boolean isSet(int field) 894: { 895: return isSet[field]; 896: } 897: 898: /** 899: * Fills any unset fields in the time field list 900: */ 901: protected void complete() 902: { 903: if (! isTimeSet) 904: computeTime(); 905: if (! areFieldsSet) 906: computeFields(); 907: } 908: 909: /** 910: * Compares the given calendar with this. 911: * @param o the object to that we should compare. 912: * @return true, if the given object is a calendar, that represents 913: * the same time (but doesn't necessary have the same fields). 914: */ 915: public boolean equals(Object o) 916: { 917: return (o instanceof Calendar) 918: && getTimeInMillis() == ((Calendar) o).getTimeInMillis(); 919: } 920: 921: /** 922: * Returns a hash code for this calendar. 923: * @return a hash code, which fullfits the general contract of 924: * <code>hashCode()</code> 925: */ 926: public int hashCode() 927: { 928: long time = getTimeInMillis(); 929: return (int) ((time & 0xffffffffL) ^ (time >> 32)); 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: }
GNU Classpath (0.17) |