| /* 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; |
| } |
| } |
| } |