GNU Classpath (0.20) | |
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: if (! (o instanceof Calendar)) 918: return false; 919: Calendar cal = (Calendar) o; 920: if (getTimeInMillis() == ((Calendar) o).getTimeInMillis() 921: && cal.getFirstDayOfWeek() == getFirstDayOfWeek() 922: && cal.isLenient() == isLenient() 923: && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek()) 924: { 925: TimeZone self = getTimeZone(); 926: TimeZone oth = cal.getTimeZone(); 927: return self == null ? oth == null : self.equals(oth); 928: } 929: return false; 930: } 931: 932: /** 933: * Returns a hash code for this calendar. 934: * @return a hash code, which fullfits the general contract of 935: * <code>hashCode()</code> 936: */ 937: public int hashCode() 938: { 939: long time = getTimeInMillis(); 940: int val = (int) ((time & 0xffffffffL) ^ (time >> 32)); 941: val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237) 942: + getMinimalDaysInFirstWeek()); 943: TimeZone self = getTimeZone(); 944: if (self != null) 945: val ^= self.hashCode(); 946: return val; 947: } 948: 949: /** 950: * Compares the given calendar with this. 951: * @param o the object to that we should compare. 952: * @return true, if the given object is a calendar, and this calendar 953: * represents a smaller time than the calendar o. 954: * @exception ClassCastException if o is not an calendar. 955: * @since JDK1.2 you don't need to override this method 956: */ 957: public boolean before(Object o) 958: { 959: return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); 960: } 961: 962: /** 963: * Compares the given calendar with this. 964: * @param o the object to that we should compare. 965: * @return true, if the given object is a calendar, and this calendar 966: * represents a bigger time than the calendar o. 967: * @exception ClassCastException if o is not an calendar. 968: * @since JDK1.2 you don't need to override this method 969: */ 970: public boolean after(Object o) 971: { 972: return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); 973: } 974: 975: /** 976: * Adds the specified amount of time to the given time field. The 977: * amount may be negative to subtract the time. If the field overflows 978: * it does what you expect: Jan, 25 + 10 Days is Feb, 4. 979: * @param field the time field. One of the time field constants. 980: * @param amount the amount of time. 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 add(int field, int amount); 986: 987: /** 988: * Rolls the specified time field up or down. This means add one 989: * to the specified field, but don't change the other fields. If 990: * the maximum for this field is reached, start over with the 991: * minimum value. <br> 992: * 993: * <strong>Note:</strong> There may be situation, where the other 994: * fields must be changed, e.g rolling the month on May, 31. 995: * The date June, 31 is automatically converted to July, 1. 996: * @param field the time field. One of the time field constants. 997: * @param up the direction, true for up, false for down. 998: * @throws ArrayIndexOutOfBoundsException if the field is outside 999: * the valid range. The value of field must be >= 0 and 1000: * <= <code>FIELD_COUNT</code>. 1001: */ 1002: public abstract void roll(int field, boolean up); 1003: 1004: /** 1005: * Rolls up or down the specified time field by the given amount. 1006: * A negative amount rolls down. The default implementation is 1007: * call <code>roll(int, boolean)</code> for the specified amount. 1008: * 1009: * Subclasses should override this method to do more intuitiv things. 1010: * 1011: * @param field the time field. One of the time field constants. 1012: * @param amount the amount to roll by, positive for rolling up, 1013: * negative for rolling down. 1014: * @throws ArrayIndexOutOfBoundsException if the field is outside 1015: * the valid range. The value of field must be >= 0 and 1016: * <= <code>FIELD_COUNT</code>. 1017: * @since JDK1.2 1018: */ 1019: public void roll(int field, int amount) 1020: { 1021: while (amount > 0) 1022: { 1023: roll(field, true); 1024: amount--; 1025: } 1026: while (amount < 0) 1027: { 1028: roll(field, false); 1029: amount++; 1030: } 1031: } 1032: 1033: /** 1034: * Sets the time zone to the specified value. 1035: * @param zone the new time zone 1036: */ 1037: public void setTimeZone(TimeZone zone) 1038: { 1039: this.zone = zone; 1040: } 1041: 1042: /** 1043: * Gets the time zone of this calendar 1044: * @return the current time zone. 1045: */ 1046: public TimeZone getTimeZone() 1047: { 1048: return zone; 1049: } 1050: 1051: /** 1052: * Specifies if the date/time interpretation should be lenient. 1053: * If the flag is set, a date such as "February 30, 1996" will be 1054: * treated as the 29th day after the February 1. If this flag 1055: * is false, such dates will cause an exception. 1056: * @param lenient true, if the date should be interpreted linient, 1057: * false if it should be interpreted strict. 1058: */ 1059: public void setLenient(boolean lenient) 1060: { 1061: this.lenient = lenient; 1062: } 1063: 1064: /** 1065: * Tells if the date/time interpretation is lenient. 1066: * @return true, if the date should be interpreted linient, 1067: * false if it should be interpreted strict. 1068: */ 1069: public boolean isLenient() 1070: { 1071: return lenient; 1072: } 1073: 1074: /** 1075: * Sets what the first day of week is. This is used for 1076: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 1077: * @param value the first day of week. One of SUNDAY to SATURDAY. 1078: */ 1079: public void setFirstDayOfWeek(int value) 1080: { 1081: firstDayOfWeek = value; 1082: } 1083: 1084: /** 1085: * Gets what the first day of week is. This is used for 1086: * WEEK_OF_MONTH and WEEK_OF_YEAR fields. 1087: * @return the first day of week. One of SUNDAY to SATURDAY. 1088: */ 1089: public int getFirstDayOfWeek() 1090: { 1091: return firstDayOfWeek; 1092: } 1093: 1094: /** 1095: * Sets how many days are required in the first week of the year. 1096: * If the first day of the year should be the first week you should 1097: * set this value to 1. If the first week must be a full week, set 1098: * it to 7. 1099: * @param value the minimal days required in the first week. 1100: */ 1101: public void setMinimalDaysInFirstWeek(int value) 1102: { 1103: minimalDaysInFirstWeek = value; 1104: } 1105: 1106: /** 1107: * Gets how many days are required in the first week of the year. 1108: * @return the minimal days required in the first week. 1109: * @see #setMinimalDaysInFirstWeek 1110: */ 1111: public int getMinimalDaysInFirstWeek() 1112: { 1113: return minimalDaysInFirstWeek; 1114: } 1115: 1116: /** 1117: * Gets the smallest value that is allowed for the specified field. 1118: * @param field the time field. One of the time field constants. 1119: * @return the smallest value. 1120: */ 1121: public abstract int getMinimum(int field); 1122: 1123: /** 1124: * Gets the biggest value that is allowed for the specified field. 1125: * @param field the time field. One of the time field constants. 1126: * @return the biggest value. 1127: */ 1128: public abstract int getMaximum(int field); 1129: 1130: /** 1131: * Gets the greatest minimum value that is allowed for the specified field. 1132: * @param field the time field. One of the time field constants. 1133: * @return the greatest minimum value. 1134: */ 1135: public abstract int getGreatestMinimum(int field); 1136: 1137: /** 1138: * Gets the smallest maximum value that is allowed for the 1139: * specified field. For example this is 28 for DAY_OF_MONTH. 1140: * @param field the time field. One of the time field constants. 1141: * @return the least maximum value. 1142: */ 1143: public abstract int getLeastMaximum(int field); 1144: 1145: /** 1146: * Gets the actual minimum value that is allowed for the specified field. 1147: * This value is dependent on the values of the other fields. 1148: * @param field the time field. One of the time field constants. 1149: * @return the actual minimum value. 1150: * @throws ArrayIndexOutOfBoundsException if the field is outside 1151: * the valid range. The value of field must be >= 0 and 1152: * <= <code>FIELD_COUNT</code>. 1153: * @since jdk1.2 1154: */ 1155: public int getActualMinimum(int field) 1156: { 1157: Calendar tmp = (Calendar) clone(); // To avoid restoring state 1158: int min = tmp.getGreatestMinimum(field); 1159: int end = tmp.getMinimum(field); 1160: tmp.set(field, min); 1161: for (; min > end; min--) 1162: { 1163: tmp.add(field, -1); // Try to get smaller 1164: if (tmp.get(field) != min - 1) 1165: break; // Done if not successful 1166: } 1167: return min; 1168: } 1169: 1170: /** 1171: * Gets the actual maximum value that is allowed for the specified field. 1172: * This value is dependent on the values of the other fields. 1173: * @param field the time field. One of the time field constants. 1174: * @return the actual maximum value. 1175: * @throws ArrayIndexOutOfBoundsException if the field is outside 1176: * the valid range. The value of field must be >= 0 and 1177: * <= <code>FIELD_COUNT</code>. 1178: * @since jdk1.2 1179: */ 1180: public int getActualMaximum(int field) 1181: { 1182: Calendar tmp = (Calendar) clone(); // To avoid restoring state 1183: int max = tmp.getLeastMaximum(field); 1184: int end = tmp.getMaximum(field); 1185: tmp.set(field, max); 1186: for (; max < end; max++) 1187: { 1188: tmp.add(field, 1); 1189: if (tmp.get(field) != max + 1) 1190: break; 1191: } 1192: return max; 1193: } 1194: 1195: /** 1196: * Return a clone of this object. 1197: */ 1198: public Object clone() 1199: { 1200: try 1201: { 1202: Calendar cal = (Calendar) super.clone(); 1203: cal.fields = (int[]) fields.clone(); 1204: cal.isSet = (boolean[]) isSet.clone(); 1205: return cal; 1206: } 1207: catch (CloneNotSupportedException ex) 1208: { 1209: return null; 1210: } 1211: } 1212: 1213: private static final String[] fieldNames = 1214: { 1215: ",ERA=", ",YEAR=", ",MONTH=", 1216: ",WEEK_OF_YEAR=", 1217: ",WEEK_OF_MONTH=", 1218: ",DAY_OF_MONTH=", 1219: ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", 1220: ",DAY_OF_WEEK_IN_MONTH=", 1221: ",AM_PM=", ",HOUR=", 1222: ",HOUR_OF_DAY=", ",MINUTE=", 1223: ",SECOND=", ",MILLISECOND=", 1224: ",ZONE_OFFSET=", ",DST_OFFSET=" 1225: }; 1226: 1227: /** 1228: * Returns a string representation of this object. It is mainly 1229: * for debugging purposes and its content is implementation 1230: * specific. 1231: */ 1232: public String toString() 1233: { 1234: StringBuffer sb = new StringBuffer(); 1235: sb.append(getClass().getName()).append('['); 1236: sb.append("time="); 1237: if (isTimeSet) 1238: sb.append(time); 1239: else 1240: sb.append("?"); 1241: sb.append(",zone=" + zone); 1242: sb.append(",areFieldsSet=" + areFieldsSet); 1243: for (int i = 0; i < FIELD_COUNT; i++) 1244: { 1245: sb.append(fieldNames[i]); 1246: if (isSet[i]) 1247: sb.append(fields[i]); 1248: else 1249: sb.append("?"); 1250: } 1251: sb.append(",lenient=").append(lenient); 1252: sb.append(",firstDayOfWeek=").append(firstDayOfWeek); 1253: sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); 1254: sb.append("]"); 1255: return sb.toString(); 1256: } 1257: 1258: /** 1259: * Saves the state of the object to the stream. Ideally we would 1260: * only write the time field, but we need to be compatible with 1261: * earlier versions. <br> 1262: * 1263: * This doesn't write the JDK1.1 field nextStamp to the stream, as 1264: * I don't know what it is good for, and because the documentation 1265: * says, that it could be omitted. */ 1266: private void writeObject(ObjectOutputStream stream) throws IOException 1267: { 1268: if (! isTimeSet) 1269: computeTime(); 1270: stream.defaultWriteObject(); 1271: } 1272: 1273: /** 1274: * Reads the object back from stream (deserialization). 1275: */ 1276: private void readObject(ObjectInputStream stream) 1277: throws IOException, ClassNotFoundException 1278: { 1279: stream.defaultReadObject(); 1280: if (! isTimeSet) 1281: computeTime(); 1282: 1283: if (serialVersionOnStream > 1) 1284: { 1285: // This is my interpretation of the serial number: 1286: // Sun wants to remove all fields from the stream someday 1287: // and will then increase the serialVersion number again. 1288: // We prepare to be compatible. 1289: fields = new int[FIELD_COUNT]; 1290: isSet = new boolean[FIELD_COUNT]; 1291: areFieldsSet = false; 1292: } 1293: } 1294: }
GNU Classpath (0.20) |