| /* Calendar.java -- | |
| Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.util; | |
| import java.io.IOException; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.Serializable; | |
| import java.lang.reflect.Constructor; | |
| import java.lang.reflect.InvocationTargetException; | |
| /** | |
| * This class is an abstract base class for Calendars, which can be | |
| * used to convert between <code>Date</code> objects and a set of | |
| * integer fields which represent <code>YEAR</code>, | |
| * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code> | |
| * object represents a time in milliseconds since the Epoch. <br> | |
| * | |
| * This class is locale sensitive. To get the Object matching the | |
| * current locale you can use <code>getInstance</code>. You can even provide | |
| * a locale or a timezone. <code>getInstance</code> returns currently | |
| * a <code>GregorianCalendar</code> for the current date. <br> | |
| * | |
| * If you want to convert a date from the Year, Month, Day, DayOfWeek, | |
| * etc. Representation to a <code>Date</code>-Object, you can create | |
| * a new Calendar with <code>getInstance()</code>, | |
| * <code>clear()</code> all fields, <code>set(int,int)</code> the | |
| * fields you need and convert it with <code>getTime()</code>. <br> | |
| * | |
| * If you want to convert a <code>Date</code>-object to the Calendar | |
| * representation, create a new Calendar, assign the | |
| * <code>Date</code>-Object with <code>setTime()</code>, and read the | |
| * fields with <code>get(int)</code>. <br> | |
| * | |
| * When computing the date from time fields, it may happen, that there | |
| * are either two few fields set, or some fields are inconsistent. This | |
| * cases will handled in a calendar specific way. Missing fields are | |
| * replaced by the fields of the epoch: 1970 January 1 00:00. <br> | |
| * | |
| * To understand, how the day of year is computed out of the fields | |
| * look at the following table. It is traversed from top to bottom, | |
| * and for the first line all fields are set, that line is used to | |
| * compute the day. <br> | |
| * | |
| * | |
| <pre>month + day_of_month | |
| month + week_of_month + day_of_week | |
| month + day_of_week_of_month + day_of_week | |
| day_of_year | |
| day_of_week + week_of_year</pre> | |
| * | |
| * The hour_of_day-field takes precedence over the ampm and | |
| * hour_of_ampm fields. <br> | |
| * | |
| * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br> | |
| * | |
| * To convert a calendar to a human readable form and vice versa, use | |
| * the <code>java.text.DateFormat</code> class. <br> | |
| * | |
| * Other useful things you can do with an calendar, is | |
| * <code>roll</code>ing fields (that means increase/decrease a | |
| * specific field by one, propagating overflows), or | |
| * <code>add</code>ing/substracting a fixed amount to a field. | |
| * | |
| * @see Date | |
| * @see GregorianCalendar | |
| * @see TimeZone | |
| * @see java.text.DateFormat | |
| */ | |
| public abstract class Calendar implements Serializable, Cloneable | |
| { | |
| /** | |
| * Constant representing the era time field. | |
| */ | |
| public static final int ERA = 0; | |
| /** | |
| * Constant representing the year time field. | |
| */ | |
| public static final int YEAR = 1; | |
| /** | |
| * Constant representing the month time field. This field | |
| * should contain one of the JANUARY,...,DECEMBER constants below. | |
| */ | |
| public static final int MONTH = 2; | |
| /** | |
| * Constant representing the week of the year field. | |
| * @see #setFirstDayOfWeek(int) | |
| */ | |
| public static final int WEEK_OF_YEAR = 3; | |
| /** | |
| * Constant representing the week of the month time field. | |
| * @see #setFirstDayOfWeek(int) | |
| */ | |
| public static final int WEEK_OF_MONTH = 4; | |
| /** | |
| * Constant representing the day time field, synonym for DAY_OF_MONTH. | |
| */ | |
| public static final int DATE = 5; | |
| /** | |
| * Constant representing the day time field. | |
| */ | |
| public static final int DAY_OF_MONTH = 5; | |
| /** | |
| * Constant representing the day of year time field. This is | |
| * 1 for the first day in month. | |
| */ | |
| public static final int DAY_OF_YEAR = 6; | |
| /** | |
| * Constant representing the day of week time field. This field | |
| * should contain one of the SUNDAY,...,SATURDAY constants below. | |
| */ | |
| public static final int DAY_OF_WEEK = 7; | |
| /** | |
| * Constant representing the day-of-week-in-month field. For | |
| * instance this field contains 2 for the second thursday in a | |
| * month. If you give a negative number here, the day will count | |
| * from the end of the month. | |
| */ | |
| public static final int DAY_OF_WEEK_IN_MONTH = 8; | |
| /** | |
| * Constant representing the part of the day for 12-hour clock. This | |
| * should be one of AM or PM. | |
| */ | |
| public static final int AM_PM = 9; | |
| /** | |
| * Constant representing the hour time field for 12-hour clock. | |
| */ | |
| public static final int HOUR = 10; | |
| /** | |
| * Constant representing the hour of day time field for 24-hour clock. | |
| */ | |
| public static final int HOUR_OF_DAY = 11; | |
| /** | |
| * Constant representing the minute of hour time field. | |
| */ | |
| public static final int MINUTE = 12; | |
| /** | |
| * Constant representing the second time field. | |
| */ | |
| public static final int SECOND = 13; | |
| /** | |
| * Constant representing the millisecond time field. | |
| */ | |
| public static final int MILLISECOND = 14; | |
| /** | |
| * Constant representing the time zone offset time field for the | |
| * time given in the other fields. It is measured in | |
| * milliseconds. The default is the offset of the time zone. | |
| */ | |
| public static final int ZONE_OFFSET = 15; | |
| /** | |
| * Constant representing the daylight saving time offset in | |
| * milliseconds. The default is the value given by the time zone. | |
| */ | |
| public static final int DST_OFFSET = 16; | |
| /** | |
| * Number of time fields. | |
| */ | |
| public static final int FIELD_COUNT = 17; | |
| /** | |
| * Constant representing Sunday. | |
| */ | |
| public static final int SUNDAY = 1; | |
| /** | |
| * Constant representing Monday. | |
| */ | |
| public static final int MONDAY = 2; | |
| /** | |
| * Constant representing Tuesday. | |
| */ | |
| public static final int TUESDAY = 3; | |
| /** | |
| * Constant representing Wednesday. | |
| */ | |
| public static final int WEDNESDAY = 4; | |
| /** | |
| * Constant representing Thursday. | |
| */ | |
| public static final int THURSDAY = 5; | |
| /** | |
| * Constant representing Friday. | |
| */ | |
| public static final int FRIDAY = 6; | |
| /** | |
| * Constant representing Saturday. | |
| */ | |
| public static final int SATURDAY = 7; | |
| /** | |
| * Constant representing January. | |
| */ | |
| public static final int JANUARY = 0; | |
| /** | |
| * Constant representing February. | |
| */ | |
| public static final int FEBRUARY = 1; | |
| /** | |
| * Constant representing March. | |
| */ | |
| public static final int MARCH = 2; | |
| /** | |
| * Constant representing April. | |
| */ | |
| public static final int APRIL = 3; | |
| /** | |
| * Constant representing May. | |
| */ | |
| public static final int MAY = 4; | |
| /** | |
| * Constant representing June. | |
| */ | |
| public static final int JUNE = 5; | |
| /** | |
| * Constant representing July. | |
| */ | |
| public static final int JULY = 6; | |
| /** | |
| * Constant representing August. | |
| */ | |
| public static final int AUGUST = 7; | |
| /** | |
| * Constant representing September. | |
| */ | |
| public static final int SEPTEMBER = 8; | |
| /** | |
| * Constant representing October. | |
| */ | |
| public static final int OCTOBER = 9; | |
| /** | |
| * Constant representing November. | |
| */ | |
| public static final int NOVEMBER = 10; | |
| /** | |
| * Constant representing December. | |
| */ | |
| public static final int DECEMBER = 11; | |
| /** | |
| * Constant representing Undecimber. This is an artificial name useful | |
| * for lunar calendars. | |
| */ | |
| public static final int UNDECIMBER = 12; | |
| /** | |
| * Useful constant for 12-hour clock. | |
| */ | |
| public static final int AM = 0; | |
| /** | |
| * Useful constant for 12-hour clock. | |
| */ | |
| public static final int PM = 1; | |
| /** | |
| * The time fields. The array is indexed by the constants YEAR to | |
| * DST_OFFSET. | |
| * @serial | |
| */ | |
| protected int[] fields = new int[FIELD_COUNT]; | |
| /** | |
| * The flags which tell if the fields above have a value. | |
| * @serial | |
| */ | |
| protected boolean[] isSet = new boolean[FIELD_COUNT]; | |
| /** | |
| * The time in milliseconds since the epoch. | |
| * @serial | |
| */ | |
| protected long time; | |
| /** | |
| * Tells if the above field has a valid value. | |
| * @serial | |
| */ | |
| protected boolean isTimeSet; | |
| /** | |
| * Tells if the fields have a valid value. This superseeds the isSet | |
| * array. | |
| * @serial | |
| */ | |
| protected boolean areFieldsSet; | |
| /** | |
| * The time zone of this calendar. Used by sub classes to do UTC / local | |
| * time conversion. Sub classes can access this field with getTimeZone(). | |
| * @serial | |
| */ | |
| private TimeZone zone; | |
| /** | |
| * Specifies if the date/time interpretation should be lenient. | |
| * If the flag is set, a date such as "February 30, 1996" will be | |
| * treated as the 29th day after the February 1. If this flag | |
| * is false, such dates will cause an exception. | |
| * @serial | |
| */ | |
| private boolean lenient; | |
| /** | |
| * Sets what the first day of week is. This is used for | |
| * WEEK_OF_MONTH and WEEK_OF_YEAR fields. | |
| * @serial | |
| */ | |
| private int firstDayOfWeek; | |
| /** | |
| * Sets how many days are required in the first week of the year. | |
| * If the first day of the year should be the first week you should | |
| * set this value to 1. If the first week must be a full week, set | |
| * it to 7. | |
| * @serial | |
| */ | |
| private int minimalDaysInFirstWeek; | |
| /** | |
| * Is set to true if DST_OFFSET is explicitly set. In that case | |
| * it's value overrides the value computed from the current | |
| * time and the timezone. | |
| */ | |
| private boolean explicitDSTOffset = false; | |
| /** | |
| * The version of the serialized data on the stream. | |
| * <dl><dt>0 or not present</dt> | |
| * <dd> JDK 1.1.5 or later.</dd> | |
| * <dt>1</dt> | |
| * <dd>JDK 1.1.6 or later. This always writes a correct `time' value | |
| * on the stream, as well as the other fields, to be compatible with | |
| * earlier versions</dd></dl> | |
| * @since JDK1.1.6 | |
| * @serial | |
| */ | |
| private int serialVersionOnStream = 1; | |
| /** | |
| * XXX - I have not checked the compatibility. The documentation of | |
| * the serialized-form is quite hairy... | |
| */ | |
| static final long serialVersionUID = -1807547505821590642L; | |
| /** | |
| * The name of the resource bundle. Used only by getBundle() | |
| */ | |
| private static final String bundleName = "gnu.java.locale.Calendar"; | |
| /** | |
| * get resource bundle: | |
| * The resources should be loaded via this method only. Iff an application | |
| * uses this method, the resourcebundle is required. | |
| */ | |
| private static ResourceBundle getBundle(Locale locale) | |
| { | |
| return ResourceBundle.getBundle(bundleName, locale, | |
| ClassLoader.getSystemClassLoader()); | |
| } | |
| /** | |
| * Constructs a new Calendar with the default time zone and the default | |
| * locale. | |
| */ | |
| protected Calendar() | |
| { | |
| this(TimeZone.getDefault(), Locale.getDefault()); | |
| } | |
| /** | |
| * Constructs a new Calendar with the given time zone and the given | |
| * locale. | |
| * @param zone a time zone. | |
| * @param locale a locale. | |
| */ | |
| protected Calendar(TimeZone zone, Locale locale) | |
| { | |
| this.zone = zone; | |
| lenient = true; | |
| ResourceBundle rb = getBundle(locale); | |
| firstDayOfWeek = ((Integer) rb.getObject("firstDayOfWeek")).intValue(); | |
| minimalDaysInFirstWeek = ((Integer) rb.getObject("minimalDaysInFirstWeek")) | |
| .intValue(); | |
| clear(); | |
| } | |
| /** | |
| * Creates a calendar representing the actual time, using the default | |
| * time zone and locale. | |
| */ | |
| public static synchronized Calendar getInstance() | |
| { | |
| return getInstance(TimeZone.getDefault(), Locale.getDefault()); | |
| } | |
| /** | |
| * Creates a calendar representing the actual time, using the given | |
| * time zone and the default locale. | |
| * @param zone a time zone. | |
| */ | |
| public static synchronized Calendar getInstance(TimeZone zone) | |
| { | |
| return getInstance(zone, Locale.getDefault()); | |
| } | |
| /** | |
| * Creates a calendar representing the actual time, using the default | |
| * time zone and the given locale. | |
| * @param locale a locale. | |
| */ | |
| public static synchronized Calendar getInstance(Locale locale) | |
| { | |
| return getInstance(TimeZone.getDefault(), locale); | |
| } | |
| /** | |
| * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle | |
| * lookup for every getInstance call. | |
| */ | |
| private static HashMap cache = new HashMap(); | |
| /** Preset argument types for calendar-class constructor lookup. */ | |
| private static Class[] ctorArgTypes = new Class[] | |
| { | |
| TimeZone.class, Locale.class | |
| }; | |
| /** | |
| * Creates a calendar representing the actual time, using the given | |
| * time zone and locale. | |
| * @param zone a time zone. | |
| * @param locale a locale. | |
| */ | |
| public static synchronized Calendar getInstance(TimeZone zone, Locale locale) | |
| { | |
| Class calendarClass = (Class) cache.get(locale); | |
| Throwable exception = null; | |
| try | |
| { | |
| if (calendarClass == null) | |
| { | |
| ResourceBundle rb = getBundle(locale); | |
| String calendarClassName = rb.getString("calendarClass"); | |
| if (calendarClassName != null) | |
| { | |
| calendarClass = Class.forName(calendarClassName); | |
| if (Calendar.class.isAssignableFrom(calendarClass)) | |
| cache.put(locale, calendarClass); | |
| } | |
| } | |
| // GregorianCalendar is by far the most common case. Optimize by | |
| // avoiding reflection. | |
| if (calendarClass == GregorianCalendar.class) | |
| return new GregorianCalendar(zone, locale); | |
| if (Calendar.class.isAssignableFrom(calendarClass)) | |
| { | |
| Constructor ctor = calendarClass.getConstructor(ctorArgTypes); | |
| return (Calendar) ctor.newInstance(new Object[] { zone, locale }); | |
| } | |
| } | |
| catch (ClassNotFoundException ex) | |
| { | |
| exception = ex; | |
| } | |
| catch (IllegalAccessException ex) | |
| { | |
| exception = ex; | |
| } | |
| catch (NoSuchMethodException ex) | |
| { | |
| exception = ex; | |
| } | |
| catch (InstantiationException ex) | |
| { | |
| exception = ex; | |
| } | |
| catch (InvocationTargetException ex) | |
| { | |
| exception = ex; | |
| } | |
| throw new RuntimeException("Error instantiating calendar for locale " | |
| + locale, exception); | |
| } | |
| /** | |
| * Gets the set of locales for which a Calendar is available. | |
| * @exception MissingResourceException if locale data couldn't be found. | |
| * @return the set of locales. | |
| */ | |
| public static synchronized Locale[] getAvailableLocales() | |
| { | |
| ResourceBundle rb = getBundle(new Locale("", "")); | |
| return (Locale[]) rb.getObject("availableLocales"); | |
| } | |
| /** | |
| * Converts the time field values (<code>fields</code>) to | |
| * milliseconds since the epoch UTC (<code>time</code>). Override | |
| * this method if you write your own Calendar. */ | |
| protected abstract void computeTime(); | |
| /** | |
| * Converts the milliseconds since the epoch UTC | |
| * (<code>time</code>) to time fields | |
| * (<code>fields</code>). Override this method if you write your | |
| * own Calendar. | |
| */ | |
| protected abstract void computeFields(); | |
| /** | |
| * Converts the time represented by this object to a | |
| * <code>Date</code>-Object. | |
| * @return the Date. | |
| */ | |
| public final Date getTime() | |
| { | |
| if (! isTimeSet) | |
| computeTime(); | |
| return new Date(time); | |
| } | |
| /** | |
| * Sets this Calendar's time to the given Date. All time fields | |
| * are invalidated by this method. | |
| */ | |
| public final void setTime(Date date) | |
| { | |
| setTimeInMillis(date.getTime()); | |
| } | |
| /** | |
| * Returns the time represented by this Calendar. | |
| * @return the time in milliseconds since the epoch. | |
| * @specnote This was made public in 1.4. | |
| */ | |
| public long getTimeInMillis() | |
| { | |
| if (! isTimeSet) | |
| computeTime(); | |
| return time; | |
| } | |
| /** | |
| * Sets this Calendar's time to the given Time. All time fields | |
| * are invalidated by this method. | |
| * @param time the time in milliseconds since the epoch | |
| * @specnote This was made public in 1.4. | |
| */ | |
| public void setTimeInMillis(long time) | |
| { | |
| clear(); | |
| this.time = time; | |
| isTimeSet = true; | |
| computeFields(); | |
| } | |
| /** | |
| * Gets the value of the specified field. They are recomputed | |
| * if they are invalid. | |
| * @param field the time field. One of the time field constants. | |
| * @return the value of the specified field | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| * @specnote Not final since JDK 1.4 | |
| */ | |
| public int get(int field) | |
| { | |
| // If the requested field is invalid, force all fields to be recomputed. | |
| if (! isSet[field]) | |
| areFieldsSet = false; | |
| complete(); | |
| return fields[field]; | |
| } | |
| /** | |
| * Gets the value of the specified field. This method doesn't | |
| * recompute the fields, if they are invalid. | |
| * @param field the time field. One of the time field constants. | |
| * @return the value of the specified field, undefined if | |
| * <code>areFieldsSet</code> or <code>isSet[field]</code> is false. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| */ | |
| protected final int internalGet(int field) | |
| { | |
| return fields[field]; | |
| } | |
| /** | |
| * Sets the time field with the given value. This does invalidate | |
| * the time in milliseconds. | |
| * @param field the time field. One of the time field constants | |
| * @param value the value to be set. | |
| * @throws ArrayIndexOutOfBoundsException if field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| * @specnote Not final since JDK 1.4 | |
| */ | |
| public void set(int field, int value) | |
| { | |
| if (isTimeSet) | |
| for (int i = 0; i < FIELD_COUNT; i++) | |
| isSet[i] = false; | |
| isTimeSet = false; | |
| fields[field] = value; | |
| isSet[field] = true; | |
| // The five valid date patterns, in order of priority | |
| // 1 YEAR + MONTH + DAY_OF_MONTH | |
| // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK | |
| // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK | |
| // 4 YEAR + DAY_OF_YEAR | |
| // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR | |
| switch (field) | |
| { | |
| case MONTH: // pattern 1,2 or 3 | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[WEEK_OF_YEAR] = false; | |
| break; | |
| case DAY_OF_MONTH: // pattern 1 | |
| isSet[YEAR] = true; | |
| isSet[MONTH] = true; | |
| isSet[WEEK_OF_MONTH] = true; | |
| isSet[DAY_OF_WEEK] = false; | |
| isSet[DAY_OF_WEEK_IN_MONTH] = false; | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[WEEK_OF_YEAR] = false; | |
| break; | |
| case WEEK_OF_MONTH: // pattern 2 | |
| if (! isSet[DAY_OF_WEEK]) | |
| fields[DAY_OF_WEEK] = getFirstDayOfWeek(); | |
| isSet[YEAR] = true; | |
| isSet[MONTH] = true; | |
| isSet[DAY_OF_WEEK] = true; | |
| isSet[DAY_OF_MONTH] = false; | |
| isSet[DAY_OF_WEEK_IN_MONTH] = false; | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[WEEK_OF_YEAR] = false; | |
| break; | |
| case DAY_OF_WEEK_IN_MONTH: // pattern 3 | |
| if (! isSet[DAY_OF_WEEK]) | |
| fields[DAY_OF_WEEK] = getFirstDayOfWeek(); | |
| isSet[YEAR] = true; | |
| isSet[MONTH] = true; | |
| isSet[DAY_OF_WEEK] = true; | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[DAY_OF_MONTH] = false; | |
| isSet[WEEK_OF_MONTH] = false; | |
| isSet[WEEK_OF_YEAR] = false; | |
| break; | |
| case DAY_OF_YEAR: // pattern 4 | |
| isSet[YEAR] = true; | |
| isSet[MONTH] = false; | |
| isSet[WEEK_OF_MONTH] = false; | |
| isSet[DAY_OF_MONTH] = false; | |
| isSet[DAY_OF_WEEK] = false; | |
| isSet[WEEK_OF_YEAR] = false; | |
| isSet[DAY_OF_WEEK_IN_MONTH] = false; | |
| break; | |
| case WEEK_OF_YEAR: // pattern 5 | |
| if (! isSet[DAY_OF_WEEK]) | |
| fields[DAY_OF_WEEK] = getFirstDayOfWeek(); | |
| isSet[YEAR] = true; | |
| isSet[DAY_OF_WEEK] = true; | |
| isSet[MONTH] = false; | |
| isSet[DAY_OF_MONTH] = false; | |
| isSet[WEEK_OF_MONTH] = false; | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[DAY_OF_WEEK_IN_MONTH] = false; | |
| break; | |
| case AM_PM: | |
| isSet[HOUR] = true; | |
| isSet[HOUR_OF_DAY] = false; | |
| break; | |
| case HOUR_OF_DAY: | |
| isSet[AM_PM] = false; | |
| isSet[HOUR] = false; | |
| break; | |
| case HOUR: | |
| isSet[AM_PM] = true; | |
| isSet[HOUR_OF_DAY] = false; | |
| break; | |
| case DST_OFFSET: | |
| explicitDSTOffset = true; | |
| } | |
| // May have crossed over a DST boundary. | |
| if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET)) | |
| isSet[DST_OFFSET] = false; | |
| } | |
| /** | |
| * Sets the fields for year, month, and date | |
| * @param year the year. | |
| * @param month the month, one of the constants JANUARY..UNDICEMBER. | |
| * @param date the day of the month | |
| */ | |
| public final void set(int year, int month, int date) | |
| { | |
| isTimeSet = false; | |
| fields[YEAR] = year; | |
| fields[MONTH] = month; | |
| fields[DATE] = date; | |
| isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; | |
| isSet[WEEK_OF_YEAR] = false; | |
| isSet[DAY_OF_YEAR] = false; | |
| isSet[WEEK_OF_MONTH] = false; | |
| isSet[DAY_OF_WEEK] = false; | |
| isSet[DAY_OF_WEEK_IN_MONTH] = false; | |
| isSet[ERA] = false; | |
| if (! explicitDSTOffset) | |
| isSet[DST_OFFSET] = false; // May have crossed a DST boundary. | |
| } | |
| /** | |
| * Sets the fields for year, month, date, hour, and minute | |
| * @param year the year. | |
| * @param month the month, one of the constants JANUARY..UNDICEMBER. | |
| * @param date the day of the month | |
| * @param hour the hour of day. | |
| * @param minute the minute. | |
| */ | |
| public final void set(int year, int month, int date, int hour, int minute) | |
| { | |
| set(year, month, date); | |
| fields[HOUR_OF_DAY] = hour; | |
| fields[MINUTE] = minute; | |
| isSet[HOUR_OF_DAY] = isSet[MINUTE] = true; | |
| isSet[AM_PM] = false; | |
| isSet[HOUR] = false; | |
| } | |
| /** | |
| * Sets the fields for year, month, date, hour, and minute | |
| * @param year the year. | |
| * @param month the month, one of the constants JANUARY..UNDICEMBER. | |
| * @param date the day of the month | |
| * @param hour the hour of day. | |
| * @param minute the minute. | |
| * @param second the second. | |
| */ | |
| public final void set(int year, int month, int date, int hour, int minute, | |
| int second) | |
| { | |
| set(year, month, date, hour, minute); | |
| fields[SECOND] = second; | |
| isSet[SECOND] = true; | |
| } | |
| /** | |
| * Clears the values of all the time fields. | |
| */ | |
| public final void clear() | |
| { | |
| isTimeSet = false; | |
| areFieldsSet = false; | |
| int zoneOffs = zone.getRawOffset(); | |
| int[] tempFields = | |
| { | |
| 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, | |
| 0, 0, zoneOffs, 0 | |
| }; | |
| fields = tempFields; | |
| for (int i = 0; i < FIELD_COUNT; i++) | |
| isSet[i] = false; | |
| } | |
| /** | |
| * Clears the values of the specified time field. | |
| * @param field the time field. One of the time field constants. | |
| * @throws ArrayIndexOutOfBoundsException if field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| */ | |
| public final void clear(int field) | |
| { | |
| int[] tempFields = | |
| { | |
| 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, | |
| 0, 0, zone.getRawOffset(), 0 | |
| }; | |
| isTimeSet = false; | |
| areFieldsSet = false; | |
| isSet[field] = false; | |
| fields[field] = tempFields[field]; | |
| } | |
| /** | |
| * Determines if the specified field has a valid value. | |
| * @return true if the specified field has a value. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| */ | |
| public final boolean isSet(int field) | |
| { | |
| return isSet[field]; | |
| } | |
| /** | |
| * Fills any unset fields in the time field list | |
| */ | |
| protected void complete() | |
| { | |
| if (! isTimeSet) | |
| computeTime(); | |
| if (! areFieldsSet) | |
| computeFields(); | |
| } | |
| /** | |
| * Compares the given calendar with this. | |
| * @param o the object to that we should compare. | |
| * @return true, if the given object is a calendar, that represents | |
| * the same time (but doesn't necessary have the same fields). | |
| */ | |
| public boolean equals(Object o) | |
| { | |
| if (! (o instanceof Calendar)) | |
| return false; | |
| Calendar cal = (Calendar) o; | |
| if (getTimeInMillis() == ((Calendar) o).getTimeInMillis() | |
| && cal.getFirstDayOfWeek() == getFirstDayOfWeek() | |
| && cal.isLenient() == isLenient() | |
| && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek()) | |
| { | |
| TimeZone self = getTimeZone(); | |
| TimeZone oth = cal.getTimeZone(); | |
| return self == null ? oth == null : self.equals(oth); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns a hash code for this calendar. | |
| * @return a hash code, which fullfits the general contract of | |
| * <code>hashCode()</code> | |
| */ | |
| public int hashCode() | |
| { | |
| long time = getTimeInMillis(); | |
| int val = (int) ((time & 0xffffffffL) ^ (time >> 32)); | |
| val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237) | |
| + getMinimalDaysInFirstWeek()); | |
| TimeZone self = getTimeZone(); | |
| if (self != null) | |
| val ^= self.hashCode(); | |
| return val; | |
| } | |
| /** | |
| * Compares the given calendar with this. | |
| * @param o the object to that we should compare. | |
| * @return true, if the given object is a calendar, and this calendar | |
| * represents a smaller time than the calendar o. | |
| * @exception ClassCastException if o is not an calendar. | |
| * @since JDK1.2 you don't need to override this method | |
| */ | |
| public boolean before(Object o) | |
| { | |
| return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); | |
| } | |
| /** | |
| * Compares the given calendar with this. | |
| * @param o the object to that we should compare. | |
| * @return true, if the given object is a calendar, and this calendar | |
| * represents a bigger time than the calendar o. | |
| * @exception ClassCastException if o is not an calendar. | |
| * @since JDK1.2 you don't need to override this method | |
| */ | |
| public boolean after(Object o) | |
| { | |
| return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); | |
| } | |
| /** | |
| * Adds the specified amount of time to the given time field. The | |
| * amount may be negative to subtract the time. If the field overflows | |
| * it does what you expect: Jan, 25 + 10 Days is Feb, 4. | |
| * @param field the time field. One of the time field constants. | |
| * @param amount the amount of time. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| */ | |
| public abstract void add(int field, int amount); | |
| /** | |
| * Rolls the specified time field up or down. This means add one | |
| * to the specified field, but don't change the other fields. If | |
| * the maximum for this field is reached, start over with the | |
| * minimum value. <br> | |
| * | |
| * <strong>Note:</strong> There may be situation, where the other | |
| * fields must be changed, e.g rolling the month on May, 31. | |
| * The date June, 31 is automatically converted to July, 1. | |
| * @param field the time field. One of the time field constants. | |
| * @param up the direction, true for up, false for down. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| */ | |
| public abstract void roll(int field, boolean up); | |
| /** | |
| * Rolls up or down the specified time field by the given amount. | |
| * A negative amount rolls down. The default implementation is | |
| * call <code>roll(int, boolean)</code> for the specified amount. | |
| * | |
| * Subclasses should override this method to do more intuitiv things. | |
| * | |
| * @param field the time field. One of the time field constants. | |
| * @param amount the amount to roll by, positive for rolling up, | |
| * negative for rolling down. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| * @since JDK1.2 | |
| */ | |
| public void roll(int field, int amount) | |
| { | |
| while (amount > 0) | |
| { | |
| roll(field, true); | |
| amount--; | |
| } | |
| while (amount < 0) | |
| { | |
| roll(field, false); | |
| amount++; | |
| } | |
| } | |
| /** | |
| * Sets the time zone to the specified value. | |
| * @param zone the new time zone | |
| */ | |
| public void setTimeZone(TimeZone zone) | |
| { | |
| this.zone = zone; | |
| } | |
| /** | |
| * Gets the time zone of this calendar | |
| * @return the current time zone. | |
| */ | |
| public TimeZone getTimeZone() | |
| { | |
| return zone; | |
| } | |
| /** | |
| * Specifies if the date/time interpretation should be lenient. | |
| * If the flag is set, a date such as "February 30, 1996" will be | |
| * treated as the 29th day after the February 1. If this flag | |
| * is false, such dates will cause an exception. | |
| * @param lenient true, if the date should be interpreted linient, | |
| * false if it should be interpreted strict. | |
| */ | |
| public void setLenient(boolean lenient) | |
| { | |
| this.lenient = lenient; | |
| } | |
| /** | |
| * Tells if the date/time interpretation is lenient. | |
| * @return true, if the date should be interpreted linient, | |
| * false if it should be interpreted strict. | |
| */ | |
| public boolean isLenient() | |
| { | |
| return lenient; | |
| } | |
| /** | |
| * Sets what the first day of week is. This is used for | |
| * WEEK_OF_MONTH and WEEK_OF_YEAR fields. | |
| * @param value the first day of week. One of SUNDAY to SATURDAY. | |
| */ | |
| public void setFirstDayOfWeek(int value) | |
| { | |
| firstDayOfWeek = value; | |
| } | |
| /** | |
| * Gets what the first day of week is. This is used for | |
| * WEEK_OF_MONTH and WEEK_OF_YEAR fields. | |
| * @return the first day of week. One of SUNDAY to SATURDAY. | |
| */ | |
| public int getFirstDayOfWeek() | |
| { | |
| return firstDayOfWeek; | |
| } | |
| /** | |
| * Sets how many days are required in the first week of the year. | |
| * If the first day of the year should be the first week you should | |
| * set this value to 1. If the first week must be a full week, set | |
| * it to 7. | |
| * @param value the minimal days required in the first week. | |
| */ | |
| public void setMinimalDaysInFirstWeek(int value) | |
| { | |
| minimalDaysInFirstWeek = value; | |
| } | |
| /** | |
| * Gets how many days are required in the first week of the year. | |
| * @return the minimal days required in the first week. | |
| * @see #setMinimalDaysInFirstWeek | |
| */ | |
| public int getMinimalDaysInFirstWeek() | |
| { | |
| return minimalDaysInFirstWeek; | |
| } | |
| /** | |
| * Gets the smallest value that is allowed for the specified field. | |
| * @param field the time field. One of the time field constants. | |
| * @return the smallest value. | |
| */ | |
| public abstract int getMinimum(int field); | |
| /** | |
| * Gets the biggest value that is allowed for the specified field. | |
| * @param field the time field. One of the time field constants. | |
| * @return the biggest value. | |
| */ | |
| public abstract int getMaximum(int field); | |
| /** | |
| * Gets the greatest minimum value that is allowed for the specified field. | |
| * @param field the time field. One of the time field constants. | |
| * @return the greatest minimum value. | |
| */ | |
| public abstract int getGreatestMinimum(int field); | |
| /** | |
| * Gets the smallest maximum value that is allowed for the | |
| * specified field. For example this is 28 for DAY_OF_MONTH. | |
| * @param field the time field. One of the time field constants. | |
| * @return the least maximum value. | |
| */ | |
| public abstract int getLeastMaximum(int field); | |
| /** | |
| * Gets the actual minimum value that is allowed for the specified field. | |
| * This value is dependent on the values of the other fields. | |
| * @param field the time field. One of the time field constants. | |
| * @return the actual minimum value. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| * @since jdk1.2 | |
| */ | |
| public int getActualMinimum(int field) | |
| { | |
| Calendar tmp = (Calendar) clone(); // To avoid restoring state | |
| int min = tmp.getGreatestMinimum(field); | |
| int end = tmp.getMinimum(field); | |
| tmp.set(field, min); | |
| for (; min > end; min--) | |
| { | |
| tmp.add(field, -1); // Try to get smaller | |
| if (tmp.get(field) != min - 1) | |
| break; // Done if not successful | |
| } | |
| return min; | |
| } | |
| /** | |
| * Gets the actual maximum value that is allowed for the specified field. | |
| * This value is dependent on the values of the other fields. | |
| * @param field the time field. One of the time field constants. | |
| * @return the actual maximum value. | |
| * @throws ArrayIndexOutOfBoundsException if the field is outside | |
| * the valid range. The value of field must be >= 0 and | |
| * <= <code>FIELD_COUNT</code>. | |
| * @since jdk1.2 | |
| */ | |
| public int getActualMaximum(int field) | |
| { | |
| Calendar tmp = (Calendar) clone(); // To avoid restoring state | |
| int max = tmp.getLeastMaximum(field); | |
| int end = tmp.getMaximum(field); | |
| tmp.set(field, max); | |
| for (; max < end; max++) | |
| { | |
| tmp.add(field, 1); | |
| if (tmp.get(field) != max + 1) | |
| break; | |
| } | |
| return max; | |
| } | |
| /** | |
| * Return a clone of this object. | |
| */ | |
| public Object clone() | |
| { | |
| try | |
| { | |
| Calendar cal = (Calendar) super.clone(); | |
| cal.fields = (int[]) fields.clone(); | |
| cal.isSet = (boolean[]) isSet.clone(); | |
| return cal; | |
| } | |
| catch (CloneNotSupportedException ex) | |
| { | |
| return null; | |
| } | |
| } | |
| private static final String[] fieldNames = | |
| { | |
| ",ERA=", ",YEAR=", ",MONTH=", | |
| ",WEEK_OF_YEAR=", | |
| ",WEEK_OF_MONTH=", | |
| ",DAY_OF_MONTH=", | |
| ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", | |
| ",DAY_OF_WEEK_IN_MONTH=", | |
| ",AM_PM=", ",HOUR=", | |
| ",HOUR_OF_DAY=", ",MINUTE=", | |
| ",SECOND=", ",MILLISECOND=", | |
| ",ZONE_OFFSET=", ",DST_OFFSET=" | |
| }; | |
| /** | |
| * Returns a string representation of this object. It is mainly | |
| * for debugging purposes and its content is implementation | |
| * specific. | |
| */ | |
| public String toString() | |
| { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.append(getClass().getName()).append('['); | |
| sb.append("time="); | |
| if (isTimeSet) | |
| sb.append(time); | |
| else | |
| sb.append("?"); | |
| sb.append(",zone=" + zone); | |
| sb.append(",areFieldsSet=" + areFieldsSet); | |
| for (int i = 0; i < FIELD_COUNT; i++) | |
| { | |
| sb.append(fieldNames[i]); | |
| if (isSet[i]) | |
| sb.append(fields[i]); | |
| else | |
| sb.append("?"); | |
| } | |
| sb.append(",lenient=").append(lenient); | |
| sb.append(",firstDayOfWeek=").append(firstDayOfWeek); | |
| sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); | |
| sb.append("]"); | |
| return sb.toString(); | |
| } | |
| /** | |
| * Saves the state of the object to the stream. Ideally we would | |
| * only write the time field, but we need to be compatible with | |
| * earlier versions. <br> | |
| * | |
| * This doesn't write the JDK1.1 field nextStamp to the stream, as | |
| * I don't know what it is good for, and because the documentation | |
| * says, that it could be omitted. */ | |
| private void writeObject(ObjectOutputStream stream) throws IOException | |
| { | |
| if (! isTimeSet) | |
| computeTime(); | |
| stream.defaultWriteObject(); | |
| } | |
| /** | |
| * Reads the object back from stream (deserialization). | |
| */ | |
| private void readObject(ObjectInputStream stream) | |
| throws IOException, ClassNotFoundException | |
| { | |
| stream.defaultReadObject(); | |
| if (! isTimeSet) | |
| computeTime(); | |
| if (serialVersionOnStream > 1) | |
| { | |
| // This is my interpretation of the serial number: | |
| // Sun wants to remove all fields from the stream someday | |
| // and will then increase the serialVersion number again. | |
| // We prepare to be compatible. | |
| fields = new int[FIELD_COUNT]; | |
| isSet = new boolean[FIELD_COUNT]; | |
| areFieldsSet = false; | |
| } | |
| } | |
| } |