/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Imported by CG 20080202 based on Apache Harmony ("enhanced") revision 612718.
*/
package java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.Serializable;
/**
* Calendar is an abstract class which provides the conversion between Dates and
* integer calendar fields, such as the month, year or minute. Subclasses of
* this class implement a specific calendar type, such as the gregorian
* calendar.
*
* @see Date
* @see GregorianCalendar
* @see TimeZone
*/
public abstract class Calendar implements Serializable, Cloneable {
private static final long serialVersionUID = -1807547505821590642L;
/**
* Set to true when the calendar fields have been set from the time, set to
* false when a field is changed and the fields must be recomputed.
*/
protected boolean areFieldsSet;
/**
* An integer array of calendar fields.
*/
protected int[] fields;
/*
* A boolean array. Each element indicates if the corresponding field has
* been set.
*/
protected boolean[] isSet;
/**
* Set to true when the time has been set, set to false when a field is
* changed and the time must be recomputed.
*/
protected boolean isTimeSet;
/**
* The time in milliseconds since January 1, 1970.
*/
protected long time;
transient int lastTimeFieldSet;
transient int lastDateFieldSet;
private boolean lenient;
/*
** TODO [CG 20080206] These values (firstDayOfWeek, minimalDaysInFirstWeek)
** should really be derived from the Locale. Apache Harmoby uses IBM's
** ICU package to do this, but at 4.4 MB that's a bit over the top for us.
*/
private int firstDayOfWeek = 2;
private int minimalDaysInFirstWeek = 1;
private TimeZone zone;
public static final int JANUARY = 0, FEBRUARY = 1, MARCH = 2, APRIL = 3,
MAY = 4, JUNE = 5, JULY = 6, AUGUST = 7, SEPTEMBER = 8,
OCTOBER = 9, NOVEMBER = 10, DECEMBER = 11, UNDECIMBER = 12,
SUNDAY = 1, MONDAY = 2, TUESDAY = 3, WEDNESDAY = 4, THURSDAY = 5,
FRIDAY = 6, SATURDAY = 7;
public static final int ERA = 0, YEAR = 1, MONTH = 2, WEEK_OF_YEAR = 3,
WEEK_OF_MONTH = 4, DATE = 5, DAY_OF_MONTH = 5, DAY_OF_YEAR = 6,
DAY_OF_WEEK = 7, DAY_OF_WEEK_IN_MONTH = 8,
AM_PM = 9, HOUR = 10, HOUR_OF_DAY = 11, MINUTE = 12, SECOND = 13,
MILLISECOND = 14, ZONE_OFFSET = 15, DST_OFFSET = 16,
FIELD_COUNT = 17,
AM = 0, PM = 1;
private static String[] fieldNames = { "ERA=", "YEAR=", "MONTH=", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"WEEK_OF_YEAR=", "WEEK_OF_MONTH=", "DAY_OF_MONTH=", "DAY_OF_YEAR=", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"DAY_OF_WEEK=", "DAY_OF_WEEK_IN_MONTH=", "AM_PM=", "HOUR=", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
"HOUR_OF_DAY", "MINUTE=", "SECOND=", "MILLISECOND=", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"ZONE_OFFSET=", "DST_OFFSET=" }; //$NON-NLS-1$ //$NON-NLS-2$
/**
* Initializes this Calendar instance using the default TimeZone and Locale.
*
*/
protected Calendar() {
this(TimeZone.getDefault(), Locale.getDefault());
}
Calendar(TimeZone timezone) {
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
areFieldsSet = isTimeSet = false;
setLenient(true);
setTimeZone(timezone);
}
/**
* Initializes this Calendar instance using the specified TimeZone and
* Locale.
*
* @param timezone
* the timezone
* @param locale
* the locale
*/
protected Calendar(TimeZone timezone, Locale locale) {
this(timezone);
// TODO: get firstDayOfWeek, minimalDaysInFirstWeek from Locale
}
/**
* Adds the specified amount to a Calendar field.
*
* @param field
* the Calendar field to modify
* @param value
* the amount to add to the field
*
* @exception IllegalArgumentException
* when the specified field is DST_OFFSET or ZONE_OFFSET.
*/
abstract public void add(int field, int value);
/**
* Answers if the Date specified by this Calendar instance is after the Date
* specified by the parameter. The comparison is not dependent on the time
* zones of the Calendars.
*
* @param calendar
* the Calendar instance to compare
* @return true when this Calendar is after calendar, false otherwise
*
* @exception IllegalArgumentException
* when the time is not set and the time cannot be computed
* from the current field values
*/
public boolean after(Object calendar) {
if (!(calendar instanceof Calendar)) {
return false;
}
return getTimeInMillis() > ((Calendar) calendar).getTimeInMillis();
}
/**
* Answers if the Date specified by this Calendar instance is before the
* Date specified by the parameter. The comparison is not dependent on the
* time zones of the Calendars.
*
* @param calendar
* the Calendar instance to compare
* @return true when this Calendar is before calendar, false otherwise
*
* @exception IllegalArgumentException
* when the time is not set and the time cannot be computed
* from the current field values
*/
public boolean before(Object calendar) {
if (!(calendar instanceof Calendar)) {
return false;
}
return getTimeInMillis() < ((Calendar) calendar).getTimeInMillis();
}
/**
* Clears all of the fields of this Calendar. All fields are initialized to
* zero.
*
*/
public final void clear() {
for (int i = 0; i < FIELD_COUNT; i++) {
fields[i] = 0;
isSet[i] = false;
}
areFieldsSet = isTimeSet = false;
}
/**
* Clears the specified field to zero.
*
* @param field
* the field to clear
*/
public final void clear(int field) {
fields[field] = 0;
isSet[field] = false;
areFieldsSet = isTimeSet = false;
}
/**
* Answers a new Calendar with the same properties.
*
* @return a shallow copy of this Calendar
*
* @see java.lang.Cloneable
*/
public Object clone() {
try {
Calendar clone = (Calendar) super.clone();
clone.fields = (int[])fields.clone();
clone.isSet = (boolean[])isSet.clone();
clone.zone = (TimeZone) zone.clone();
return clone;
} catch (CloneNotSupportedException e) {
return null;
}
}
/**
* Computes the time from the fields if the time has not already been set.
* Computes the fields from the time if the fields are not already set.
*
* @exception IllegalArgumentException
* when the time is not set and the time cannot be computed
* from the current field values
*/
protected void complete() {
if (!isTimeSet) {
computeTime();
isTimeSet = true;
}
if (!areFieldsSet) {
computeFields();
areFieldsSet = true;
}
}
/**
* Computes the Calendar fields from the time.
*
*/
protected abstract void computeFields();
/**
* Computes the time from the Calendar fields.
*
* @exception IllegalArgumentException
* when the time cannot be computed from the current field
* values
*/
protected abstract void computeTime();
/**
* Compares the specified object to this Calendar and answer if they are
* equal. The object must be an instance of Calendar and have the same
* properties.
*
* @param object
* the object to compare with this object
* @return true if the specified object is equal to this Calendar, false
* otherwise
*/
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof Calendar)) {
return false;
}
Calendar cal = (Calendar) object;
return getTimeInMillis() == cal.getTimeInMillis()
&& isLenient() == cal.isLenient()
&& getFirstDayOfWeek() == cal.getFirstDayOfWeek()
&& getMinimalDaysInFirstWeek() == cal
.getMinimalDaysInFirstWeek()
&& getTimeZone().equals(cal.getTimeZone());
}
/**
* Gets the value of the specified field after computing the field values
* from the time if required.
*
* @param field
* the field
* @return the value of the specified field
*
* @exception IllegalArgumentException
* when the fields are not set, the time is not set, and the
* time cannot be computed from the current field values
*/
public int get(int field) {
complete();
return fields[field];
}
/**
* Gets the maximum value of the specified field for the current date.
*
* @param field
* the field
* @return the maximum value of the specified field
*/
public int getActualMaximum(int field) {
int value, next;
if (getMaximum(field) == (next = getLeastMaximum(field))) {
return next;
}
complete();
long orgTime = time;
set(field, next);
do {
value = next;
roll(field, true);
next = get(field);
} while (next > value);
time = orgTime;
areFieldsSet = false;
return value;
}
/**
* Gets the minimum value of the specified field for the current date.
*
* @param field
* the field
* @return the minimum value of the specified field
*/
public int getActualMinimum(int field) {
int value, next;
if (getMinimum(field) == (next = getGreatestMinimum(field))) {
return next;
}
complete();
long orgTime = time;
set(field, next);
do {
value = next;
roll(field, false);
next = get(field);
} while (next < value);
time = orgTime;
areFieldsSet = false;
return value;
}
/**
* Gets the list of installed Locales which support Calendar.
*
* @return an array of Locale
*/
public static synchronized Locale[] getAvailableLocales() {
return Locale.getAvailableLocales();
}
/**
* Gets the first day of the week for this Calendar.
*
* @return a Calendar day of the week
*/
public int getFirstDayOfWeek() {
return firstDayOfWeek;
}
/**
* Gets the greatest minimum value of the specified field.
*
* @param field
* the field
* @return the greatest minimum value of the specified field
*/
abstract public int getGreatestMinimum(int field);
/**
* Constructs a new instance of the Calendar subclass appropriate for the
* default Locale.
*
* @return a Calendar subclass instance set to the current date and time in
* the default timezone
*/
public static synchronized Calendar getInstance() {
return new GregorianCalendar();
}
/**
* Constructs a new instance of the Calendar subclass appropriate for the
* specified Locale.
*
* @param locale
* the locale to use
* @return a Calendar subclass instance set to the current date and time
*/
public static synchronized Calendar getInstance(Locale locale) {
return new GregorianCalendar(locale);
}
/**
* Constructs a new instance of the Calendar subclass appropriate for the
* default Locale, using the specified TimeZone.
*
* @param timezone
* the timezone to use
* @return a Calendar subclass instance set to the current date and time in
* the specified timezone
*/
public static synchronized Calendar getInstance(TimeZone timezone) {
return new GregorianCalendar(timezone);
}
/**
* Constructs a new instance of the Calendar subclass appropriate for the
* specified Locale.
*
* @param timezone
* the timezone to use
* @param locale
* the locale to use
* @return a Calendar subclass instance set to the current date and time in
* the specified timezone
*/
public static synchronized Calendar getInstance(TimeZone timezone,
Locale locale) {
return new GregorianCalendar(timezone, locale);
}
/**
* Gets the smallest maximum value of the specified field.
*
* @param field
* the field
* @return the smallest maximum value of the specified field
*/
abstract public int getLeastMaximum(int field);
/**
* Gets the greatest maximum value of the specified field.
*
* @param field
* the field
* @return the greatest maximum value of the specified field
*/
abstract public int getMaximum(int field);
/**
* Gets the minimal days in the first week of the year.
*
* @return the minimal days in the first week of the year
*/
public int getMinimalDaysInFirstWeek() {
return minimalDaysInFirstWeek;
}
/**
* Gets the smallest minimum value of the specified field.
*
* @param field
* the field
* @return the smallest minimum value of the specified field
*/
abstract public int getMinimum(int field);
/**
* Gets the time of this Calendar as a Date object.
*
* @return a new Date initialized to the time of this Calendar
*
* @exception IllegalArgumentException
* when the time is not set and the time cannot be computed
* from the current field values
*/
public final Date getTime() {
return new Date(getTimeInMillis());
}
/**
* Computes the time from the fields if required and answers the time.
*
* @return the time of this Calendar
*
* @exception IllegalArgumentException
* when the time is not set and the time cannot be computed
* from the current field values
*/
public long getTimeInMillis() {
if (!isTimeSet) {
computeTime();
isTimeSet = true;
}
return time;
}
/**
* Gets the timezone of this Calendar.
*
* @return the timezone used by this Calendar
*/
public TimeZone getTimeZone() {
return zone;
}
/**
* Answers an integer hash code for the receiver. Objects which are equal
* answer the same value for this method.
*
* @return the receiver's hash
*
* @see #equals
*/
public int hashCode() {
return (isLenient() ? 1237 : 1231) + getFirstDayOfWeek()
+ getMinimalDaysInFirstWeek() + getTimeZone().hashCode();
}
/**
* Gets the value of the specified field without recomputing.
*
* @param field
* the field
* @return the value of the specified field
*/
protected final int internalGet(int field) {
return fields[field];
}
/**
* Answers if this Calendar accepts field values which are outside the valid
* range for the field.
*
* @return true if this Calendar is lenient, false otherwise
*/
public boolean isLenient() {
return lenient;
}
/**
* Answers if the specified field is set.
*
* @param field
* a calendar field
* @return true if the specified field is set, false otherwise
*/
public final boolean isSet(int field) {
return isSet[field];
}
/**
* Adds the specified amount the specified field and wrap the value of the
* field when it goes beyond the maximum or minimum value for the current
* date. Other fields will be adjusted as required to maintain a consistent
* date.
*
* @param field
* the field to roll
* @param value
* the amount to add
*/
public void roll(int field, int value) {
boolean increment = value >= 0;
int count = increment ? value : -value;
for (int i = 0; i < count; i++) {
roll(field, increment);
}
}
/**
* Increment or decrement the specified field and wrap the value of the
* field when it goes beyond the maximum or minimum value for the current
* date. Other fields will be adjusted as required to maintain a consistent
* date.
*
* @param field
* the field to roll
* @param increment
* true to increment the field, false to decrement
*/
abstract public void roll(int field, boolean increment);
/**
* Sets a field to the specified value.
*
* @param field
* the Calendar field to modify
* @param value
* the value
*/
public void set(int field, int value) {
fields[field] = value;
isSet[field] = true;
areFieldsSet = isTimeSet = false;
if (field > MONTH && field < AM_PM) {
lastDateFieldSet = field;
}
if (field == HOUR || field == HOUR_OF_DAY) {
lastTimeFieldSet = field;
}
if (field == AM_PM) {
lastTimeFieldSet = HOUR;
}
}
/**
* Sets the year, month and day of the month fields.
*
* @param year
* the year
* @param month
* the month
* @param day
* the day of the month
*/
public final void set(int year, int month, int day) {
set(YEAR, year);
set(MONTH, month);
set(DATE, day);
}
/**
* Sets the year, month, day of the month, hour of day and minute fields.
*
* @param year
* the year
* @param month
* the month
* @param day
* the day of the month
* @param hourOfDay
* the hour of day
* @param minute
* the minute
*/
public final void set(int year, int month, int day, int hourOfDay,
int minute) {
set(year, month, day);
set(HOUR_OF_DAY, hourOfDay);
set(MINUTE, minute);
}
/**
* Sets the year, month, day of the month, hour of day, minute and second
* fields.
*
* @param year
* the year
* @param month
* the month
* @param day
* the day of the month
* @param hourOfDay
* the hour of day
* @param minute
* the minute
* @param second
* the second
*/
public final void set(int year, int month, int day, int hourOfDay,
int minute, int second) {
set(year, month, day, hourOfDay, minute);
set(SECOND, second);
}
/**
* Sets the first day of the week for this Calendar.
*
* @param value
* a Calendar day of the week
*/
public void setFirstDayOfWeek(int value) {
firstDayOfWeek = value;
}
/**
* Sets this Calendar to accept field values which are outside the valid
* range for the field.
*
* @param value
* a boolean value
*/
public void setLenient(boolean value) {
lenient = value;
}
/**
* Sets the minimal days in the first week of the year.
*
* @param value
* the minimal days in the first week of the year
*/
public void setMinimalDaysInFirstWeek(int value) {
minimalDaysInFirstWeek = value;
}
/**
* Sets the time of this Calendar.
*
* @param date
* a Date object
*/
public final void setTime(Date date) {
setTimeInMillis(date.getTime());
}
/**
* Sets the time of this Calendar.
*
* @param milliseconds
* the time as the number of milliseconds since Jan. 1, 1970
*/
public void setTimeInMillis(long milliseconds) {
if (!isTimeSet || !areFieldsSet || time != milliseconds) {
time = milliseconds;
isTimeSet = true;
areFieldsSet = false;
complete();
}
}
/**
* Sets the timezone used by this Calendar.
*
* @param timezone
* a TimeZone
*/
public void setTimeZone(TimeZone timezone) {
zone = timezone;
areFieldsSet = false;
}
/**
* Answers the string representation of this Calendar.
*
* @return the string representation of this Calendar
*/
public String toString() {
StringBuffer result = new StringBuffer(getClass().getName() + "[time="
+ (isTimeSet ? String.valueOf(time) : "?")
+ ",areFieldsSet="
+ areFieldsSet
+ // ",areAllFieldsSet=" + areAllFieldsSet +
",lenient=" + lenient + ",zone=" + zone + ",firstDayOfWeek="
+ firstDayOfWeek + ",minimalDaysInFirstWeek="
+ minimalDaysInFirstWeek);
for (int i = 0; i < FIELD_COUNT; i++) {
result.append(',');
result.append(fieldNames[i]);
result.append('=');
if (isSet[i]) {
result.append(fields[i]);
} else {
result.append('?');
}
}
result.append(']');
return result.toString();
}
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("areFieldsSet", Boolean.TYPE),
new ObjectStreamField("fields", int[].class),
new ObjectStreamField("firstDayOfWeek", Integer.TYPE),
new ObjectStreamField("isSet", boolean[].class),
new ObjectStreamField("isTimeSet", Boolean.TYPE),
new ObjectStreamField("lenient", Boolean.TYPE),
new ObjectStreamField("minimalDaysInFirstWeek", Integer.TYPE),
new ObjectStreamField("nextStamp", Integer.TYPE),
new ObjectStreamField("serialVersionOnStream", Integer.TYPE),
new ObjectStreamField("time", Long.TYPE),
new ObjectStreamField("zone", TimeZone.class), };
private void writeObject(ObjectOutputStream stream) throws IOException {
complete();
ObjectOutputStream.PutField putFields = stream.putFields();
putFields.put("areFieldsSet", areFieldsSet);
putFields.put("fields", this.fields);
putFields.put("firstDayOfWeek", firstDayOfWeek);
putFields.put("isSet", isSet);
putFields.put("isTimeSet", isTimeSet);
putFields.put("lenient", lenient);
putFields.put("minimalDaysInFirstWeek", minimalDaysInFirstWeek);
putFields.put("nextStamp", 2 /* MINIMUM_USER_STAMP */);
putFields.put("serialVersionOnStream", 1);
putFields.put("time", time);
putFields.put("zone", zone);
stream.writeFields();
}
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
ObjectInputStream.GetField readFields = stream.readFields();
areFieldsSet = readFields.get("areFieldsSet", false);
this.fields = (int[]) readFields.get("fields", null);
firstDayOfWeek = readFields.get("firstDayOfWeek", Calendar.SUNDAY);
isSet = (boolean[]) readFields.get("isSet", null);
isTimeSet = readFields.get("isTimeSet", false);
lenient = readFields.get("lenient", true);
minimalDaysInFirstWeek = readFields.get("minimalDaysInFirstWeek", 1);
time = readFields.get("time", 0L);
zone = (TimeZone) readFields.get("zone", null);
}
}