/*
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* (C) Copyright Taligent, Inc. 1996-1998 - All Rights Reserved
* (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
*
* The original version of this source code and documentation is copyrighted
* and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
* materials are provided under terms of a License Agreement between Taligent
* and Sun. This technology is protected by multiple US and International
* patents. This notice and attribution to Taligent may not be removed.
* Taligent is a registered trademark of Taligent, Inc.
*
*/
package java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PermissionCollection;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.BuddhistCalendar;
import sun.util.calendar.ZoneInfo;
import sun.util.locale.provider.CalendarDataUtility;
/**
* The Calendar
class is an abstract class that provides methods
* for converting between a specific instant in time and a set of {@link
* #fields calendar fields} such as YEAR
, MONTH
,
* DAY_OF_MONTH
, HOUR
, and so on, and for
* manipulating the calendar fields, such as getting the date of the next
* week. An instant in time can be represented by a millisecond value that is
* an offset from the Epoch, January 1, 1970
* 00:00:00.000 GMT (Gregorian).
*
*
The class also provides additional fields and methods for
* implementing a concrete calendar system outside the package. Those
* fields and methods are defined as protected
.
*
*
* Like other locale-sensitive classes, Calendar
provides a
* class method, getInstance
, for getting a generally useful
* object of this type. Calendar
's getInstance
method
* returns a Calendar
object whose
* calendar fields have been initialized with the current date and time:
*
** ** Calendar rightNow = Calendar.getInstance(); **
A Calendar
object can produce all the calendar field values
* needed to implement the date-time formatting for a particular language and
* calendar style (for example, Japanese-Gregorian, Japanese-Traditional).
* Calendar
defines the range of values returned by
* certain calendar fields, as well as their meaning. For example,
* the first month of the calendar system has value MONTH ==
* JANUARY
for all calendars. Other values are defined by the
* concrete subclass, such as ERA
. See individual field
* documentation and subclass documentation for details.
*
*
The calendar field values can be set by calling the set
* methods. Any field values set in a Calendar
will not be
* interpreted until it needs to calculate its time value (milliseconds from
* the Epoch) or values of the calendar fields. Calling the
* get
, getTimeInMillis
, getTime
,
* add
and roll
involves such calculation.
*
*
Calendar
has two modes for interpreting the calendar
* fields, lenient and non-lenient. When a
* Calendar
is in lenient mode, it accepts a wider range of
* calendar field values than it produces. When a Calendar
* recomputes calendar field values for return by get()
, all of
* the calendar fields are normalized. For example, a lenient
* GregorianCalendar
interprets MONTH == JANUARY
,
* DAY_OF_MONTH == 32
as February 1.
*
When a Calendar
is in non-lenient mode, it throws an
* exception if there is any inconsistency in its calendar fields. For
* example, a GregorianCalendar
always produces
* DAY_OF_MONTH
values between 1 and the length of the month. A
* non-lenient GregorianCalendar
throws an exception upon
* calculating its time or calendar field values if any out-of-range field
* value has been set.
*
*
Calendar
defines a locale-specific seven day week using two
* parameters: the first day of the week and the minimal days in first week
* (from 1 to 7). These numbers are taken from the locale resource data when a
* Calendar
is constructed. They may also be specified explicitly
* through the methods for setting their values.
*
* When setting or getting the WEEK_OF_MONTH
or
* WEEK_OF_YEAR
fields, Calendar
must determine the
* first week of the month or year as a reference point. The first week of a
* month or year is defined as the earliest seven day period beginning on
* getFirstDayOfWeek()
and containing at least
* getMinimalDaysInFirstWeek()
days of that month or year. Weeks
* numbered ..., -1, 0 precede the first week; weeks numbered 2, 3,... follow
* it. Note that the normalized numbering returned by get()
may be
* different. For example, a specific Calendar
subclass may
* designate the week before week 1 of a year as week n
of
* the previous year.
*
*
Calendar
will resolve
* calendar field values to determine the date and time in the
* following way.
*
* If there is any conflict in calendar field values,
* Calendar
gives priorities to calendar fields that have been set
* more recently. The following are the default combinations of the
* calendar fields. The most recent combination, as determined by the
* most recently set single field, will be used.
*
*
** * For the time of day fields: ** YEAR + MONTH + DAY_OF_MONTH * YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK * YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK * YEAR + DAY_OF_YEAR * YEAR + DAY_OF_WEEK + WEEK_OF_YEAR *
** ** HOUR_OF_DAY * AM_PM + HOUR *
If there are any calendar fields whose values haven't been set in the selected
* field combination, Calendar
uses their default values. The default
* value of each field may vary by concrete calendar systems. For example, in
* GregorianCalendar
, the default of a field is the same as that
* of the start of the Epoch: i.e., YEAR = 1970
, MONTH =
* JANUARY
, DAY_OF_MONTH = 1
, etc.
*
*
* Note: There are certain possible ambiguities in * interpretation of certain singular times, which are resolved in the * following ways: *
* The date or time format strings are not part of the definition of a * calendar, as those must be modifiable or overridable by the user at * runtime. Use {@link DateFormat} * to format dates. * *
set()
, add()
, and roll()
.
*
* set(f, value)
changes calendar field
* f
to value
. In addition, it sets an
* internal member variable to indicate that calendar field f
has
* been changed. Although calendar field f
is changed immediately,
* the calendar's time value in milliseconds is not recomputed until the next call to
* get()
, getTime()
, getTimeInMillis()
,
* add()
, or roll()
is made. Thus, multiple calls to
* set()
do not trigger multiple, unnecessary
* computations. As a result of changing a calendar field using
* set()
, other calendar fields may also change, depending on the
* calendar field, the calendar field value, and the calendar system. In addition,
* get(f)
will not necessarily return value
set by
* the call to the set
method
* after the calendar fields have been recomputed. The specifics are determined by
* the concrete calendar class.
Example: Consider a GregorianCalendar
* originally set to August 31, 1999. Calling set(Calendar.MONTH,
* Calendar.SEPTEMBER)
sets the date to September 31,
* 1999. This is a temporary internal representation that resolves to
* October 1, 1999 if getTime()
is then called. However, a
* call to set(Calendar.DAY_OF_MONTH, 30)
before the call to
* getTime()
sets the date to September 30, 1999, since
* no recomputation occurs after set()
itself.
add(f, delta)
adds delta
* to field f
. This is equivalent to calling set(f,
* get(f) + delta)
with two adjustments:
** *Add rule 1. The value of field
* *f
* after the call minus the value of fieldf
before the * call isdelta
, modulo any overflow that has occurred in * fieldf
. Overflow occurs when a field value exceeds its * range and, as a result, the next larger field is incremented or * decremented and the field value is adjusted back into its range.Add rule 2. If a smaller field is expected to be * invariant, but it is impossible for it to be equal to its * prior value because of changes in its minimum or maximum after field *
*f
is changed or other constraints, such as time zone * offset changes, then its value is adjusted to be as close * as possible to its expected value. A smaller field represents a * smaller unit of time.HOUR
is a smaller field than *DAY_OF_MONTH
. No adjustment is made to smaller fields * that are not expected to be invariant. The calendar system * determines what fields are expected to be invariant.
In addition, unlike set()
, add()
forces
* an immediate recomputation of the calendar's milliseconds and all
* fields.
Example: Consider a GregorianCalendar
* originally set to August 31, 1999. Calling add(Calendar.MONTH,
* 13)
sets the calendar to September 30, 2000. Add rule
* 1 sets the MONTH
field to September, since
* adding 13 months to August gives September of the next year. Since
* DAY_OF_MONTH
cannot be 31 in September in a
* GregorianCalendar
, add rule 2 sets the
* DAY_OF_MONTH
to 30, the closest possible value. Although
* it is a smaller field, DAY_OF_WEEK
is not adjusted by
* rule 2, since it is expected to change when the month changes in a
* GregorianCalendar
.
roll(f, delta)
adds
* delta
to field f
without changing larger
* fields. This is equivalent to calling add(f, delta)
with
* the following adjustment:
** *Roll rule. Larger fields are unchanged after the * call. A larger field represents a larger unit of * time.
*DAY_OF_MONTH
is a larger field than *HOUR
.
Example: See {@link java.util.GregorianCalendar#roll(int, int)}. * *
Usage model. To motivate the behavior of
* add()
and roll()
, consider a user interface
* component with increment and decrement buttons for the month, day, and
* year, and an underlying GregorianCalendar
. If the
* interface reads January 31, 1999 and the user presses the month
* increment button, what should it read? If the underlying
* implementation uses set()
, it might read March 3, 1999. A
* better result would be February 28, 1999. Furthermore, if the user
* presses the month increment button again, it should read March 31,
* 1999, not March 28, 1999. By saving the original date and using either
* add()
or roll()
, depending on whether larger
* fields should be affected, the user interface can behave as most users
* will intuitively expect.
get
and set
indicating the
* era, e.g., AD or BC in the Julian calendar. This is a calendar-specific
* value; see subclass documentation.
*
* @see GregorianCalendar#AD
* @see GregorianCalendar#BC
*/
public final static int ERA = 0;
/**
* Field number for get
and set
indicating the
* year. This is a calendar-specific value; see subclass documentation.
*/
public final static int YEAR = 1;
/**
* Field number for get
and set
indicating the
* month. This is a calendar-specific value. The first month of
* the year in the Gregorian and Julian calendars is
* JANUARY
which is 0; the last depends on the number
* of months in a year.
*
* @see #JANUARY
* @see #FEBRUARY
* @see #MARCH
* @see #APRIL
* @see #MAY
* @see #JUNE
* @see #JULY
* @see #AUGUST
* @see #SEPTEMBER
* @see #OCTOBER
* @see #NOVEMBER
* @see #DECEMBER
* @see #UNDECIMBER
*/
public final static int MONTH = 2;
/**
* Field number for get
and set
indicating the
* week number within the current year. The first week of the year, as
* defined by getFirstDayOfWeek()
and
* getMinimalDaysInFirstWeek()
, has value 1. Subclasses define
* the value of WEEK_OF_YEAR
for days before the first week of
* the year.
*
* @see #getFirstDayOfWeek
* @see #getMinimalDaysInFirstWeek
*/
public final static int WEEK_OF_YEAR = 3;
/**
* Field number for get
and set
indicating the
* week number within the current month. The first week of the month, as
* defined by getFirstDayOfWeek()
and
* getMinimalDaysInFirstWeek()
, has value 1. Subclasses define
* the value of WEEK_OF_MONTH
for days before the first week of
* the month.
*
* @see #getFirstDayOfWeek
* @see #getMinimalDaysInFirstWeek
*/
public final static int WEEK_OF_MONTH = 4;
/**
* Field number for get
and set
indicating the
* day of the month. This is a synonym for DAY_OF_MONTH
.
* The first day of the month has value 1.
*
* @see #DAY_OF_MONTH
*/
public final static int DATE = 5;
/**
* Field number for get
and set
indicating the
* day of the month. This is a synonym for DATE
.
* The first day of the month has value 1.
*
* @see #DATE
*/
public final static int DAY_OF_MONTH = 5;
/**
* Field number for get
and set
indicating the day
* number within the current year. The first day of the year has value 1.
*/
public final static int DAY_OF_YEAR = 6;
/**
* Field number for get
and set
indicating the day
* of the week. This field takes values SUNDAY
,
* MONDAY
, TUESDAY
, WEDNESDAY
,
* THURSDAY
, FRIDAY
, and SATURDAY
.
*
* @see #SUNDAY
* @see #MONDAY
* @see #TUESDAY
* @see #WEDNESDAY
* @see #THURSDAY
* @see #FRIDAY
* @see #SATURDAY
*/
public final static int DAY_OF_WEEK = 7;
/**
* Field number for get
and set
indicating the
* ordinal number of the day of the week within the current month. Together
* with the DAY_OF_WEEK
field, this uniquely specifies a day
* within a month. Unlike WEEK_OF_MONTH
and
* WEEK_OF_YEAR
, this field's value does not depend on
* getFirstDayOfWeek()
or
* getMinimalDaysInFirstWeek()
. DAY_OF_MONTH 1
* through 7
always correspond to DAY_OF_WEEK_IN_MONTH
* 1
; 8
through 14
correspond to
* DAY_OF_WEEK_IN_MONTH 2
, and so on.
* DAY_OF_WEEK_IN_MONTH 0
indicates the week before
* DAY_OF_WEEK_IN_MONTH 1
. Negative values count back from the
* end of the month, so the last Sunday of a month is specified as
* DAY_OF_WEEK = SUNDAY, DAY_OF_WEEK_IN_MONTH = -1
. Because
* negative values count backward they will usually be aligned differently
* within the month than positive values. For example, if a month has 31
* days, DAY_OF_WEEK_IN_MONTH -1
will overlap
* DAY_OF_WEEK_IN_MONTH 5
and the end of 4
.
*
* @see #DAY_OF_WEEK
* @see #WEEK_OF_MONTH
*/
public final static int DAY_OF_WEEK_IN_MONTH = 8;
/**
* Field number for get
and set
indicating
* whether the HOUR
is before or after noon.
* E.g., at 10:04:15.250 PM the AM_PM
is PM
.
*
* @see #AM
* @see #PM
* @see #HOUR
*/
public final static int AM_PM = 9;
/**
* Field number for get
and set
indicating the
* hour of the morning or afternoon. HOUR
is used for the
* 12-hour clock (0 - 11). Noon and midnight are represented by 0, not by 12.
* E.g., at 10:04:15.250 PM the HOUR
is 10.
*
* @see #AM_PM
* @see #HOUR_OF_DAY
*/
public final static int HOUR = 10;
/**
* Field number for get
and set
indicating the
* hour of the day. HOUR_OF_DAY
is used for the 24-hour clock.
* E.g., at 10:04:15.250 PM the HOUR_OF_DAY
is 22.
*
* @see #HOUR
*/
public final static int HOUR_OF_DAY = 11;
/**
* Field number for get
and set
indicating the
* minute within the hour.
* E.g., at 10:04:15.250 PM the MINUTE
is 4.
*/
public final static int MINUTE = 12;
/**
* Field number for get
and set
indicating the
* second within the minute.
* E.g., at 10:04:15.250 PM the SECOND
is 15.
*/
public final static int SECOND = 13;
/**
* Field number for get
and set
indicating the
* millisecond within the second.
* E.g., at 10:04:15.250 PM the MILLISECOND
is 250.
*/
public final static int MILLISECOND = 14;
/**
* Field number for get
and set
* indicating the raw offset from GMT in milliseconds.
*
* This field reflects the correct GMT offset value of the time
* zone of this Calendar
if the
* TimeZone
implementation subclass supports
* historical GMT offset changes.
*/
public final static int ZONE_OFFSET = 15;
/**
* Field number for get
and set
indicating the
* daylight saving offset in milliseconds.
*
* This field reflects the correct daylight saving offset value of
* the time zone of this There are two ways to set a {@code Calendar} to a date-time value. One
* is to set the instant parameter to a millisecond offset from the Epoch. The other is to set individual
* field parameters, such as {@link Calendar#YEAR YEAR}, to their desired
* values. These two ways can't be mixed. Trying to set both the instant and
* individual fields will cause an {@link IllegalStateException} to be
* thrown. However, it is permitted to override previous values of the
* instant or field parameters.
*
* If no enough field parameters are given for determining date and/or
* time, calendar specific default values are used when building a
* {@code Calendar}. For example, if the {@link Calendar#YEAR YEAR} value
* isn't given for the Gregorian calendar, 1970 will be used. If there are
* any conflicts among field parameters, the resolution rules are applied.
* Therefore, the order of field setting matters.
*
* In addition to the date-time parameters,
* the {@linkplain #setLocale(Locale) locale},
* {@linkplain #setTimeZone(TimeZone) time zone},
* {@linkplain #setWeekDefinition(int, int) week definition}, and
* {@linkplain #setLenient(boolean) leniency mode} parameters can be set.
*
* Examples
* The following are sample usages. Sample code assumes that the
* {@code Calendar} constants are statically imported.
*
* The following code produces a {@code Calendar} with date 2012-12-31
* (Gregorian) because Monday is the first day of a week with the ISO 8601
* compatible week parameters.
* The following code produces a Japanese {@code Calendar} with date
* 1989-01-08 (Gregorian), assuming that the default {@link Calendar#ERA ERA}
* is Heisei that started on that day.
* If the specified calendar doesn't support week dates, the
* {@link #build() build} method will throw an {@link IllegalArgumentException}.
*
* @param weekYear the week year
* @param weekOfYear the week number based on {@code weekYear}
* @param dayOfWeek the day of week value: one of the constants
* for the {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} field:
* {@link Calendar#SUNDAY SUNDAY}, ..., {@link Calendar#SATURDAY SATURDAY}.
* @return this {@code Calendar.Builder}
* @see Calendar#setWeekDate(int, int, int)
* @see Calendar#isWeekDateSupported()
*/
public Builder setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) {
allocateFields();
internalSet(WEEK_YEAR, weekYear);
internalSet(WEEK_OF_YEAR, weekOfYear);
internalSet(DAY_OF_WEEK, dayOfWeek);
return this;
}
/**
* Sets the time zone parameter to the given {@code zone}. If no time
* zone parameter is given to this {@code Caledar.Builder}, the
* {@linkplain TimeZone#getDefault() default
* In addition to the available calendar types returned by the
* {@link Calendar#getAvailableCalendarTypes() Calendar.getAvailableCalendarTypes}
* method, {@code "gregorian"} and {@code "iso8601"} as aliases of
* {@code "gregory"} can be used with this method.
*
* @param type the calendar type
* @return this {@code Calendar.Builder}
* @throws NullPointerException if {@code type} is {@code null}
* @throws IllegalArgumentException if {@code type} is unknown
* @throws IllegalStateException if another calendar type has already been set
* @see Calendar#getCalendarType()
* @see Calendar#getAvailableCalendarTypes()
*/
public Builder setCalendarType(String type) {
if (type.equals("gregorian")) { // NPE if type == null
type = "gregory";
}
if (!Calendar.getAvailableCalendarTypes().contains(type)
&& !type.equals("iso8601")) {
throw new IllegalArgumentException("unknown calendar type: " + type);
}
if (this.type == null) {
this.type = type;
} else {
if (!this.type.equals(type)) {
throw new IllegalStateException("calendar type override");
}
}
return this;
}
/**
* Sets the locale parameter to the given {@code locale}. If no locale
* is given to this {@code Calendar.Builder}, the {@linkplain
* Locale#getDefault(Locale.Category) default If no calendar type is explicitly given by a call to the
* {@link #setCalendarType(String) setCalendarType} method,
* the {@code Locale} value is used to determine what type of
* {@code Calendar} to be built.
*
* If no week definition parameters are explicitly given by a call to
* the {@link #setWeekDefinition(int,int) setWeekDefinition} method, the
* {@code Locale}'s default values are used.
*
* @param locale the {@link Locale}
* @throws NullPointerException if {@code locale} is {@code null}
* @return this {@code Calendar.Builder}
* @see Calendar#getInstance(Locale)
*/
public Builder setLocale(Locale locale) {
if (locale == null) {
throw new NullPointerException();
}
this.locale = locale;
return this;
}
/**
* Sets the week definition parameters to the values given by
* {@code firstDayOfWeek} and {@code minimalDaysInFirstWeek} that are
* used to determine the first
* week of a year. The parameters given by this method have
* precedence over the default values given by the
* {@linkplain #setLocale(Locale) locale}.
*
* @param firstDayOfWeek the first day of a week; one of
* {@link Calendar#SUNDAY} to {@link Calendar#SATURDAY}
* @param minimalDaysInFirstWeek the minimal number of days in the first
* week (1..7)
* @return this {@code Calendar.Builder}
* @throws IllegalArgumentException if {@code firstDayOfWeek} or
* {@code minimalDaysInFirstWeek} is invalid
* @see Calendar#getFirstDayOfWeek()
* @see Calendar#getMinimalDaysInFirstWeek()
*/
public Builder setWeekDefinition(int firstDayOfWeek, int minimalDaysInFirstWeek) {
if (!isValidWeekParameter(firstDayOfWeek)
|| !isValidWeekParameter(minimalDaysInFirstWeek)) {
throw new IllegalArgumentException();
}
this.firstDayOfWeek = firstDayOfWeek;
this.minimalDaysInFirstWeek = minimalDaysInFirstWeek;
return this;
}
/**
* Returns a {@code Calendar} built from the parameters set by the
* setter methods. The calendar type given by the {@link #setCalendarType(String)
* setCalendarType} method or the {@linkplain #setLocale(Locale) locale} is
* used to determine what {@code Calendar} to be created. If no explicit
* calendar type is given, the locale's default calendar is created.
*
* If the calendar type is {@code "iso8601"}, the
* {@linkplain GregorianCalendar#setGregorianChange(Date) Gregorian change date}
* of a {@link GregorianCalendar} is set to {@code Date(Long.MIN_VALUE)}
* to be the proleptic Gregorian calendar. Its week definition
* parameters are also set to be compatible
* with the ISO 8601 standard. Note that the
* {@link GregorianCalendar#getCalendarType() getCalendarType} method of
* a {@code GregorianCalendar} created with {@code "iso8601"} returns
* {@code "gregory"}.
*
* The default values are used for locale and time zone if these
* parameters haven't been given explicitly.
*
* Any out of range field values are either normalized in lenient
* mode or detected as an invalid value in non-lenient mode.
*
* @return a {@code Calendar} built with parameters of this {@code
* Calendar.Builder}
* @throws IllegalArgumentException if the calendar type is unknown, or
* if any invalid field values are given in non-lenient mode, or
* if a week date is given for the calendar type that doesn't
* support week dates.
* @see Calendar#getInstance(TimeZone, Locale)
* @see Locale#getDefault(Locale.Category)
* @see TimeZone#getDefault()
*/
public Calendar build() {
if (locale == null) {
locale = Locale.getDefault();
}
if (zone == null) {
zone = TimeZone.getDefault();
}
Calendar cal;
if (type == null) {
type = locale.getUnicodeLocaleType("ca");
}
if (type == null) {
if (locale.getCountry() == "TH"
&& locale.getLanguage() == "th") {
type = "buddhist";
} else {
type = "gregory";
}
}
switch (type) {
case "gregory":
cal = new GregorianCalendar(zone, locale, true);
break;
case "iso8601":
GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);
// make gcal a proleptic Gregorian
gcal.setGregorianChange(new Date(Long.MIN_VALUE));
// and week definition to be compatible with ISO 8601
setWeekDefinition(MONDAY, 4);
cal = gcal;
break;
case "buddhist":
cal = new BuddhistCalendar(zone, locale);
cal.clear();
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, locale, true);
break;
default:
throw new IllegalArgumentException("unknown calendar type: " + type);
}
cal.setLenient(lenient);
if (firstDayOfWeek != 0) {
cal.setFirstDayOfWeek(firstDayOfWeek);
cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
}
if (isInstantSet()) {
cal.setTimeInMillis(instant);
cal.complete();
return cal;
}
if (fields != null) {
boolean weekDate = isSet(WEEK_YEAR)
&& fields[WEEK_YEAR] > fields[YEAR];
if (weekDate && !cal.isWeekDateSupported()) {
throw new IllegalArgumentException("week date is unsupported by " + type);
}
// Set the fields from the min stamp to the max stamp so that
// the fields resolution works in the Calendar.
for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
for (int index = 0; index <= maxFieldIndex; index++) {
if (fields[index] == stamp) {
cal.set(index, fields[NFIELDS + index]);
break;
}
}
}
if (weekDate) {
int weekOfYear = isSet(WEEK_OF_YEAR) ? fields[NFIELDS + WEEK_OF_YEAR] : 1;
int dayOfWeek = isSet(DAY_OF_WEEK)
? fields[NFIELDS + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
cal.setWeekDate(fields[NFIELDS + WEEK_YEAR], weekOfYear, dayOfWeek);
}
cal.complete();
}
return cal;
}
private void allocateFields() {
if (fields == null) {
fields = new int[NFIELDS * 2];
nextStamp = MINIMUM_USER_STAMP;
maxFieldIndex = -1;
}
}
private void internalSet(int field, int value) {
fields[field] = nextStamp++;
if (nextStamp < 0) {
throw new IllegalStateException("stamp counter overflow");
}
fields[NFIELDS + field] = value;
if (field > maxFieldIndex && field < WEEK_YEAR) {
maxFieldIndex = field;
}
}
private boolean isInstantSet() {
return nextStamp == COMPUTED;
}
private boolean isSet(int index) {
return fields != null && fields[index] > UNSET;
}
private boolean isValidWeekParameter(int value) {
return value > 0 && value <= 7;
}
}
/**
* Constructs a Calendar with the default time zone
* and locale.
* @see TimeZone#getDefault
*/
protected Calendar()
{
this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
sharedZone = true;
}
/**
* Constructs a calendar with the specified time zone and locale.
*
* @param zone the time zone to use
* @param aLocale the locale for the week data
*/
protected Calendar(TimeZone zone, Locale aLocale)
{
fields = new int[FIELD_COUNT];
isSet = new boolean[FIELD_COUNT];
stamp = new int[FIELD_COUNT];
this.zone = zone;
setWeekCountData(aLocale);
}
/**
* Gets a calendar using the default time zone and locale. The
*
* Note: Calling The {@link #HOUR_OF_DAY}, {@link #HOUR} and {@link #AM_PM}
* fields are handled independently and the the resolution rule for the time of
* day is applied. Clearing one of the fields doesn't reset
* the hour of day value of this For example, if this The default implementation supports the calendar fields for
* which a {@link DateFormatSymbols} has names in the given
* Narrow names may not be unique due to use of single characters,
* such as "S" for Sunday and Saturday. In that case narrow names are not
* included in the returned {@code Map}.
*
* The values of other calendar fields may be taken into
* account to determine a set of display names. For example, if
* this {@code Calendar} is a lunisolar calendar system and
* the year value given by the {@link #YEAR} field has a leap
* month, this method would return month names containing the leap
* month name, and month names are mapped to their values specific
* for the year.
*
* The default implementation supports display names contained in
* a {@link DateFormatSymbols}. For example, if {@code field}
* is {@link #MONTH} and {@code style} is {@link
* #ALL_STYLES}, this method returns a {@code Map} containing
* all strings returned by {@link DateFormatSymbols#getShortMonths()}
* and {@link DateFormatSymbols#getMonths()}.
*
* @param field
* the calendar field for which the display names are returned
* @param style
* the style applied to the string representation; one of {@link
* #SHORT_FORMAT} ({@link #SHORT}), {@link #SHORT_STANDALONE},
* {@link #LONG_FORMAT} ({@link #LONG}), {@link #LONG_STANDALONE},
* {@link #NARROW_FORMAT}, or {@link #NARROW_STANDALONE}
* @param locale
* the locale for the display names
* @return a {@code Map} containing all display names in
* {@code style} and {@code locale} and their
* field values, or {@code null} if no display names
* are defined for {@code field}
* @exception IllegalArgumentException
* if {@code field} or {@code style} is invalid,
* or if this {@code Calendar} is non-lenient and any
* of the calendar fields have invalid values
* @exception NullPointerException
* if {@code locale} is null
* @since 1.6
*/
public Map This method supports the calendar fields resolution as described in
* the class description. If the bit mask for a given field is on and its
* field has not been set (i.e., The default implementation of this method returns the class name of
* this {@code Calendar} instance. Any subclasses that implement
* LDML-defined calendar systems should override this method to return
* appropriate calendar types.
*
* @return the LDML-defined calendar type or the class name of this
* {@code Calendar} instance
* @since 1.8
* @see Locale extensions
* @see Locale.Builder#setLocale(Locale)
* @see Locale.Builder#setUnicodeLocaleKeyword(String, String)
*/
public String getCalendarType() {
return this.getClass().getName();
}
/**
* Compares this The Use the {@link #compareTo(Calendar) compareTo} method to
* compare only the time values.
*
* @param obj the object to compare with.
* @return roll(Calendar.DATE, true).
* When rolling on the year or Calendar.YEAR field, it will roll the year
* value in the range between 1 and the value returned by calling
* NOTE: This default implementation on The default implementation of this method returns {@code false}.
*
* @return {@code true} if this {@code Calendar} supports week dates;
* {@code false} otherwise.
* @see #getWeekYear()
* @see #setWeekDate(int,int,int)
* @see #getWeeksInWeekYear()
* @since 1.7
*/
public boolean isWeekDateSupported() {
return false;
}
/**
* Returns the week year represented by this {@code Calendar}. The
* week year is in sync with the week cycle. The {@linkplain
* #getFirstDayOfWeek() first day of the first week} is the first
* day of the week year.
*
* The default implementation of this method throws an
* {@link UnsupportedOperationException}.
*
* @return the week year of this {@code Calendar}
* @exception UnsupportedOperationException
* if any week year numbering isn't supported
* in this {@code Calendar}.
* @see #isWeekDateSupported()
* @see #getFirstDayOfWeek()
* @see #getMinimalDaysInFirstWeek()
* @since 1.7
*/
public int getWeekYear() {
throw new UnsupportedOperationException();
}
/**
* Sets the date of this {@code Calendar} with the the given date
* specifiers - week year, week of year, and day of week.
*
* Unlike the {@code set} method, all of the calendar fields
* and {@code time} values are calculated upon return.
*
* If {@code weekOfYear} is out of the valid week-of-year range
* in {@code weekYear}, the {@code weekYear} and {@code
* weekOfYear} values are adjusted in lenient mode, or an {@code
* IllegalArgumentException} is thrown in non-lenient mode.
*
* The default implementation of this method throws an
* {@code UnsupportedOperationException}.
*
* @param weekYear the week year
* @param weekOfYear the week number based on {@code weekYear}
* @param dayOfWeek the day of week value: one of the constants
* for the {@link #DAY_OF_WEEK} field: {@link
* #SUNDAY}, ..., {@link #SATURDAY}.
* @exception IllegalArgumentException
* if any of the given date specifiers is invalid
* or any of the calendar fields are inconsistent
* with the given date specifiers in non-lenient mode
* @exception UnsupportedOperationException
* if any week year numbering isn't supported in this
* {@code Calendar}.
* @see #isWeekDateSupported()
* @see #getFirstDayOfWeek()
* @see #getMinimalDaysInFirstWeek()
* @since 1.7
*/
public void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) {
throw new UnsupportedOperationException();
}
/**
* Returns the number of weeks in the week year represented by this
* {@code Calendar}.
*
* The default implementation of this method throws an
* {@code UnsupportedOperationException}.
*
* @return the number of weeks in the week year.
* @exception UnsupportedOperationException
* if any week year numbering isn't supported in this
* {@code Calendar}.
* @see #WEEK_OF_YEAR
* @see #isWeekDateSupported()
* @see #getWeekYear()
* @see #getActualMaximum(int)
* @since 1.7
*/
public int getWeeksInWeekYear() {
throw new UnsupportedOperationException();
}
/**
* Returns the minimum value for the given calendar field of this
* The default implementation of this method uses an iterative
* algorithm to determine the actual minimum value for the
* calendar field. Subclasses should, if possible, override this
* with a more efficient implementation - in many cases, they can
* simply return The default implementation of this method uses an iterative
* algorithm to determine the actual maximum value for the
* calendar field. Subclasses should, if possible, override this
* with a more efficient implementation.
*
* @param field the calendar field
* @return the maximum of the given calendar field for the time
* value of this Calendar
if the
* TimeZone
implementation subclass supports
* historical Daylight Saving Time schedule changes.
*/
public final static int DST_OFFSET = 16;
/**
* The number of distinct fields recognized by get
and set
.
* Field numbers range from 0..FIELD_COUNT-1
.
*/
public final static int FIELD_COUNT = 17;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Sunday.
*/
public final static int SUNDAY = 1;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Monday.
*/
public final static int MONDAY = 2;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Tuesday.
*/
public final static int TUESDAY = 3;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Wednesday.
*/
public final static int WEDNESDAY = 4;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Thursday.
*/
public final static int THURSDAY = 5;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Friday.
*/
public final static int FRIDAY = 6;
/**
* Value of the {@link #DAY_OF_WEEK} field indicating
* Saturday.
*/
public final static int SATURDAY = 7;
/**
* Value of the {@link #MONTH} field indicating the
* first month of the year in the Gregorian and Julian calendars.
*/
public final static int JANUARY = 0;
/**
* Value of the {@link #MONTH} field indicating the
* second month of the year in the Gregorian and Julian calendars.
*/
public final static int FEBRUARY = 1;
/**
* Value of the {@link #MONTH} field indicating the
* third month of the year in the Gregorian and Julian calendars.
*/
public final static int MARCH = 2;
/**
* Value of the {@link #MONTH} field indicating the
* fourth month of the year in the Gregorian and Julian calendars.
*/
public final static int APRIL = 3;
/**
* Value of the {@link #MONTH} field indicating the
* fifth month of the year in the Gregorian and Julian calendars.
*/
public final static int MAY = 4;
/**
* Value of the {@link #MONTH} field indicating the
* sixth month of the year in the Gregorian and Julian calendars.
*/
public final static int JUNE = 5;
/**
* Value of the {@link #MONTH} field indicating the
* seventh month of the year in the Gregorian and Julian calendars.
*/
public final static int JULY = 6;
/**
* Value of the {@link #MONTH} field indicating the
* eighth month of the year in the Gregorian and Julian calendars.
*/
public final static int AUGUST = 7;
/**
* Value of the {@link #MONTH} field indicating the
* ninth month of the year in the Gregorian and Julian calendars.
*/
public final static int SEPTEMBER = 8;
/**
* Value of the {@link #MONTH} field indicating the
* tenth month of the year in the Gregorian and Julian calendars.
*/
public final static int OCTOBER = 9;
/**
* Value of the {@link #MONTH} field indicating the
* eleventh month of the year in the Gregorian and Julian calendars.
*/
public final static int NOVEMBER = 10;
/**
* Value of the {@link #MONTH} field indicating the
* twelfth month of the year in the Gregorian and Julian calendars.
*/
public final static int DECEMBER = 11;
/**
* Value of the {@link #MONTH} field indicating the
* thirteenth month of the year. Although GregorianCalendar
* does not use this value, lunar calendars do.
*/
public final static int UNDECIMBER = 12;
/**
* Value of the {@link #AM_PM} field indicating the
* period of the day from midnight to just before noon.
*/
public final static int AM = 0;
/**
* Value of the {@link #AM_PM} field indicating the
* period of the day from noon to just before midnight.
*/
public final static int PM = 1;
/**
* A style specifier for {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating names in all styles, such as
* "January" and "Jan".
*
* @see #SHORT_FORMAT
* @see #LONG_FORMAT
* @see #SHORT_STANDALONE
* @see #LONG_STANDALONE
* @see #SHORT
* @see #LONG
* @since 1.6
*/
public static final int ALL_STYLES = 0;
static final int STANDALONE_MASK = 0x8000;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} equivalent to {@link #SHORT_FORMAT}.
*
* @see #SHORT_STANDALONE
* @see #LONG
* @since 1.6
*/
public static final int SHORT = 1;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} equivalent to {@link #LONG_FORMAT}.
*
* @see #LONG_STANDALONE
* @see #SHORT
* @since 1.6
*/
public static final int LONG = 2;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a narrow name used for format. Narrow names
* are typically single character strings, such as "M" for Monday.
*
* @see #NARROW_STANDALONE
* @see #SHORT_FORMAT
* @see #LONG_FORMAT
* @since 1.8
*/
public static final int NARROW_FORMAT = 4;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a narrow name independently. Narrow names
* are typically single character strings, such as "M" for Monday.
*
* @see #NARROW_FORMAT
* @see #SHORT_STANDALONE
* @see #LONG_STANDALONE
* @since 1.8
*/
public static final int NARROW_STANDALONE = NARROW_FORMAT | STANDALONE_MASK;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a short name used for format.
*
* @see #SHORT_STANDALONE
* @see #LONG_FORMAT
* @see #LONG_STANDALONE
* @since 1.8
*/
public static final int SHORT_FORMAT = 1;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a long name used for format.
*
* @see #LONG_STANDALONE
* @see #SHORT_FORMAT
* @see #SHORT_STANDALONE
* @since 1.8
*/
public static final int LONG_FORMAT = 2;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a short name used independently,
* such as a month abbreviation as calendar headers.
*
* @see #SHORT_FORMAT
* @see #LONG_FORMAT
* @see #LONG_STANDALONE
* @since 1.8
*/
public static final int SHORT_STANDALONE = SHORT | STANDALONE_MASK;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating a long name used independently,
* such as a month name as calendar headers.
*
* @see #LONG_FORMAT
* @see #SHORT_FORMAT
* @see #SHORT_STANDALONE
* @since 1.8
*/
public static final int LONG_STANDALONE = LONG | STANDALONE_MASK;
// Internal notes:
// Calendar contains two kinds of time representations: current "time" in
// milliseconds, and a set of calendar "fields" representing the current time.
// The two representations are usually in sync, but can get out of sync
// as follows.
// 1. Initially, no fields are set, and the time is invalid.
// 2. If the time is set, all fields are computed and in sync.
// 3. If a single field is set, the time is invalid.
// Recomputation of the time and fields happens when the object needs
// to return a result to the user, or use a result for a computation.
/**
* The calendar field values for the currently set time for this calendar.
* This is an array of FIELD_COUNT
integers, with index values
* ERA
through DST_OFFSET
.
* @serial
*/
@SuppressWarnings("ProtectedField")
protected int fields[];
/**
* The flags which tell if a specified calendar field for the calendar is set.
* A new object has no fields set. After the first call to a method
* which generates the fields, they all remain set after that.
* This is an array of FIELD_COUNT
booleans, with index values
* ERA
through DST_OFFSET
.
* @serial
*/
@SuppressWarnings("ProtectedField")
protected boolean isSet[];
/**
* Pseudo-time-stamps which specify when each field was set. There
* are two special values, UNSET and COMPUTED. Values from
* MINIMUM_USER_SET to Integer.MAX_VALUE are legal user set values.
*/
transient private int stamp[];
/**
* The currently set time for this calendar, expressed in milliseconds after
* January 1, 1970, 0:00:00 GMT.
* @see #isTimeSet
* @serial
*/
@SuppressWarnings("ProtectedField")
protected long time;
/**
* True if then the value of time
is valid.
* The time is made invalid by a change to an item of field[]
.
* @see #time
* @serial
*/
@SuppressWarnings("ProtectedField")
protected boolean isTimeSet;
/**
* True if fields[]
are in sync with the currently set time.
* If false, then the next attempt to get the value of a field will
* force a recomputation of all fields from the current value of
* time
.
* @serial
*/
@SuppressWarnings("ProtectedField")
protected boolean areFieldsSet;
/**
* True if all fields have been set.
* @serial
*/
transient boolean areAllFieldsSet;
/**
* True
if this calendar allows out-of-range field values during computation
* of time
from fields[]
.
* @see #setLenient
* @see #isLenient
* @serial
*/
private boolean lenient = true;
/**
* The TimeZone
used by this calendar. Calendar
* uses the time zone data to translate between locale and GMT time.
* @serial
*/
private TimeZone zone;
/**
* True
if zone references to a shared TimeZone object.
*/
transient private boolean sharedZone = false;
/**
* The first day of the week, with possible values SUNDAY
,
* MONDAY
, etc. This is a locale-dependent value.
* @serial
*/
private int firstDayOfWeek;
/**
* The number of days required for the first week in a month or year,
* with possible values from 1 to 7. This is a locale-dependent value.
* @serial
*/
private int minimalDaysInFirstWeek;
/**
* Cache to hold the firstDayOfWeek and minimalDaysInFirstWeek
* of a Locale.
*/
private static final ConcurrentMapstamp[]
, an internal array.
* This actually should not be written out to the stream, and will probably
* be removed from the stream in the near future. In the meantime,
* a value of MINIMUM_USER_STAMP
should be used.
* @serial
*/
private int nextStamp = MINIMUM_USER_STAMP;
// the internal serial version which says which version was written
// - 0 (default) for version up to JDK 1.1.5
// - 1 for version from JDK 1.1.6, which writes a correct 'time' value
// as well as compatible values for other fields. This is a
// transitional format.
// - 2 (not implemented yet) a future version, in which fields[],
// areFieldsSet, and isTimeSet become transient, and isSet[] is
// removed. In JDK 1.1.6 we write a format compatible with version 2.
static final int currentSerialVersion = 1;
/**
* The version of the serialized data on the stream. Possible values:
*
*
* When streaming out this class, the most recent format
* and the highest allowable serialVersionOnStream
* is written.
* @serial
* @since JDK1.1.6
*/
private int serialVersionOnStream = currentSerialVersion;
// Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = -1807547505821590642L;
// Mask values for calendar fields
@SuppressWarnings("PointlessBitwiseExpression")
final static int ERA_MASK = (1 << ERA);
final static int YEAR_MASK = (1 << YEAR);
final static int MONTH_MASK = (1 << MONTH);
final static int WEEK_OF_YEAR_MASK = (1 << WEEK_OF_YEAR);
final static int WEEK_OF_MONTH_MASK = (1 << WEEK_OF_MONTH);
final static int DAY_OF_MONTH_MASK = (1 << DAY_OF_MONTH);
final static int DATE_MASK = DAY_OF_MONTH_MASK;
final static int DAY_OF_YEAR_MASK = (1 << DAY_OF_YEAR);
final static int DAY_OF_WEEK_MASK = (1 << DAY_OF_WEEK);
final static int DAY_OF_WEEK_IN_MONTH_MASK = (1 << DAY_OF_WEEK_IN_MONTH);
final static int AM_PM_MASK = (1 << AM_PM);
final static int HOUR_MASK = (1 << HOUR);
final static int HOUR_OF_DAY_MASK = (1 << HOUR_OF_DAY);
final static int MINUTE_MASK = (1 << MINUTE);
final static int SECOND_MASK = (1 << SECOND);
final static int MILLISECOND_MASK = (1 << MILLISECOND);
final static int ZONE_OFFSET_MASK = (1 << ZONE_OFFSET);
final static int DST_OFFSET_MASK = (1 << DST_OFFSET);
/**
* {@code Calendar.Builder} is used for creating a {@code Calendar} from
* various date-time parameters.
*
*
* Calendar cal = new Calendar.Builder().setCalendarType("iso8601")
* .setWeekDate(2013, 1, MONDAY).build();
*
* Calendar cal = new Calendar.Builder().setCalendarType("japanese")
* .setFields(YEAR, 1, DAY_OF_YEAR, 1).build();
*
* @since 1.8
* @see Calendar#getInstance(TimeZone, Locale)
* @see Calendar#fields
*/
public static class Builder {
private static final int NFIELDS = FIELD_COUNT + 1; // +1 for WEEK_YEAR
private static final int WEEK_YEAR = FIELD_COUNT;
private long instant;
// Calendar.stamp[] (lower half) and Calendar.fields[] (upper half) combined
private int[] fields;
// Pseudo timestamp starting from MINIMUM_USER_STAMP.
// (COMPUTED is used to indicate that the instant has been set.)
private int nextStamp;
// maxFieldIndex keeps the max index of fields which have been set.
// (WEEK_YEAR is never included.)
private int maxFieldIndex;
private String type;
private TimeZone zone;
private boolean lenient = true;
private Locale locale;
private int firstDayOfWeek, minimalDaysInFirstWeek;
/**
* Constructs a {@code Calendar.Builder}.
*/
public Builder() {
}
/**
* Sets the instant parameter to the given {@code instant} value that is
* a millisecond offset from the
* Epoch.
*
* @param instant a millisecond offset from the Epoch
* @return this {@code Calendar.Builder}
* @throws IllegalStateException if any of the field parameters have
* already been set
* @see Calendar#setTime(Date)
* @see Calendar#setTimeInMillis(long)
* @see Calendar#time
*/
public Builder setInstant(long instant) {
if (fields != null) {
throw new IllegalStateException();
}
this.instant = instant;
nextStamp = COMPUTED;
return this;
}
/**
* Sets the instant parameter to the {@code instant} value given by a
* {@link Date}. This method is equivalent to a call to
* {@link #setInstant(long) setInstant(instant.getTime())}.
*
* @param instant a {@code Date} representing a millisecond offset from
* the Epoch
* @return this {@code Calendar.Builder}
* @throws NullPointerException if {@code instant} is {@code null}
* @throws IllegalStateException if any of the field parameters have
* already been set
* @see Calendar#setTime(Date)
* @see Calendar#setTimeInMillis(long)
* @see Calendar#time
*/
public Builder setInstant(Date instant) {
return setInstant(instant.getTime()); // NPE if instant == null
}
/**
* Sets the {@code field} parameter to the given {@code value}.
* {@code field} is an index to the {@link Calendar#fields}, such as
* {@link Calendar#DAY_OF_MONTH DAY_OF_MONTH}. Field value validation is
* not performed in this method. Any out of range values are either
* normalized in lenient mode or detected as an invalid value in
* non-lenient mode when building a {@code Calendar}.
*
* @param field an index to the {@code Calendar} fields
* @param value the field value
* @return this {@code Calendar.Builder}
* @throws IllegalArgumentException if {@code field} is invalid
* @throws IllegalStateException if the instant value has already been set,
* or if fields have been set too many
* (approximately {@link Integer#MAX_VALUE}) times.
* @see Calendar#set(int, int)
*/
public Builder set(int field, int value) {
// Note: WEEK_YEAR can't be set with this method.
if (field < 0 || field >= FIELD_COUNT) {
throw new IllegalArgumentException("field is invalid");
}
if (isInstantSet()) {
throw new IllegalStateException("instant has been set");
}
allocateFields();
internalSet(field, value);
return this;
}
/**
* Sets field parameters to their values given by
* {@code fieldValuePairs} that are pairs of a field and its value.
* For example,
*
* setFeilds(Calendar.YEAR, 2013,
* Calendar.MONTH, Calendar.DECEMBER,
* Calendar.DAY_OF_MONTH, 23);
* is equivalent to the sequence of the following
* {@link #set(int, int) set} calls:
*
* set(Calendar.YEAR, 2013)
* .set(Calendar.MONTH, Calendar.DECEMBER)
* .set(Calendar.DAY_OF_MONTH, 23);
*
* @param fieldValuePairs field-value pairs
* @return this {@code Calendar.Builder}
* @throws NullPointerException if {@code fieldValuePairs} is {@code null}
* @throws IllegalArgumentException if any of fields are invalid,
* or if {@code fieldValuePairs.length} is an odd number.
* @throws IllegalStateException if the instant value has been set,
* or if fields have been set too many (approximately
* {@link Integer#MAX_VALUE}) times.
*/
public Builder setFields(int... fieldValuePairs) {
int len = fieldValuePairs.length;
if ((len % 2) != 0) {
throw new IllegalArgumentException();
}
if (isInstantSet()) {
throw new IllegalStateException("instant has been set");
}
if ((nextStamp + len / 2) < 0) {
throw new IllegalStateException("stamp counter overflow");
}
allocateFields();
for (int i = 0; i < len; ) {
int field = fieldValuePairs[i++];
// Note: WEEK_YEAR can't be set with this method.
if (field < 0 || field >= FIELD_COUNT) {
throw new IllegalArgumentException("field is invalid");
}
internalSet(field, fieldValuePairs[i++]);
}
return this;
}
/**
* Sets the date field parameters to the values given by {@code year},
* {@code month}, and {@code dayOfMonth}. This method is equivalent to
* a call to:
*
* setFields(Calendar.YEAR, year,
* Calendar.MONTH, month,
* Calendar.DAY_OF_MONTH, dayOfMonth);
*
* @param year the {@link Calendar#YEAR YEAR} value
* @param month the {@link Calendar#MONTH MONTH} value
* (the month numbering is 0-based).
* @param dayOfMonth the {@link Calendar#DAY_OF_MONTH DAY_OF_MONTH} value
* @return this {@code Calendar.Builder}
*/
public Builder setDate(int year, int month, int dayOfMonth) {
return setFields(YEAR, year, MONTH, month, DAY_OF_MONTH, dayOfMonth);
}
/**
* Sets the time of day field parameters to the values given by
* {@code hourOfDay}, {@code minute}, and {@code second}. This method is
* equivalent to a call to:
*
* setTimeOfDay(hourOfDay, minute, second, 0);
*
* @param hourOfDay the {@link Calendar#HOUR_OF_DAY HOUR_OF_DAY} value
* (24-hour clock)
* @param minute the {@link Calendar#MINUTE MINUTE} value
* @param second the {@link Calendar#SECOND SECOND} value
* @return this {@code Calendar.Builder}
*/
public Builder setTimeOfDay(int hourOfDay, int minute, int second) {
return setTimeOfDay(hourOfDay, minute, second, 0);
}
/**
* Sets the time of day field parameters to the values given by
* {@code hourOfDay}, {@code minute}, {@code second}, and
* {@code millis}. This method is equivalent to a call to:
*
* setFields(Calendar.HOUR_OF_DAY, hourOfDay,
* Calendar.MINUTE, minute,
* Calendar.SECOND, second,
* Calendar.MILLISECOND, millis);
*
* @param hourOfDay the {@link Calendar#HOUR_OF_DAY HOUR_OF_DAY} value
* (24-hour clock)
* @param minute the {@link Calendar#MINUTE MINUTE} value
* @param second the {@link Calendar#SECOND SECOND} value
* @param millis the {@link Calendar#MILLISECOND MILLISECOND} value
* @return this {@code Calendar.Builder}
*/
public Builder setTimeOfDay(int hourOfDay, int minute, int second, int millis) {
return setFields(HOUR_OF_DAY, hourOfDay, MINUTE, minute,
SECOND, second, MILLISECOND, millis);
}
/**
* Sets the week-based date parameters to the values with the given
* date specifiers - week year, week of year, and day of week.
*
* TimeZone
} will be used in the {@link #build() build}
* method.
*
* @param zone the {@link TimeZone}
* @return this {@code Calendar.Builder}
* @throws NullPointerException if {@code zone} is {@code null}
* @see Calendar#setTimeZone(TimeZone)
*/
public Builder setTimeZone(TimeZone zone) {
if (zone == null) {
throw new NullPointerException();
}
this.zone = zone;
return this;
}
/**
* Sets the lenient mode parameter to the value given by {@code lenient}.
* If no lenient parameter is given to this {@code Calendar.Builder},
* lenient mode will be used in the {@link #build() build} method.
*
* @param lenient {@code true} for lenient mode;
* {@code false} for non-lenient mode
* @return this {@code Calendar.Builder}
* @see Calendar#setLenient(boolean)
*/
public Builder setLenient(boolean lenient) {
this.lenient = lenient;
return this;
}
/**
* Sets the calendar type parameter to the given {@code type}. The
* calendar type given by this method has precedence over any explicit
* or implicit calendar type given by the
* {@linkplain #setLocale(Locale) locale}.
*
* Locale
}
* for {@link Locale.Category#FORMAT} will be used.
*
* Calendar
returned is based on the current time
* in the default time zone with the default locale.
*
* @return a Calendar.
*/
public static Calendar getInstance()
{
Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));
cal.sharedZone = true;
return cal;
}
/**
* Gets a calendar using the specified time zone and default locale.
* The Calendar
returned is based on the current time
* in the given time zone with the default locale.
*
* @param zone the time zone to use
* @return a Calendar.
*/
public static Calendar getInstance(TimeZone zone)
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
/**
* Gets a calendar using the default time zone and specified locale.
* The Calendar
returned is based on the current time
* in the default time zone with the given locale.
*
* @param aLocale the locale for the week data
* @return a Calendar.
*/
public static Calendar getInstance(Locale aLocale)
{
Calendar cal = createCalendar(TimeZone.getDefaultRef(), aLocale);
cal.sharedZone = true;
return cal;
}
/**
* Gets a calendar with the specified time zone and locale.
* The Calendar
returned is based on the current time
* in the given time zone with the given locale.
*
* @param zone the time zone to use
* @param aLocale the locale for the week data
* @return a Calendar.
*/
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
/**
* Returns an array of all locales for which the getInstance
* methods of this class can return localized instances.
* The array returned must contain at least a Locale
* instance equal to {@link java.util.Locale#US Locale.US}.
*
* @return An array of locales for which localized
* Calendar
instances are available.
*/
public static synchronized Locale[] getAvailableLocales()
{
return DateFormat.getAvailableLocales();
}
/**
* Converts the current calendar field values in {@link #fields fields[]}
* to the millisecond time value
* {@link #time}.
*
* @see #complete()
* @see #computeFields()
*/
protected abstract void computeTime();
/**
* Converts the current millisecond time value {@link #time}
* to calendar field values in {@link #fields fields[]}.
* This allows you to sync up the calendar field values with
* a new time that is set for the calendar. The time is not
* recomputed first; to recompute the time, then the fields, call the
* {@link #complete()} method.
*
* @see #computeTime()
*/
protected abstract void computeFields();
/**
* Returns a Date
object representing this
* Calendar
's time value (millisecond offset from the Epoch").
*
* @return a Date
representing the time value.
* @see #setTime(Date)
* @see #getTimeInMillis()
*/
public final Date getTime() {
return new Date(getTimeInMillis());
}
/**
* Sets this Calendar's time with the given Date
.
* setTime()
with
* Date(Long.MAX_VALUE)
or Date(Long.MIN_VALUE)
* may yield incorrect field values from get()
.
*
* @param date the given Date.
* @see #getTime()
* @see #setTimeInMillis(long)
*/
public final void setTime(Date date) {
setTimeInMillis(date.getTime());
}
/**
* Returns this Calendar's time value in milliseconds.
*
* @return the current time as UTC milliseconds from the epoch.
* @see #getTime()
* @see #setTimeInMillis(long)
*/
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
/**
* Sets this Calendar's current time from the given long value.
*
* @param millis the new time in UTC milliseconds from the epoch.
* @see #setTime(Date)
* @see #getTimeInMillis()
*/
public void setTimeInMillis(long millis) {
// If we don't need to recalculate the calendar field values,
// do nothing.
if (time == millis && isTimeSet && areFieldsSet && areAllFieldsSet
&& (zone instanceof ZoneInfo) && !((ZoneInfo)zone).isDirty()) {
return;
}
time = millis;
isTimeSet = true;
areFieldsSet = false;
computeFields();
areAllFieldsSet = areFieldsSet = true;
}
/**
* Returns the value of the given calendar field. In lenient mode,
* all calendar fields are normalized. In non-lenient mode, all
* calendar fields are validated and this method throws an
* exception if any calendar fields have out-of-range values. The
* normalization and validation are handled by the
* {@link #complete()} method, which process is calendar
* system dependent.
*
* @param field the given calendar field.
* @return the value for the given calendar field.
* @throws ArrayIndexOutOfBoundsException if the specified field is out of range
* (field < 0 || field >= FIELD_COUNT
).
* @see #set(int,int)
* @see #complete()
*/
public int get(int field)
{
complete();
return internalGet(field);
}
/**
* Returns the value of the given calendar field. This method does
* not involve normalization or validation of the field value.
*
* @param field the given calendar field.
* @return the value for the given calendar field.
* @see #get(int)
*/
protected final int internalGet(int field)
{
return fields[field];
}
/**
* Sets the value of the given calendar field. This method does
* not affect any setting state of the field in this
* Calendar
instance.
*
* @throws IndexOutOfBoundsException if the specified field is out of range
* (field < 0 || field >= FIELD_COUNT
).
* @see #areFieldsSet
* @see #isTimeSet
* @see #areAllFieldsSet
* @see #set(int,int)
*/
final void internalSet(int field, int value)
{
fields[field] = value;
}
/**
* Sets the given calendar field to the given value. The value is not
* interpreted by this method regardless of the leniency mode.
*
* @param field the given calendar field.
* @param value the value to be set for the given calendar field.
* @throws ArrayIndexOutOfBoundsException if the specified field is out of range
* (field < 0 || field >= FIELD_COUNT
).
* in non-lenient mode.
* @see #set(int,int,int)
* @see #set(int,int,int,int,int)
* @see #set(int,int,int,int,int,int)
* @see #get(int)
*/
public void set(int field, int value)
{
// If the fields are partially normalized, calculate all the
// fields before changing any fields.
if (areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false;
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
/**
* Sets the values for the calendar fields YEAR
,
* MONTH
, and DAY_OF_MONTH
.
* Previous values of other calendar fields are retained. If this is not desired,
* call {@link #clear()} first.
*
* @param year the value used to set the YEAR
calendar field.
* @param month the value used to set the MONTH
calendar field.
* Month value is 0-based. e.g., 0 for January.
* @param date the value used to set the DAY_OF_MONTH
calendar field.
* @see #set(int,int)
* @see #set(int,int,int,int,int)
* @see #set(int,int,int,int,int,int)
*/
public final void set(int year, int month, int date)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
}
/**
* Sets the values for the calendar fields YEAR
,
* MONTH
, DAY_OF_MONTH
,
* HOUR_OF_DAY
, and MINUTE
.
* Previous values of other fields are retained. If this is not desired,
* call {@link #clear()} first.
*
* @param year the value used to set the YEAR
calendar field.
* @param month the value used to set the MONTH
calendar field.
* Month value is 0-based. e.g., 0 for January.
* @param date the value used to set the DAY_OF_MONTH
calendar field.
* @param hourOfDay the value used to set the HOUR_OF_DAY
calendar field.
* @param minute the value used to set the MINUTE
calendar field.
* @see #set(int,int)
* @see #set(int,int,int)
* @see #set(int,int,int,int,int,int)
*/
public final void set(int year, int month, int date, int hourOfDay, int minute)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
set(HOUR_OF_DAY, hourOfDay);
set(MINUTE, minute);
}
/**
* Sets the values for the fields YEAR
, MONTH
,
* DAY_OF_MONTH
, HOUR
, MINUTE
, and
* SECOND
.
* Previous values of other fields are retained. If this is not desired,
* call {@link #clear()} first.
*
* @param year the value used to set the YEAR
calendar field.
* @param month the value used to set the MONTH
calendar field.
* Month value is 0-based. e.g., 0 for January.
* @param date the value used to set the DAY_OF_MONTH
calendar field.
* @param hourOfDay the value used to set the HOUR_OF_DAY
calendar field.
* @param minute the value used to set the MINUTE
calendar field.
* @param second the value used to set the SECOND
calendar field.
* @see #set(int,int)
* @see #set(int,int,int)
* @see #set(int,int,int,int,int)
*/
public final void set(int year, int month, int date, int hourOfDay, int minute,
int second)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
set(HOUR_OF_DAY, hourOfDay);
set(MINUTE, minute);
set(SECOND, second);
}
/**
* Sets all the calendar field values and the time value
* (millisecond offset from the Epoch) of
* this Calendar
undefined. This means that {@link
* #isSet(int) isSet()} will return false
for all the
* calendar fields, and the date and time calculations will treat
* the fields as if they had never been set. A
* Calendar
implementation class may use its specific
* default field values for date/time calculations. For example,
* GregorianCalendar
uses 1970 if the
* YEAR
field value is undefined.
*
* @see #clear(int)
*/
public final void clear()
{
for (int i = 0; i < fields.length; ) {
stamp[i] = fields[i] = 0; // UNSET == 0
isSet[i++] = false;
}
areAllFieldsSet = areFieldsSet = false;
isTimeSet = false;
}
/**
* Sets the given calendar field value and the time value
* (millisecond offset from the Epoch) of
* this Calendar
undefined. This means that {@link
* #isSet(int) isSet(field)} will return false
, and
* the date and time calculations will treat the field as if it
* had never been set. A Calendar
implementation
* class may use the field's specific default value for date and
* time calculations.
*
* Calendar
. Use {@link
* #set(int,int) set(Calendar.HOUR_OF_DAY, 0)} to reset the hour
* value.
*
* @param field the calendar field to be cleared.
* @see #clear()
*/
public final void clear(int field)
{
fields[field] = 0;
stamp[field] = UNSET;
isSet[field] = false;
areAllFieldsSet = areFieldsSet = false;
isTimeSet = false;
}
/**
* Determines if the given calendar field has a value set,
* including cases that the value has been set by internal fields
* calculations triggered by a get
method call.
*
* @return true
if the given calendar field has a value set;
* false
otherwise.
*/
public final boolean isSet(int field)
{
return stamp[field] != UNSET;
}
/**
* Returns the string representation of the calendar
* field
value in the given style
and
* locale
. If no string representation is
* applicable, null
is returned. This method calls
* {@link Calendar#get(int) get(field)} to get the calendar
* field
value if the string representation is
* applicable to the given calendar field
.
*
* Calendar
is a
* GregorianCalendar
and its date is 2005-01-01, then
* the string representation of the {@link #MONTH} field would be
* "January" in the long style in an English locale or "Jan" in
* the short style. However, no string representation would be
* available for the {@link #DAY_OF_MONTH} field, and this method
* would return null
.
*
* locale
.
*
* @param field
* the calendar field for which the string representation
* is returned
* @param style
* the style applied to the string representation; one of {@link
* #SHORT_FORMAT} ({@link #SHORT}), {@link #SHORT_STANDALONE},
* {@link #LONG_FORMAT} ({@link #LONG}), {@link #LONG_STANDALONE},
* {@link #NARROW_FORMAT}, or {@link #NARROW_STANDALONE}.
* @param locale
* the locale for the string representation
* (any calendar types specified by {@code locale} are ignored)
* @return the string representation of the given
* {@code field} in the given {@code style}, or
* {@code null} if no string representation is
* applicable.
* @exception IllegalArgumentException
* if {@code field} or {@code style} is invalid,
* or if this {@code Calendar} is non-lenient and any
* of the calendar fields have invalid values
* @exception NullPointerException
* if {@code locale} is null
* @since 1.6
*/
public String getDisplayName(int field, int style, Locale locale) {
if (!checkDisplayNameParams(field, style, SHORT, NARROW_FORMAT, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
// the standalone and narrow styles are supported only through CalendarDataProviders.
if (isStandaloneStyle(style) || isNarrowStyle(style)) {
return CalendarDataUtility.retrieveFieldValueName(getCalendarType(),
field, get(field),
style, locale);
}
DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
String[] strings = getFieldStrings(field, style, symbols);
if (strings != null) {
int fieldValue = get(field);
if (fieldValue < strings.length) {
return strings[fieldValue];
}
}
return null;
}
/**
* Returns a {@code Map} containing all names of the calendar
* {@code field} in the given {@code style} and
* {@code locale} and their corresponding field values. For
* example, if this {@code Calendar} is a {@link
* GregorianCalendar}, the returned map would contain "Jan" to
* {@link #JANUARY}, "Feb" to {@link #FEBRUARY}, and so on, in the
* {@linkplain #SHORT short} style in an English locale.
*
* true
if the field has been set externally,
* false
otherwise.
* @exception IndexOutOfBoundsException if the specified
* field
is out of range
* (field < 0 || field >= FIELD_COUNT
).
* @see #selectFields()
* @see #setFieldsComputed(int)
*/
final boolean isExternallySet(int field) {
return stamp[field] >= MINIMUM_USER_STAMP;
}
/**
* Returns a field mask (bit mask) indicating all calendar fields that
* have the state of externally or internally set.
*
* @return a bit mask indicating set state fields
*/
final int getSetStateFields() {
int mask = 0;
for (int i = 0; i < fields.length; i++) {
if (stamp[i] != UNSET) {
mask |= 1 << i;
}
}
return mask;
}
/**
* Sets the state of the specified calendar fields to
* computed. This state means that the specified calendar fields
* have valid values that have been set by internal time calculation
* rather than by calling one of the setter methods.
*
* @param fieldMask the field to be marked as computed.
* @exception IndexOutOfBoundsException if the specified
* field
is out of range
* (field < 0 || field >= FIELD_COUNT
).
* @see #isExternallySet(int)
* @see #selectFields()
*/
final void setFieldsComputed(int fieldMask) {
if (fieldMask == ALL_FIELDS) {
for (int i = 0; i < fields.length; i++) {
stamp[i] = COMPUTED;
isSet[i] = true;
}
areFieldsSet = areAllFieldsSet = true;
} else {
for (int i = 0; i < fields.length; i++) {
if ((fieldMask & 1) == 1) {
stamp[i] = COMPUTED;
isSet[i] = true;
} else {
if (areAllFieldsSet && !isSet[i]) {
areAllFieldsSet = false;
}
}
fieldMask >>>= 1;
}
}
}
/**
* Sets the state of the calendar fields that are not specified
* by fieldMask
to unset. If fieldMask
* specifies all the calendar fields, then the state of this
* Calendar
becomes that all the calendar fields are in sync
* with the time value (millisecond offset from the Epoch).
*
* @param fieldMask the field mask indicating which calendar fields are in
* sync with the time value.
* @exception IndexOutOfBoundsException if the specified
* field
is out of range
* (field < 0 || field >= FIELD_COUNT
).
* @see #isExternallySet(int)
* @see #selectFields()
*/
final void setFieldsNormalized(int fieldMask) {
if (fieldMask != ALL_FIELDS) {
for (int i = 0; i < fields.length; i++) {
if ((fieldMask & 1) == 0) {
stamp[i] = fields[i] = 0; // UNSET == 0
isSet[i] = false;
}
fieldMask >>= 1;
}
}
// Some or all of the fields are in sync with the
// milliseconds, but the stamp values are not normalized yet.
areFieldsSet = true;
areAllFieldsSet = false;
}
/**
* Returns whether the calendar fields are partially in sync with the time
* value or fully in sync but not stamp values are not normalized yet.
*/
final boolean isPartiallyNormalized() {
return areFieldsSet && !areAllFieldsSet;
}
/**
* Returns whether the calendar fields are fully in sync with the time
* value.
*/
final boolean isFullyNormalized() {
return areFieldsSet && areAllFieldsSet;
}
/**
* Marks this Calendar as not sync'd.
*/
final void setUnnormalized() {
areFieldsSet = areAllFieldsSet = false;
}
/**
* Returns whether the specified field
is on in the
* fieldMask
.
*/
static boolean isFieldSet(int fieldMask, int field) {
return (fieldMask & (1 << field)) != 0;
}
/**
* Returns a field mask indicating which calendar field values
* to be used to calculate the time value. The calendar fields are
* returned as a bit mask, each bit of which corresponds to a field, i.e.,
* the mask value of field
is (1 <<
* field)
. For example, 0x26 represents the YEAR
,
* MONTH
, and DAY_OF_MONTH
fields (i.e., 0x26 is
* equal to
* (1<<YEAR)|(1<<MONTH)|(1<<DAY_OF_MONTH))
.
*
* isSet(field)
is
* false
), then the default value of the field has to be
* used, which case means that the field has been selected because the
* selected combination involves the field.
*
* @return a bit mask of selected fields
* @see #isExternallySet(int)
*/
final int selectFields() {
// This implementation has been taken from the GregorianCalendar class.
// The YEAR field must always be used regardless of its SET
// state because YEAR is a mandatory field to determine the date
// and the default value (EPOCH_YEAR) may change through the
// normalization process.
int fieldMask = YEAR_MASK;
if (stamp[ERA] != UNSET) {
fieldMask |= ERA_MASK;
}
// Find the most recent group of fields specifying the day within
// the year. These may be any of the following combinations:
// MONTH + DAY_OF_MONTH
// MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
// MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
// DAY_OF_YEAR
// WEEK_OF_YEAR + DAY_OF_WEEK
// We look for the most recent of the fields in each group to determine
// the age of the group. For groups involving a week-related field such
// as WEEK_OF_MONTH, DAY_OF_WEEK_IN_MONTH, or WEEK_OF_YEAR, both the
// week-related field and the DAY_OF_WEEK must be set for the group as a
// whole to be considered. (See bug 4153860 - liu 7/24/98.)
int dowStamp = stamp[DAY_OF_WEEK];
int monthStamp = stamp[MONTH];
int domStamp = stamp[DAY_OF_MONTH];
int womStamp = aggregateStamp(stamp[WEEK_OF_MONTH], dowStamp);
int dowimStamp = aggregateStamp(stamp[DAY_OF_WEEK_IN_MONTH], dowStamp);
int doyStamp = stamp[DAY_OF_YEAR];
int woyStamp = aggregateStamp(stamp[WEEK_OF_YEAR], dowStamp);
int bestStamp = domStamp;
if (womStamp > bestStamp) {
bestStamp = womStamp;
}
if (dowimStamp > bestStamp) {
bestStamp = dowimStamp;
}
if (doyStamp > bestStamp) {
bestStamp = doyStamp;
}
if (woyStamp > bestStamp) {
bestStamp = woyStamp;
}
/* No complete combination exists. Look for WEEK_OF_MONTH,
* DAY_OF_WEEK_IN_MONTH, or WEEK_OF_YEAR alone. Treat DAY_OF_WEEK alone
* as DAY_OF_WEEK_IN_MONTH.
*/
if (bestStamp == UNSET) {
womStamp = stamp[WEEK_OF_MONTH];
dowimStamp = Math.max(stamp[DAY_OF_WEEK_IN_MONTH], dowStamp);
woyStamp = stamp[WEEK_OF_YEAR];
bestStamp = Math.max(Math.max(womStamp, dowimStamp), woyStamp);
/* Treat MONTH alone or no fields at all as DAY_OF_MONTH. This may
* result in bestStamp = domStamp = UNSET if no fields are set,
* which indicates DAY_OF_MONTH.
*/
if (bestStamp == UNSET) {
bestStamp = domStamp = monthStamp;
}
}
if (bestStamp == domStamp ||
(bestStamp == womStamp && stamp[WEEK_OF_MONTH] >= stamp[WEEK_OF_YEAR]) ||
(bestStamp == dowimStamp && stamp[DAY_OF_WEEK_IN_MONTH] >= stamp[WEEK_OF_YEAR])) {
fieldMask |= MONTH_MASK;
if (bestStamp == domStamp) {
fieldMask |= DAY_OF_MONTH_MASK;
} else {
assert (bestStamp == womStamp || bestStamp == dowimStamp);
if (dowStamp != UNSET) {
fieldMask |= DAY_OF_WEEK_MASK;
}
if (womStamp == dowimStamp) {
// When they are equal, give the priority to
// WEEK_OF_MONTH for compatibility.
if (stamp[WEEK_OF_MONTH] >= stamp[DAY_OF_WEEK_IN_MONTH]) {
fieldMask |= WEEK_OF_MONTH_MASK;
} else {
fieldMask |= DAY_OF_WEEK_IN_MONTH_MASK;
}
} else {
if (bestStamp == womStamp) {
fieldMask |= WEEK_OF_MONTH_MASK;
} else {
assert (bestStamp == dowimStamp);
if (stamp[DAY_OF_WEEK_IN_MONTH] != UNSET) {
fieldMask |= DAY_OF_WEEK_IN_MONTH_MASK;
}
}
}
}
} else {
assert (bestStamp == doyStamp || bestStamp == woyStamp ||
bestStamp == UNSET);
if (bestStamp == doyStamp) {
fieldMask |= DAY_OF_YEAR_MASK;
} else {
assert (bestStamp == woyStamp);
if (dowStamp != UNSET) {
fieldMask |= DAY_OF_WEEK_MASK;
}
fieldMask |= WEEK_OF_YEAR_MASK;
}
}
// Find the best set of fields specifying the time of day. There
// are only two possibilities here; the HOUR_OF_DAY or the
// AM_PM and the HOUR.
int hourOfDayStamp = stamp[HOUR_OF_DAY];
int hourStamp = aggregateStamp(stamp[HOUR], stamp[AM_PM]);
bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
// if bestStamp is still UNSET, then take HOUR or AM_PM. (See 4846659)
if (bestStamp == UNSET) {
bestStamp = Math.max(stamp[HOUR], stamp[AM_PM]);
}
// Hours
if (bestStamp != UNSET) {
if (bestStamp == hourOfDayStamp) {
fieldMask |= HOUR_OF_DAY_MASK;
} else {
fieldMask |= HOUR_MASK;
if (stamp[AM_PM] != UNSET) {
fieldMask |= AM_PM_MASK;
}
}
}
if (stamp[MINUTE] != UNSET) {
fieldMask |= MINUTE_MASK;
}
if (stamp[SECOND] != UNSET) {
fieldMask |= SECOND_MASK;
}
if (stamp[MILLISECOND] != UNSET) {
fieldMask |= MILLISECOND_MASK;
}
if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP) {
fieldMask |= ZONE_OFFSET_MASK;
}
if (stamp[DST_OFFSET] >= MINIMUM_USER_STAMP) {
fieldMask |= DST_OFFSET_MASK;
}
return fieldMask;
}
int getBaseStyle(int style) {
return style & ~STANDALONE_MASK;
}
boolean isStandaloneStyle(int style) {
return (style & STANDALONE_MASK) != 0;
}
boolean isNarrowStyle(int style) {
return style == NARROW_FORMAT || style == NARROW_STANDALONE;
}
/**
* Returns the pseudo-time-stamp for two fields, given their
* individual pseudo-time-stamps. If either of the fields
* is unset, then the aggregate is unset. Otherwise, the
* aggregate is the later of the two stamps.
*/
private static int aggregateStamp(int stamp_a, int stamp_b) {
if (stamp_a == UNSET || stamp_b == UNSET) {
return UNSET;
}
return (stamp_a > stamp_b) ? stamp_a : stamp_b;
}
/**
* Returns an unmodifiable {@code Set} containing all calendar types
* supported by {@code Calendar} in the runtime environment. The available
* calendar types can be used for the Unicode locale extensions.
* The {@code Set} returned contains at least {@code "gregory"}. The
* calendar types don't include aliases, such as {@code "gregorian"} for
* {@code "gregory"}.
*
* @return an unmodifiable {@code Set} containing all available calendar types
* @since 1.8
* @see #getCalendarType()
* @see Calendar.Builder#setCalendarType(String)
* @see Locale#getUnicodeLocaleType(String)
*/
public static SetCalendar
to the specified
* Object
. The result is true
if and only if
* the argument is a Calendar
object of the same calendar
* system that represents the same time value (millisecond offset from the
* Epoch) under the same
* Calendar
parameters as this object.
*
* Calendar
parameters are the values represented
* by the isLenient
, getFirstDayOfWeek
,
* getMinimalDaysInFirstWeek
and getTimeZone
* methods. If there is any difference in those parameters
* between the two Calendar
s, this method returns
* false
.
*
* true
if this object is equal to obj
;
* false
otherwise.
*/
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
try {
Calendar that = (Calendar)obj;
return compareTo(getMillisOf(that)) == 0 &&
lenient == that.lenient &&
firstDayOfWeek == that.firstDayOfWeek &&
minimalDaysInFirstWeek == that.minimalDaysInFirstWeek &&
zone.equals(that.zone);
} catch (Exception e) {
// Note: GregorianCalendar.computeTime throws
// IllegalArgumentException if the ERA value is invalid
// even it's in lenient mode.
}
return false;
}
/**
* Returns a hash code for this calendar.
*
* @return a hash code value for this object.
* @since 1.2
*/
@Override
public int hashCode() {
// 'otheritems' represents the hash code for the previous versions.
int otheritems = (lenient ? 1 : 0)
| (firstDayOfWeek << 1)
| (minimalDaysInFirstWeek << 4)
| (zone.hashCode() << 7);
long t = getMillisOf(this);
return (int) t ^ (int)(t >> 32) ^ otheritems;
}
/**
* Returns whether this Calendar
represents a time
* before the time represented by the specified
* Object
. This method is equivalent to:
*
* if and only if
* compareTo(when) < 0
*
when
is a Calendar
* instance. Otherwise, the method returns false
.
*
* @param when the Object
to be compared
* @return true
if the time of this
* Calendar
is before the time represented by
* when
; false
otherwise.
* @see #compareTo(Calendar)
*/
public boolean before(Object when) {
return when instanceof Calendar
&& compareTo((Calendar)when) < 0;
}
/**
* Returns whether this Calendar
represents a time
* after the time represented by the specified
* Object
. This method is equivalent to:
*
* if and only if
* compareTo(when) > 0
*
when
is a Calendar
* instance. Otherwise, the method returns false
.
*
* @param when the Object
to be compared
* @return true
if the time of this Calendar
is
* after the time represented by when
; false
* otherwise.
* @see #compareTo(Calendar)
*/
public boolean after(Object when) {
return when instanceof Calendar
&& compareTo((Calendar)when) > 0;
}
/**
* Compares the time values (millisecond offsets from the Epoch) represented by two
* Calendar
objects.
*
* @param anotherCalendar the Calendar
to be compared.
* @return the value 0
if the time represented by the argument
* is equal to the time represented by this Calendar
; a value
* less than 0
if the time of this Calendar
is
* before the time represented by the argument; and a value greater than
* 0
if the time of this Calendar
is after the
* time represented by the argument.
* @exception NullPointerException if the specified Calendar
is
* null
.
* @exception IllegalArgumentException if the time value of the
* specified Calendar
object can't be obtained due to
* any invalid calendar values.
* @since 1.5
*/
@Override
public int compareTo(Calendar anotherCalendar) {
return compareTo(getMillisOf(anotherCalendar));
}
/**
* Adds or subtracts the specified amount of time to the given calendar field,
* based on the calendar's rules. For example, to subtract 5 days from
* the current time of the calendar, you can achieve it by calling:
* add(Calendar.DAY_OF_MONTH, -5)
.
*
* @param field the calendar field.
* @param amount the amount of date or time to be added to the field.
* @see #roll(int,int)
* @see #set(int,int)
*/
abstract public void add(int field, int amount);
/**
* Adds or subtracts (up/down) a single unit of time on the given time
* field without changing larger fields. For example, to roll the current
* date up by one day, you can achieve it by calling:
* getMaximum(Calendar.YEAR)
.
* When rolling on the month or Calendar.MONTH field, other fields like
* date might conflict and, need to be changed. For instance,
* rolling the month on the date 01/31/96 will result in 02/29/96.
* When rolling on the hour-in-day or Calendar.HOUR_OF_DAY field, it will
* roll the hour value in the range between 0 and 23, which is zero-based.
*
* @param field the time field.
* @param up indicates if the value of the specified time field is to be
* rolled up or rolled down. Use true if rolling up, false otherwise.
* @see Calendar#add(int,int)
* @see Calendar#set(int,int)
*/
abstract public void roll(int field, boolean up);
/**
* Adds the specified (signed) amount to the specified calendar field
* without changing larger fields. A negative amount means to roll
* down.
*
* Calendar
just repeatedly calls the
* version of {@link #roll(int,boolean) roll()} that rolls by one unit. This may not
* always do the right thing. For example, if the DAY_OF_MONTH
field is 31,
* rolling through February will leave it set to 28. The GregorianCalendar
* version of this function takes care of this problem. Other subclasses
* should also provide overrides of this function that do the right thing.
*
* @param field the calendar field.
* @param amount the signed amount to add to the calendar field
.
* @since 1.2
* @see #roll(int,boolean)
* @see #add(int,int)
* @see #set(int,int)
*/
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 with the given time zone value.
*
* @param value the given time zone.
*/
public void setTimeZone(TimeZone value)
{
zone = value;
sharedZone = false;
/* Recompute the fields from the time using the new zone. This also
* works if isTimeSet is false (after a call to set()). In that case
* the time will be computed from the fields using the new zone, then
* the fields will get recomputed from that. Consider the sequence of
* calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
* Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. More
* generally, a call to setTimeZone() affects calls to set() BEFORE AND
* AFTER it up to the next call to complete().
*/
areAllFieldsSet = areFieldsSet = false;
}
/**
* Gets the time zone.
*
* @return the time zone object associated with this calendar.
*/
public TimeZone getTimeZone()
{
// If the TimeZone object is shared by other Calendar instances, then
// create a clone.
if (sharedZone) {
zone = (TimeZone) zone.clone();
sharedZone = false;
}
return zone;
}
/**
* Returns the time zone (without cloning).
*/
TimeZone getZone() {
return zone;
}
/**
* Sets the sharedZone flag to shared
.
*/
void setZoneShared(boolean shared) {
sharedZone = shared;
}
/**
* Specifies whether or not date/time interpretation is to be lenient. With
* lenient interpretation, a date such as "February 942, 1996" will be
* treated as being equivalent to the 941st day after February 1, 1996.
* With strict (non-lenient) interpretation, such dates will cause an exception to be
* thrown. The default is lenient.
*
* @param lenient true
if the lenient mode is to be turned
* on; false
if it is to be turned off.
* @see #isLenient()
* @see java.text.DateFormat#setLenient
*/
public void setLenient(boolean lenient)
{
this.lenient = lenient;
}
/**
* Tells whether date/time interpretation is to be lenient.
*
* @return true
if the interpretation mode of this calendar is lenient;
* false
otherwise.
* @see #setLenient(boolean)
*/
public boolean isLenient()
{
return lenient;
}
/**
* Sets what the first day of the week is; e.g., SUNDAY
in the U.S.,
* MONDAY
in France.
*
* @param value the given first day of the week.
* @see #getFirstDayOfWeek()
* @see #getMinimalDaysInFirstWeek()
*/
public void setFirstDayOfWeek(int value)
{
if (firstDayOfWeek == value) {
return;
}
firstDayOfWeek = value;
invalidateWeekFields();
}
/**
* Gets what the first day of the week is; e.g., SUNDAY
in the U.S.,
* MONDAY
in France.
*
* @return the first day of the week.
* @see #setFirstDayOfWeek(int)
* @see #getMinimalDaysInFirstWeek()
*/
public int getFirstDayOfWeek()
{
return firstDayOfWeek;
}
/**
* Sets what the minimal days required in the first week of the year are;
* For example, if the first week is defined as one that contains the first
* day of the first month of a year, call this method with value 1. If it
* must be a full week, use value 7.
*
* @param value the given minimal days required in the first week
* of the year.
* @see #getMinimalDaysInFirstWeek()
*/
public void setMinimalDaysInFirstWeek(int value)
{
if (minimalDaysInFirstWeek == value) {
return;
}
minimalDaysInFirstWeek = value;
invalidateWeekFields();
}
/**
* Gets what the minimal days required in the first week of the year are;
* e.g., if the first week is defined as one that contains the first day
* of the first month of a year, this method returns 1. If
* the minimal days required must be a full week, this method
* returns 7.
*
* @return the minimal days required in the first week of the year.
* @see #setMinimalDaysInFirstWeek(int)
*/
public int getMinimalDaysInFirstWeek()
{
return minimalDaysInFirstWeek;
}
/**
* Returns whether this {@code Calendar} supports week dates.
*
* Calendar
instance. The minimum value is defined as
* the smallest value returned by the {@link #get(int) get} method
* for any possible time value. The minimum value depends on
* calendar system specific parameters of the instance.
*
* @param field the calendar field.
* @return the minimum value for the given calendar field.
* @see #getMaximum(int)
* @see #getGreatestMinimum(int)
* @see #getLeastMaximum(int)
* @see #getActualMinimum(int)
* @see #getActualMaximum(int)
*/
abstract public int getMinimum(int field);
/**
* Returns the maximum value for the given calendar field of this
* Calendar
instance. The maximum value is defined as
* the largest value returned by the {@link #get(int) get} method
* for any possible time value. The maximum value depends on
* calendar system specific parameters of the instance.
*
* @param field the calendar field.
* @return the maximum value for the given calendar field.
* @see #getMinimum(int)
* @see #getGreatestMinimum(int)
* @see #getLeastMaximum(int)
* @see #getActualMinimum(int)
* @see #getActualMaximum(int)
*/
abstract public int getMaximum(int field);
/**
* Returns the highest minimum value for the given calendar field
* of this Calendar
instance. The highest minimum
* value is defined as the largest value returned by {@link
* #getActualMinimum(int)} for any possible time value. The
* greatest minimum value depends on calendar system specific
* parameters of the instance.
*
* @param field the calendar field.
* @return the highest minimum value for the given calendar field.
* @see #getMinimum(int)
* @see #getMaximum(int)
* @see #getLeastMaximum(int)
* @see #getActualMinimum(int)
* @see #getActualMaximum(int)
*/
abstract public int getGreatestMinimum(int field);
/**
* Returns the lowest maximum value for the given calendar field
* of this Calendar
instance. The lowest maximum
* value is defined as the smallest value returned by {@link
* #getActualMaximum(int)} for any possible time value. The least
* maximum value depends on calendar system specific parameters of
* the instance. For example, a Calendar
for the
* Gregorian calendar system returns 28 for the
* DAY_OF_MONTH
field, because the 28th is the last
* day of the shortest month of this calendar, February in a
* common year.
*
* @param field the calendar field.
* @return the lowest maximum value for the given calendar field.
* @see #getMinimum(int)
* @see #getMaximum(int)
* @see #getGreatestMinimum(int)
* @see #getActualMinimum(int)
* @see #getActualMaximum(int)
*/
abstract public int getLeastMaximum(int field);
/**
* Returns the minimum value that the specified calendar field
* could have, given the time value of this Calendar
.
*
* getMinimum()
.
*
* @param field the calendar field
* @return the minimum of the given calendar field for the time
* value of this Calendar
* @see #getMinimum(int)
* @see #getMaximum(int)
* @see #getGreatestMinimum(int)
* @see #getLeastMaximum(int)
* @see #getActualMaximum(int)
* @since 1.2
*/
public int getActualMinimum(int field) {
int fieldValue = getGreatestMinimum(field);
int endValue = getMinimum(field);
// if we know that the minimum value is always the same, just return it
if (fieldValue == endValue) {
return fieldValue;
}
// clone the calendar so we don't mess with the real one, and set it to
// accept anything for the field values
Calendar work = (Calendar)this.clone();
work.setLenient(true);
// now try each value from getLeastMaximum() to getMaximum() one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual minimum for the current date
int result = fieldValue;
do {
work.set(field, fieldValue);
if (work.get(field) != fieldValue) {
break;
} else {
result = fieldValue;
fieldValue--;
}
} while (fieldValue >= endValue);
return result;
}
/**
* Returns the maximum value that the specified calendar field
* could have, given the time value of this
* Calendar
. For example, the actual maximum value of
* the MONTH
field is 12 in some years, and 13 in
* other years in the Hebrew calendar system.
*
* Calendar
* @see #getMinimum(int)
* @see #getMaximum(int)
* @see #getGreatestMinimum(int)
* @see #getLeastMaximum(int)
* @see #getActualMinimum(int)
* @since 1.2
*/
public int getActualMaximum(int field) {
int fieldValue = getLeastMaximum(field);
int endValue = getMaximum(field);
// if we know that the maximum value is always the same, just return it.
if (fieldValue == endValue) {
return fieldValue;
}
// clone the calendar so we don't mess with the real one, and set it to
// accept anything for the field values.
Calendar work = (Calendar)this.clone();
work.setLenient(true);
// if we're counting weeks, set the day of the week to Sunday. We know the
// last week of a month or year will contain the first day of the week.
if (field == WEEK_OF_YEAR || field == WEEK_OF_MONTH) {
work.set(DAY_OF_WEEK, firstDayOfWeek);
}
// now try each value from getLeastMaximum() to getMaximum() one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
int result = fieldValue;
do {
work.set(field, fieldValue);
if (work.get(field) != fieldValue) {
break;
} else {
result = fieldValue;
fieldValue++;
}
} while (fieldValue <= endValue);
return result;
}
/**
* Creates and returns a copy of this object.
*
* @return a copy of this object.
*/
@Override
public Object clone()
{
try {
Calendar other = (Calendar) super.clone();
other.fields = new int[FIELD_COUNT];
other.isSet = new boolean[FIELD_COUNT];
other.stamp = new int[FIELD_COUNT];
for (int i = 0; i < FIELD_COUNT; i++) {
other.fields[i] = fields[i];
other.stamp[i] = stamp[i];
other.isSet[i] = isSet[i];
}
other.zone = (TimeZone) zone.clone();
return other;
}
catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
private static final String[] FIELD_NAME = {
"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 the name of the specified calendar field.
*
* @param field the calendar field
* @return the calendar field name
* @exception IndexOutOfBoundsException if field
is negative,
* equal to or greater then FIELD_COUNT
.
*/
static String getFieldName(int field) {
return FIELD_NAME[field];
}
/**
* Return a string representation of this calendar. This method
* is intended to be used only for debugging purposes, and the
* format of the returned string may vary between implementations.
* The returned string may be empty but may not be null
.
*
* @return a string representation of this calendar.
*/
@Override
public String toString() {
// NOTE: BuddhistCalendar.toString() interprets the string
// produced by this method so that the Gregorian year number
// is substituted by its B.E. year value. It relies on
// "...,YEAR=Calendar
would only write out its state data and
* the current time, and not write any field data out, such as
* fields[]
, isTimeSet
, areFieldsSet
,
* and isSet[]
. nextStamp
also should not be part
* of the persistent state. Unfortunately, this didn't happen before JDK 1.1
* shipped. To be compatible with JDK 1.1, we will always have to write out
* the field values and state flags. However, nextStamp
can be
* removed from the serialization stream; this will probably happen in the
* near future.
*/
private synchronized void writeObject(ObjectOutputStream stream)
throws IOException
{
// Try to compute the time correctly, for the future (stream
// version 2) in which we don't write out fields[] or isSet[].
if (!isTimeSet) {
try {
updateTime();
}
catch (IllegalArgumentException e) {}
}
// If this Calendar has a ZoneInfo, save it and set a
// SimpleTimeZone equivalent (as a single DST schedule) for
// backward compatibility.
TimeZone savedZone = null;
if (zone instanceof ZoneInfo) {
SimpleTimeZone stz = ((ZoneInfo)zone).getLastRuleInstance();
if (stz == null) {
stz = new SimpleTimeZone(zone.getRawOffset(), zone.getID());
}
savedZone = zone;
zone = stz;
}
// Write out the 1.1 FCS object.
stream.defaultWriteObject();
// Write out the ZoneInfo object
// 4802409: we write out even if it is null, a temporary workaround
// the real fix for bug 4844924 in corba-iiop
stream.writeObject(savedZone);
if (savedZone != null) {
zone = savedZone;
}
}
private static class CalendarAccessControlContext {
private static final AccessControlContext INSTANCE;
static {
RuntimePermission perm = new RuntimePermission("accessClassInPackage.sun.util.calendar");
PermissionCollection perms = perm.newPermissionCollection();
perms.add(perm);
INSTANCE = new AccessControlContext(new ProtectionDomain[] {
new ProtectionDomain(null, perms)
});
}
private CalendarAccessControlContext() {
}
}
/**
* Reconstitutes this object from a stream (i.e., deserialize it).
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
final ObjectInputStream input = stream;
input.defaultReadObject();
stamp = new int[FIELD_COUNT];
// Starting with version 2 (not implemented yet), we expect that
// fields[], isSet[], isTimeSet, and areFieldsSet may not be
// streamed out anymore. We expect 'time' to be correct.
if (serialVersionOnStream >= 2)
{
isTimeSet = true;
if (fields == null) {
fields = new int[FIELD_COUNT];
}
if (isSet == null) {
isSet = new boolean[FIELD_COUNT];
}
}
else if (serialVersionOnStream >= 0)
{
for (int i=0; i