/* GENERATED SOURCE. DO NOT MODIFY. */ /* * Copyright (C) 1996-2015, International Business Machines * Corporation and others. All Rights Reserved. */ package android.icu.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.StringCharacterIterator; import java.util.ArrayList; import java.util.Date; import java.util.Locale; import java.util.MissingResourceException; import android.icu.impl.CalendarData; import android.icu.impl.CalendarUtil; import android.icu.impl.ICUCache; import android.icu.impl.ICUResourceBundle; import android.icu.impl.SimpleCache; import android.icu.impl.SoftCache; import android.icu.text.DateFormat; import android.icu.text.DateFormatSymbols; import android.icu.text.MessageFormat; import android.icu.text.SimpleDateFormat; import android.icu.util.ULocale.Category; /** * [icu enhancement] ICU's replacement for {@link java.util.Calendar}. Methods, fields, and other functionality specific to ICU are labeled '[icu]'. * *
Calendar
is an abstract base class for converting between
* a Date
object and a set of integer fields such as
* YEAR
, MONTH
, DAY
, HOUR
,
* and so on. (A Date
object represents a specific instant in
* time with millisecond precision. See
* {@link Date}
* for information about the Date
class.)
*
*
Subclasses of Calendar
interpret a Date
* according to the rules of a specific calendar system. ICU4J contains
* several subclasses implementing different international calendar systems.
*
*
* 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 of a type appropriate to the locale, whose
* time fields have been initialized with the current date and time:
*
** *Calendar rightNow = Calendar.getInstance()*
When a ULocale
is used by getInstance
, its
* 'calendar
' tag and value are retrieved if present. If a recognized
* value is supplied, a calendar is provided and configured as appropriate.
* Currently recognized tags are "buddhist", "chinese", "coptic", "ethiopic",
* "gregorian", "hebrew", "islamic", "islamic-civil", "japanese", and "roc". For
* example:
*will return an instance of JapaneseCalendar (using en_US conventions for * minimum days in first week, start day of week, et cetera). * *Calendar cal = Calendar.getInstance(new ULocale("en_US@calendar=japanese"));*
A Calendar
object can produce all the time 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 fields,
* as well as their meaning. For example, the first month of the year has value
* MONTH
== JANUARY
for all calendars. Other values
* are defined by the concrete subclass, such as ERA
and
* YEAR
. See individual field documentation and subclass
* documentation for details.
*
*
When a Calendar
is lenient, it accepts a wider range
* of field values than it produces. For example, a lenient
* GregorianCalendar
interprets MONTH
==
* JANUARY
, DAY_OF_MONTH
== 32 as February 1. A
* non-lenient GregorianCalendar
throws an exception when given
* out-of-range field settings. When calendars recompute field values for
* return by get()
, they normalize them. For example, a
* GregorianCalendar
always produces DAY_OF_MONTH
* values between 1 and the length of the month.
*
*
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 API.
*
*
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.
*
*
When computing a Date
from time fields, some special
* circumstances may arise: there may be insufficient information to compute the
* Date
(such as only year and month but no day in the month),
* there may be inconsistent information (such as "Tuesday, July 15, 1996" --
* July 15, 1996 is actually a Monday), or the input time might be ambiguous
* because of time zone transition.
*
*
Insufficient information. The calendar will use default * information to specify the missing fields. This may vary by calendar; for * the Gregorian calendar, the default for a field is the same as that of the * start of the epoch: i.e., YEAR = 1970, MONTH = JANUARY, DATE = 1, etc. * *
Inconsistent information. If fields conflict, the calendar * will give preference to fields set more recently. For example, when * determining the day, the calendar will look for one of the following * combinations of fields. The most recent combination, as determined by the * most recently set single field, will be used. * *
** * For the time of day: * ** MONTH + DAY_OF_MONTH * MONTH + WEEK_OF_MONTH + DAY_OF_WEEK * MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK * DAY_OF_YEAR * DAY_OF_WEEK + WEEK_OF_YEAR*
** ** HOUR_OF_DAY * AM_PM + HOUR*
Ambiguous Wall Clock Time. When time offset from UTC has * changed, it produces an ambiguous time slot around the transition. For example, * many US locations observe daylight saving time. On the date switching to daylight * saving time in US, wall clock time jumps from 12:59 AM (standard) to 2:00 AM * (daylight). Therefore, wall clock time from 1:00 AM to 1:59 AM do not exist on * the date. When the input wall time fall into this missing time slot, the ICU * Calendar resolves the time using the UTC offset before the transition by default. * In this example, 1:30 AM is interpreted as 1:30 AM standard time (non-exist), * so the final result will be 2:30 AM daylight time. * *
On the date switching back to standard time, wall clock time is moved back one * hour at 2:00 AM. So wall clock time from 1:00 AM to 1:59 AM occur twice. In this * case, the ICU Calendar resolves the time using the UTC offset after the transition * by default. For example, 1:30 AM on the date is resolved as 1:30 AM standard time. * *
Ambiguous wall clock time resolution behaviors can be customized by Calendar APIs * {@link #setRepeatedWallTimeOption(int)} and {@link #setSkippedWallTimeOption(int)}. * These methods are available in ICU 49 or later versions. * *
Note: for some non-Gregorian calendars, different * fields may be necessary for complete disambiguation. For example, a full * specification of the historial Arabic astronomical calendar requires year, * month, day-of-month and day-of-week in some cases. * *
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. * *
Field manipulation methods
* *Calendar
fields can be changed using three methods:
* set()
, add()
, and roll()
.
set(f, value)
changes field
* f
to value
. In addition, it sets an
* internal member variable to indicate that field f
has
* been changed. Although field f
is changed immediately,
* the calendar's milliseconds is not recomputed until the next call to
* get()
, getTime()
, or
* getTimeInMillis()
is made. Thus, multiple calls to
* set()
do not trigger multiple, unnecessary
* computations. As a result of changing a field using
* set()
, other fields may also change, depending on the
* field, the field value, and the calendar system. In addition,
* get(f)
will not necessarily return value
* after the 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 calendar 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 calendar 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, 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: Consider a GregorianCalendar
* originally set to August 31, 1999. Calling roll(Calendar.MONTH,
* 8)
sets the calendar to April 30, 1999. Add
* rule 1 sets the MONTH
field to April. Using a
* GregorianCalendar
, the DAY_OF_MONTH
cannot
* be 31 in the month April. Add rule 2 sets it to the closest possible
* value, 30. Finally, the roll rule maintains the
* YEAR
field value of 1999.
Example: Consider a GregorianCalendar
* originally set to Sunday June 6, 1999. Calling
* roll(Calendar.WEEK_OF_MONTH, -1)
sets the calendar to
* Tuesday June 1, 1999, whereas calling
* add(Calendar.WEEK_OF_MONTH, -1)
sets the calendar to
* Sunday May 30, 1999. This is because the roll rule imposes an
* additional constraint: The MONTH
must not change when the
* WEEK_OF_MONTH
is rolled. Taken together with add rule 1,
* the resultant date must be between Tuesday June 1 and Saturday June
* 5. According to add rule 2, the DAY_OF_WEEK
, an invariant
* when changing the WEEK_OF_MONTH
, is set to Tuesday, the
* closest possible value to Sunday (where Sunday is the first day of the
* week).
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.
Note: You should always use {@link #roll roll} and {@link #add add} rather * than attempting to perform arithmetic operations directly on the fields * of a Calendar. It is quite possible for Calendar subclasses * to have fields with non-linear behavior, for example missing months * or days during non-leap years. The subclasses' add and roll * methods will take this into account, while simple arithmetic manipulations * may give invalid results. * *
Calendar Architecture in ICU4J
* *Recently the implementation of Calendar
has changed
* significantly in order to better support subclassing. The original
* Calendar
class was designed to support subclassing, but
* it had only one implemented subclass, GregorianCalendar
.
* With the implementation of several new calendar subclasses, including
* the BuddhistCalendar
, ChineseCalendar
,
* HebrewCalendar
, IslamicCalendar
, and
* JapaneseCalendar
, the subclassing API has been reworked
* thoroughly. This section details the new subclassing API and other
* ways in which android.icu.util.Calendar
differs from
* java.util.Calendar
.
*
Changes
* *Overview of changes between the classic Calendar
* architecture and the new architecture.
*
*
fields[]
array is private
now
* instead of protected
. Subclasses must access it
* using the methods {@link #internalSet} and
* {@link #internalGet}. Motivation: Subclasses should
* not directly access data members.time
long word is private
now
* instead of protected
. Subclasses may access it using
* the method {@link #internalGetTimeInMillis}, which does not
* provoke an update. Motivation: Subclasses should not
* directly access data members.Calendar
base class. As a result, it is much easier
* to subclass Calendar
. Motivation: Subclasses
* should not have to reimplement common code. Certain behaviors are
* common across calendar systems: The definition and behavior of
* week-related fields and time fields, the arithmetic
* ({@link #add(int, int) add} and {@link #roll(int, int) roll}) behavior of many
* fields, and the field validation system.Calendar
base class contains some Gregorian
* calendar algorithmic support that subclasses can use (specifically
* in {@link #handleComputeFields}). Subclasses can use the
* methods getGregorianXxx()
to obtain precomputed
* values. Motivation: This is required by all
* Calendar
subclasses in order to implement consistent
* time zone behavior, and Gregorian-derived systems can use the
* already computed data.FIELD_COUNT
constant has been removed. Use
* {@link #getFieldCount}. In addition, framework API has been
* added to allow subclasses to define additional fields.
* Motivation: The number of fields is not constant across
* calendar systems.Date(Long.MIN_VALUE)
or
* Date(Long.MAX_VALUE)
. Instead, the
* Calendar
protected constants should be used.
* Motivation: With
* the addition of the {@link #JULIAN_DAY} field, Julian day
* numbers must be restricted to a 32-bit int
. This
* restricts the overall supported range. Furthermore, restricting
* the supported range simplifies the computations by removing
* special case code that was used to accomodate arithmetic overflow
* at millis near Long.MIN_VALUE
and
* Long.MAX_VALUE
.Calendar
.DateFormat
.Subclass API
* *The original Calendar
API was based on the experience
* of implementing a only a single subclass,
* GregorianCalendar
. As a result, all of the subclassing
* kinks had not been worked out. The new subclassing API has been
* refined based on several implemented subclasses. This includes methods
* that must be overridden and methods for subclasses to call. Subclasses
* no longer have direct access to fields
and
* stamp
. Instead, they have new API to access
* these. Subclasses are able to allocate the fields
array
* through a protected framework method; this allows subclasses to
* specify additional fields.
More functionality has been moved into the base class. The base * class now contains much of the computational machinery to support the * Gregorian calendar. This is based on two things: (1) Many calendars * are based on the Gregorian calendar (such as the Buddhist and Japanese * imperial calendars). (2) All calendars require basic * Gregorian support in order to handle timezone computations.
* *Common computations have been moved into
* Calendar
. Subclasses no longer compute the week related
* fields and the time related fields. These are commonly handled for all
* calendars by the base class.
Subclass computation of time => fields * *
The {@link #ERA}, {@link #YEAR},
* {@link #EXTENDED_YEAR}, {@link #MONTH},
* {@link #DAY_OF_MONTH}, and {@link #DAY_OF_YEAR} fields are
* computed by the subclass, based on the Julian day. All other fields
* are computed by Calendar
.
*
*
Calendar
,
* they must also be computed. These are the only fields that the
* subclass should compute. All other fields are computed by the base
* class, so time and week fields behave in a consistent way across
* all calendars. The default version of this method in
* Calendar
implements a proleptic Gregorian
* calendar. Within this method, subclasses may call
* getGregorianXxx()
to obtain the Gregorian calendar
* month, day of month, and extended year for the given date.Subclass computation of fields => time * *
The interpretation of most field values is handled entirely by
* Calendar
. Calendar
determines which fields
* are set, which are not, which are set more recently, and so on. In
* addition, Calendar
handles the computation of the time
* from the time fields and handles the week-related fields. The only
* thing the subclass must do is determine the extended year, based on
* the year fields, and then, given an extended year and a month, it must
* return a Julian day number.
*
*
Other methods * *
limitType
. This method only needs to handle the
* fields {@link #ERA}, {@link #YEAR}, {@link #MONTH},
* {@link #WEEK_OF_YEAR}, {@link #WEEK_OF_MONTH},
* {@link #DAY_OF_MONTH}, {@link #DAY_OF_YEAR},
* {@link #DAY_OF_WEEK_IN_MONTH}, {@link #YEAR_WOY}, and
* {@link #EXTENDED_YEAR}. Other fields are invariant (with
* respect to calendar system) and are handled by the base
* class.IllegalArgumentException
. The method may call
* super.validateField(field)
to handle fields in a
* generic way, that is, to compare them to the range
* getMinimum(field)
..getMaximum(field)
.int[]
* array large enough to hold the calendar's fields. This is only
* necessary if the calendar defines additional fields beyond those
* defined by Calendar
. The length of the result must be
* be between the base and maximum field counts.DateFormat
appropriate to this calendar. This is only
* required if a calendar subclass redefines the use of a field (for
* example, changes the {@link #ERA} field from a symbolic field
* to a numeric one) or defines an additional field.Normalized behavior * *
The behavior of certain fields has been made consistent across all
* calendar systems and implemented in Calendar
.
*
*
Calendar
and to maintain basic correpsondences
* between calendar systems. Affected fields: {@link #AM_PM},
* {@link #HOUR}, {@link #HOUR_OF_DAY}, {@link #MINUTE},
* {@link #SECOND}, {@link #MILLISECOND},
* {@link #ZONE_OFFSET}, and {@link #DST_OFFSET}.GregorianCalendar
fields: the
* {@link #YEAR}, {@link #MONTH}, and
* {@link #DAY_OF_MONTH}. As a result, Calendar
* always computes these fields, even for non-Gregorian calendar
* systems. These fields are available to subclasses.WEEK_OF_YEAR,
* WEEK_OF_MONTH
, {@link #DAY_OF_WEEK_IN_MONTH},
* {@link #DOW_LOCAL}, {@link #YEAR_WOY} are all computed in
* a consistent way in the base class, based on the
* {@link #EXTENDED_YEAR}, {@link #DAY_OF_YEAR},
* {@link #MONTH}, and {@link #DAY_OF_MONTH}, which are
* computed by the subclass.Supported range * *
The allowable range of Calendar
has been
* narrowed. GregorianCalendar
used to attempt to support
* the range of dates with millisecond values from
* Long.MIN_VALUE
to Long.MAX_VALUE
. This
* introduced awkward constructions (hacks) which slowed down
* performance. It also introduced non-uniform behavior at the
* boundaries. The new Calendar
protocol specifies the
* maximum range of supportable dates as those having Julian day numbers
* of -0x7F000000
to +0x7F000000
. This
* corresponds to years from ~5,800,000 BCE to ~5,800,000 CE. Programmers
* should use the protected constants in Calendar
to
* specify an extremely early or extremely late date.
General notes * *
GregorianCalendar
class supports
* dates before the historical onset of the calendar by extending the
* calendar system backward in time. Similarly, the
* HebrewCalendar
extends backward before the start of
* its epoch into zero and negative years. Subclasses do not throw
* exceptions because a date precedes the historical start of a
* calendar system. Instead, they implement
* {@link #handleGetLimit} to return appropriate limits on
* {@link #YEAR}, {@link #ERA}, etc. fields. Then, if the
* calendar is set to not be lenient, out-of-range field values will
* trigger an exception.ERA==AD ? YEAR : 1-YEAR
. Another example is the Mayan
* long count, which has years (KUN
) and nested cycles
* of years (KATUN
and BAKTUN
). The Mayan
* {@link #EXTENDED_YEAR} is computed as TUN + 20 * (KATUN
* + 20 * BAKTUN)
. The Calendar
base class uses
* the {@link #EXTENDED_YEAR} field to compute the week-related
* fields.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 is
* JANUARY
; 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 {@link #getFirstDayOfWeek()} and
* {@link #getMinimalDaysInFirstWeek()}, has value 1. Subclasses define
* the value of {@link #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 {@link #getFirstDayOfWeek()} and
* {@link #getMinimalDaysInFirstWeek()}, has value 1. Subclasses define
* the value of {@link #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 {@link #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 {@link #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 {@link #SUNDAY},
* {@link #MONDAY}, {@link #TUESDAY}, {@link #WEDNESDAY},
* {@link #THURSDAY}, {@link #FRIDAY}, and {@link #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 {@link #DAY_OF_WEEK} field, this uniquely specifies a day
* within a month. Unlike {@link #WEEK_OF_MONTH} and
* {@link #WEEK_OF_YEAR}, this field's value does not depend on
* {@link #getFirstDayOfWeek()} or
* {@link #getMinimalDaysInFirstWeek()}. DAY_OF_MONTH 1
* through 7
always correspond to DAY_OF_WEEK_IN_MONTH
* 1
; 8
through 15
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.
* 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.
*/
public final static int ZONE_OFFSET = 15;
/**
* Field number for get
and set
indicating the
* daylight savings offset in milliseconds.
*/
public final static int DST_OFFSET = 16;
/**
* [icu] Field number for get()
and set()
* indicating the extended year corresponding to the
* {@link #WEEK_OF_YEAR} field. This may be one greater or less
* than the value of {@link #EXTENDED_YEAR}.
*/
public static final int YEAR_WOY = 17;
/**
* [icu] Field number for get()
and set()
* indicating the localized day of week. This will be a value from 1
* to 7 inclusive, with 1 being the localized first day of the week.
*/
public static final int DOW_LOCAL = 18;
/**
* [icu] Field number for get()
and set()
* indicating the extended year. This is a single number designating
* the year of this calendar system, encompassing all supra-year
* fields. For example, for the Julian calendar system, year numbers
* are positive, with an era of BCE or CE. An extended year value for
* the Julian calendar system assigns positive values to CE years and
* negative values to BCE years, with 1 BCE being year 0.
*/
public static final int EXTENDED_YEAR = 19;
/**
* [icu] Field number for get()
and set()
* indicating the modified Julian day number. This is different from
* the conventional Julian day number in two regards. First, it
* demarcates days at local zone midnight, rather than noon GMT.
* Second, it is a local number; that is, it depends on the local time
* zone. It can be thought of as a single number that encompasses all
* the date-related fields.
*/
public static final int JULIAN_DAY = 20;
/**
* [icu] Field number for get()
and set()
* indicating the milliseconds in the day. This ranges from 0 to
* 23:59:59.999 (regardless of DST). This field behaves
* exactly like a composite of all time-related fields, not
* including the zone fields. As such, it also reflects
* discontinuities of those fields on DST transition days. On a day of
* DST onset, it will jump forward. On a day of DST cessation, it will
* jump backward. This reflects the fact that is must be combined with
* the DST_OFFSET field to obtain a unique local time value.
*/
public static final int MILLISECONDS_IN_DAY = 21;
/**
* [icu] Field indicating whether or not the current month is a leap month.
* Should have a value of 0 for non-leap months, and 1 for leap months.
*/
public static final int IS_LEAP_MONTH = 22;
/**
* The number of fields defined by this class. Subclasses may define
* addition fields starting with this number.
*/
protected static final int BASE_FIELD_COUNT = 23;
/**
* The maximum number of fields possible. Subclasses must not define
* more total fields than this number.
*/
protected static final int MAX_FIELD_COUNT = 32;
/**
* Value of the DAY_OF_WEEK
field indicating
* Sunday.
*/
public final static int SUNDAY = 1;
/**
* Value of the DAY_OF_WEEK
field indicating
* Monday.
*/
public final static int MONDAY = 2;
/**
* Value of the DAY_OF_WEEK
field indicating
* Tuesday.
*/
public final static int TUESDAY = 3;
/**
* Value of the DAY_OF_WEEK
field indicating
* Wednesday.
*/
public final static int WEDNESDAY = 4;
/**
* Value of the DAY_OF_WEEK
field indicating
* Thursday.
*/
public final static int THURSDAY = 5;
/**
* Value of the DAY_OF_WEEK
field indicating
* Friday.
*/
public final static int FRIDAY = 6;
/**
* Value of the DAY_OF_WEEK
field indicating
* Saturday.
*/
public final static int SATURDAY = 7;
/**
* Value of the MONTH
field indicating the
* first month of the year.
*/
public final static int JANUARY = 0;
/**
* Value of the MONTH
field indicating the
* second month of the year.
*/
public final static int FEBRUARY = 1;
/**
* Value of the MONTH
field indicating the
* third month of the year.
*/
public final static int MARCH = 2;
/**
* Value of the MONTH
field indicating the
* fourth month of the year.
*/
public final static int APRIL = 3;
/**
* Value of the MONTH
field indicating the
* fifth month of the year.
*/
public final static int MAY = 4;
/**
* Value of the MONTH
field indicating the
* sixth month of the year.
*/
public final static int JUNE = 5;
/**
* Value of the MONTH
field indicating the
* seventh month of the year.
*/
public final static int JULY = 6;
/**
* Value of the MONTH
field indicating the
* eighth month of the year.
*/
public final static int AUGUST = 7;
/**
* Value of the MONTH
field indicating the
* ninth month of the year.
*/
public final static int SEPTEMBER = 8;
/**
* Value of the MONTH
field indicating the
* tenth month of the year.
*/
public final static int OCTOBER = 9;
/**
* Value of the MONTH
field indicating the
* eleventh month of the year.
*/
public final static int NOVEMBER = 10;
/**
* Value of the MONTH
field indicating the
* twelfth month of the year.
*/
public final static int DECEMBER = 11;
/**
* Value of the MONTH
field indicating the
* thirteenth month of the year. Although {@link GregorianCalendar}
* does not use this value, lunar calendars do.
*/
public final static int UNDECIMBER = 12;
/**
* Value of the AM_PM
field indicating the
* period of the day from midnight to just before noon.
*/
public final static int AM = 0;
/**
* Value of the AM_PM
field indicating the
* period of the day from noon to just before midnight.
*/
public final static int PM = 1;
/**
* [icu] Value returned by getDayOfWeekType(int dayOfWeek) to indicate a
* weekday.
* @see #WEEKEND
* @see #WEEKEND_ONSET
* @see #WEEKEND_CEASE
* @see #getDayOfWeekType
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public static final int WEEKDAY = 0;
/**
* [icu] Value returned by getDayOfWeekType(int dayOfWeek) to indicate a
* weekend day.
* @see #WEEKDAY
* @see #WEEKEND_ONSET
* @see #WEEKEND_CEASE
* @see #getDayOfWeekType
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public static final int WEEKEND = 1;
/**
* [icu] Value returned by getDayOfWeekType(int dayOfWeek) to indicate a
* day that starts as a weekday and transitions to the weekend.
* Call getWeekendTransition() to get the point of transition.
* @see #WEEKDAY
* @see #WEEKEND
* @see #WEEKEND_CEASE
* @see #getDayOfWeekType
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public static final int WEEKEND_ONSET = 2;
/**
* [icu] Value returned by getDayOfWeekType(int dayOfWeek) to indicate a
* day that starts as the weekend and transitions to a weekday.
* Call getWeekendTransition() to get the point of transition.
* @see #WEEKDAY
* @see #WEEKEND
* @see #WEEKEND_ONSET
* @see #getDayOfWeekType
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public static final int WEEKEND_CEASE = 3;
/**
* [icu]Option used by {@link #setRepeatedWallTimeOption(int)} and
* {@link #setSkippedWallTimeOption(int)} specifying an ambiguous wall time
* to be interpreted as the latest.
* @see #setRepeatedWallTimeOption(int)
* @see #getRepeatedWallTimeOption()
* @see #setSkippedWallTimeOption(int)
* @see #getSkippedWallTimeOption()
*/
public static final int WALLTIME_LAST = 0;
/**
* [icu]Option used by {@link #setRepeatedWallTimeOption(int)} and
* {@link #setSkippedWallTimeOption(int)} specifying an ambiguous wall time
* to be interpreted as the earliest.
* @see #setRepeatedWallTimeOption(int)
* @see #getRepeatedWallTimeOption()
* @see #setSkippedWallTimeOption(int)
* @see #getSkippedWallTimeOption()
*/
public static final int WALLTIME_FIRST = 1;
/**
* [icu]Option used by {@link #setSkippedWallTimeOption(int)} specifying an
* ambiguous wall time to be interpreted as the next valid wall time.
* @see #setSkippedWallTimeOption(int)
* @see #getSkippedWallTimeOption()
*/
public static final int WALLTIME_NEXT_VALID = 2;
/**
* The number of milliseconds in one second.
*/
protected static final int ONE_SECOND = 1000;
/**
* The number of milliseconds in one minute.
*/
protected static final int ONE_MINUTE = 60*ONE_SECOND;
/**
* The number of milliseconds in one hour.
*/
protected static final int ONE_HOUR = 60*ONE_MINUTE;
/**
* The number of milliseconds in one day. Although ONE_DAY and
* ONE_WEEK can fit into ints, they must be longs in order to prevent
* arithmetic overflow when performing (bug 4173516).
*/
protected static final long ONE_DAY = 24*ONE_HOUR;
/**
* The number of milliseconds in one week. Although ONE_DAY and
* ONE_WEEK can fit into ints, they must be longs in order to prevent
* arithmetic overflow when performing (bug 4173516).
*/
protected static final long ONE_WEEK = 7*ONE_DAY;
/**
* The Julian day of the Gregorian epoch, that is, January 1, 1 on the
* Gregorian calendar.
*/
protected static final int JAN_1_1_JULIAN_DAY = 1721426;
/**
* The Julian day of the epoch, that is, January 1, 1970 on the
* Gregorian calendar.
*/
protected static final int EPOCH_JULIAN_DAY = 2440588;
/**
* The minimum supported Julian day. This value is equivalent to
* {@link #MIN_MILLIS} and {@link #MIN_DATE}.
* @see #JULIAN_DAY
*/
protected static final int MIN_JULIAN = -0x7F000000;
/**
* The minimum supported epoch milliseconds. This value is equivalent
* to {@link #MIN_JULIAN} and {@link #MIN_DATE}.
*/
protected static final long MIN_MILLIS = -184303902528000000L;
// Get around bug in jikes 1.12 for now. Later, use:
//protected static final long MIN_MILLIS = (MIN_JULIAN - EPOCH_JULIAN_DAY) * ONE_DAY;
/**
* The minimum supported Date
. This value is equivalent
* to {@link #MIN_JULIAN} and {@link #MIN_MILLIS}.
*/
protected static final Date MIN_DATE = new Date(MIN_MILLIS);
/**
* The maximum supported Julian day. This value is equivalent to
* {@link #MAX_MILLIS} and {@link #MAX_DATE}.
* @see #JULIAN_DAY
*/
protected static final int MAX_JULIAN = +0x7F000000;
/**
* The maximum supported epoch milliseconds. This value is equivalent
* to {@link #MAX_JULIAN} and {@link #MAX_DATE}.
*/
protected static final long MAX_MILLIS = (MAX_JULIAN - EPOCH_JULIAN_DAY) * ONE_DAY;
/**
* The maximum supported Date
. This value is equivalent
* to {@link #MAX_JULIAN} and {@link #MAX_MILLIS}.
*/
protected static final Date MAX_DATE = new Date(MAX_MILLIS);
// Internal notes:
// Calendar contains two kinds of time representations: current "time" in
// milliseconds, and a set of time "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 field values for the currently set time for this calendar.
* This is an array of at least {@link #BASE_FIELD_COUNT} integers.
* @see #handleCreateFields
* @serial
*/
private transient int fields[];
/**
* Pseudo-time-stamps which specify when each field was set. There
* are two special values, UNSET and INTERNALLY_SET. Values from
* MINIMUM_USER_SET to Integer.MAX_VALUE are legal user set values.
*/
private transient int stamp[];
/**
* The currently set time for this calendar, expressed in milliseconds after
* January 1, 1970, 0:00:00 GMT.
* @serial
*/
private 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
*/
private transient 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
*/
private transient boolean areFieldsSet;
/**
* True if all fields have been set. This is only false in a few
* situations: In a newly created, partially constructed object. After
* a call to clear(). In an object just read from a stream using
* readObject(). Once computeFields() has been called this is set to
* true and stays true until one of the above situations recurs.
* @serial
*/
private transient boolean areAllFieldsSet;
/**
* True if all fields have been virtually set, but have not yet been
* computed. This occurs only in setTimeInMillis(), or after readObject().
* A calendar set to this state will compute all fields from the time if it
* becomes necessary, but otherwise will delay such computation.
*/
private transient boolean areFieldsVirtuallySet;
/**
* True if this calendar allows out-of-range field values during computation
* of time
from fields[]
.
* @see #setLenient
* @serial
*/
private boolean lenient = true;
/**
* The {@link TimeZone} used by this calendar. {@link Calendar}
* uses the time zone data to translate between local and GMT time.
* @serial
*/
private TimeZone zone;
/**
* The first day of the week, with possible values {@link #SUNDAY},
* {@link #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;
/**
* First day of the weekend in this calendar's locale. Must be in
* the range SUNDAY...SATURDAY (1..7). The weekend starts at
* weekendOnsetMillis milliseconds after midnight on that day of
* the week. This value is taken from locale resource data.
*/
private int weekendOnset;
/**
* Milliseconds after midnight at which the weekend starts on the
* day of the week weekendOnset. Times that are greater than or
* equal to weekendOnsetMillis are considered part of the weekend.
* Must be in the range 0..24*60*60*1000-1. This value is taken
* from locale resource data.
*/
private int weekendOnsetMillis;
/**
* Day of the week when the weekend stops in this calendar's
* locale. Must be in the range SUNDAY...SATURDAY (1..7). The
* weekend stops at weekendCeaseMillis milliseconds after midnight
* on that day of the week. This value is taken from locale
* resource data.
*/
private int weekendCease;
/**
* Milliseconds after midnight at which the weekend stops on the
* day of the week weekendCease. Times that are greater than or
* equal to weekendCeaseMillis are considered not to be the
* weekend. Must be in the range 0..24*60*60*1000-1. This value
* is taken from locale resource data.
*/
private int weekendCeaseMillis;
/**
* Option used when the specified wall time occurs multiple times.
*/
private int repeatedWallTime = WALLTIME_LAST;
/**
* Option used when the specified wall time does not exist.
*/
private int skippedWallTime = WALLTIME_LAST;
/**
* Value of the time stamp stamp[]
indicating that
* a field has not been set since the last call to clear()
.
* @see #INTERNALLY_SET
* @see #MINIMUM_USER_STAMP
*/
protected static final int UNSET = 0;
/**
* Value of the time stamp stamp[]
indicating that a field
* has been set via computations from the time or from other fields.
* @see #UNSET
* @see #MINIMUM_USER_STAMP
*/
protected static final int INTERNALLY_SET = 1;
/**
* If the time stamp stamp[]
has a value greater than or
* equal to MINIMUM_USER_SET
then it has been set by the
* user via a call to set()
.
* @see #UNSET
* @see #INTERNALLY_SET
*/
protected static final int MINIMUM_USER_STAMP = 2;
/**
* The next available value for stamp[]
, an internal array.
* @serial
*/
private transient int nextStamp = MINIMUM_USER_STAMP;
/* Max value for stamp allowable before recalcution */
private static int STAMP_MAX = 10000;
// 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:
* serialVersionOnStream
* is written.
* @serial
*/
// private int serialVersionOnStream = currentSerialVersion;
// Proclaim serialization compatibility with JDK 1.1
// static final long serialVersionUID = -1807547505821590642L;
// haven't been compatible for awhile, no longer try
// jdk1.4.2 serialver
private static final long serialVersionUID = 6222646104888790989L;
/**
* Bitmask for internalSet() defining which fields may legally be set
* by subclasses. Any attempt to set a field not in this bitmask
* results in an exception, because such fields must be set by the base
* class.
*/
private transient int internalSetMask;
/**
* The Gregorian year, as computed by computeGregorianFields() and
* returned by getGregorianYear().
*/
private transient int gregorianYear;
/**
* The Gregorian month, as computed by computeGregorianFields() and
* returned by getGregorianMonth().
*/
private transient int gregorianMonth;
/**
* The Gregorian day of the year, as computed by
* computeGregorianFields() and returned by getGregorianDayOfYear().
*/
private transient int gregorianDayOfYear;
/**
* The Gregorian day of the month, as computed by
* computeGregorianFields() and returned by getGregorianDayOfMonth().
*/
private transient int gregorianDayOfMonth;
/**
* Constructs a Calendar with the default time zone
* and the default FORMAT
locale.
* @see TimeZone#getDefault
* @see Category#FORMAT
*/
protected Calendar()
{
this(TimeZone.getDefault(), ULocale.getDefault(Category.FORMAT));
}
/**
* 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)
{
this(zone, ULocale.forLocale(aLocale));
}
/**
* Constructs a calendar with the specified time zone and locale.
* @param zone the time zone to use
* @param locale the ulocale for the week data
*/
protected Calendar(TimeZone zone, ULocale locale)
{
this.zone = zone;
// week data
setWeekData(getRegionForCalendar(locale));
// set valid/actual locale
setCalendarLocale(locale);
initInternal();
}
/*
* Set valid/actual locale to this calendar during initialization.
*
* Valid or actual locale does not make much sense for Calendar
* object. An instance of Calendar is initialized by week data
* determine by region and calendar type (either region or keyword).
* Language is not really used for calendar creation.
*/
private void setCalendarLocale(ULocale locale) {
ULocale calLocale = locale;
if (locale.getVariant().length() != 0 || locale.getKeywords() != null) {
// Construct a ULocale, without variant and keywords (except calendar).
StringBuilder buf = new StringBuilder();
buf.append(locale.getLanguage());
String script = locale.getScript();
if (script.length() > 0) {
buf.append("_").append(script);
}
String region = locale.getCountry();
if (region.length() > 0) {
buf.append("_").append(region);
}
String calType = locale.getKeywordValue("calendar");
if (calType != null) {
buf.append("@calendar=").append(calType);
}
calLocale = new ULocale(buf.toString());
}
setLocale(calLocale, calLocale);
}
private void recalculateStamp() {
int index;
int currentValue;
int j, i;
nextStamp = 1;
for (j = 0; j < stamp.length; j++) {
currentValue = STAMP_MAX;
index = -1;
for (i = 0; i < stamp.length; i++) {
if (stamp[i] > nextStamp && stamp[i] < currentValue) {
currentValue = stamp[i];
index = i;
}
}
if (index >= 0) {
stamp[index] = ++nextStamp;
} else {
break;
}
}
nextStamp++;
}
private void initInternal()
{
// Allocate fields through the framework method. Subclasses
// may override this to define additional fields.
fields = handleCreateFields();
///CLOVER:OFF
// todo: fix, difficult to test without subclassing
if (fields == null || fields.length < BASE_FIELD_COUNT ||
fields.length > MAX_FIELD_COUNT) {
throw new IllegalStateException("Invalid fields[]");
}
///CLOVER:ON
stamp = new int[fields.length];
int mask = (1 << ERA) |
(1 << YEAR) |
(1 << MONTH) |
(1 << DAY_OF_MONTH) |
(1 << DAY_OF_YEAR) |
(1 << EXTENDED_YEAR) |
(1 << IS_LEAP_MONTH);
for (int i=BASE_FIELD_COUNT; iNote: Calling The actual maximum computation ignores smaller fields and the
* current value of like-sized fields. For example, the actual maximum
* of the DAY_OF_YEAR or MONTH depends only on the year and supra-year
* fields. The actual maximum of the DAY_OF_MONTH depends, in
* addition, on the MONTH field and any other fields at that
* granularity (such as IS_LEAP_MONTH). The
* DAY_OF_WEEK_IN_MONTH field does not depend on the current
* DAY_OF_WEEK; it returns the maximum for any day of week in the
* current month. Likewise for the WEEK_OF_MONTH and WEEK_OF_YEAR
* fields.
*
* @param field the field whose maximum is desired
* @return the maximum of the given field for the current date of this calendar
* @see #getMaximum
* @see #getLeastMaximum
*/
public int getActualMaximum(int field) {
int result;
switch (field) {
case DAY_OF_MONTH:
{
Calendar cal = (Calendar) clone();
cal.setLenient(true);
cal.prepareGetActual(field, false);
result = handleGetMonthLength(cal.get(EXTENDED_YEAR), cal.get(MONTH));
}
break;
case DAY_OF_YEAR:
{
Calendar cal = (Calendar) clone();
cal.setLenient(true);
cal.prepareGetActual(field, false);
result = handleGetYearLength(cal.get(EXTENDED_YEAR));
}
break;
case ERA:
case DAY_OF_WEEK:
case AM_PM:
case HOUR:
case HOUR_OF_DAY:
case MINUTE:
case SECOND:
case MILLISECOND:
case ZONE_OFFSET:
case DST_OFFSET:
case DOW_LOCAL:
case JULIAN_DAY:
case MILLISECONDS_IN_DAY:
// These fields all have fixed minima/maxima
result = getMaximum(field);
break;
default:
// For all other fields, do it the hard way....
result = getActualHelper(field, getLeastMaximum(field), getMaximum(field));
break;
}
return result;
}
/**
* Returns the minimum value that this field could have, given the current date.
* For most fields, this is the same as {@link #getMinimum getMinimum}
* and {@link #getGreatestMinimum getGreatestMinimum}. However, some fields,
* especially those related to week number, are more complicated.
*
* For example, assume {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* returns 4 and {@link #getFirstDayOfWeek getFirstDayOfWeek} returns SUNDAY.
* If the first day of the month is Sunday, Monday, Tuesday, or Wednesday
* there will be four or more days in the first week, so it will be week number 1,
* and
* @param field the field whose actual minimum value is desired.
* @return the minimum of the given field for the current date of this calendar
*
* @see #getMinimum
* @see #getGreatestMinimum
*/
public int getActualMinimum(int field) {
int result;
switch (field) {
case DAY_OF_WEEK:
case AM_PM:
case HOUR:
case HOUR_OF_DAY:
case MINUTE:
case SECOND:
case MILLISECOND:
case ZONE_OFFSET:
case DST_OFFSET:
case DOW_LOCAL:
case JULIAN_DAY:
case MILLISECONDS_IN_DAY:
// These fields all have fixed minima/maxima
result = getMinimum(field);
break;
default:
// For all other fields, do it the hard way....
result = getActualHelper(field, getGreatestMinimum(field), getMinimum(field));
break;
}
return result;
}
/**
* Prepare this calendar for computing the actual minimum or maximum.
* This method modifies this calendar's fields; it is called on a
* temporary calendar.
*
* Rationale: The semantics of getActualXxx() is to return the
* maximum or minimum value that the given field can take, taking into
* account other relevant fields. In general these other fields are
* larger fields. For example, when computing the actual maximum
* DAY_OF_MONTH, the current value of DAY_OF_MONTH itself is ignored,
* as is the value of any field smaller.
*
* The time fields all have fixed minima and maxima, so we don't
* need to worry about them. This also lets us set the
* MILLISECONDS_IN_DAY to zero to erase any effects the time fields
* might have when computing date fields.
*
* DAY_OF_WEEK is adjusted specially for the WEEK_OF_MONTH and
* WEEK_OF_YEAR fields to ensure that they are computed correctly.
*/
protected void prepareGetActual(int field, boolean isMinimum) {
set(MILLISECONDS_IN_DAY, 0);
switch (field) {
case YEAR:
case EXTENDED_YEAR:
set(DAY_OF_YEAR, getGreatestMinimum(DAY_OF_YEAR));
break;
case YEAR_WOY:
set(WEEK_OF_YEAR, getGreatestMinimum(WEEK_OF_YEAR));
break;
case MONTH:
set(DAY_OF_MONTH, getGreatestMinimum(DAY_OF_MONTH));
break;
case DAY_OF_WEEK_IN_MONTH:
// For dowim, the maximum occurs for the DOW of the first of the
// month.
set(DAY_OF_MONTH, 1);
set(DAY_OF_WEEK, get(DAY_OF_WEEK)); // Make this user set
break;
case WEEK_OF_MONTH:
case WEEK_OF_YEAR:
// If we're counting weeks, set the day of the week to either the
// first or last localized DOW. We know the last week of a month
// or year will contain the first day of the week, and that the
// first week will contain the last DOW.
{
int dow = firstDayOfWeek;
if (isMinimum) {
dow = (dow + 6) % 7; // set to last DOW
if (dow < SUNDAY) {
dow += 7;
}
}
set(DAY_OF_WEEK, dow);
}
break;
}
// Do this last to give it the newest time stamp
set(field, getGreatestMinimum(field));
}
private int getActualHelper(int field, int startValue, int endValue) {
if (startValue == endValue) {
// if we know that the maximum value is always the same, just return it
return startValue;
}
final int delta = (endValue > startValue) ? 1 : -1;
// 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) clone();
// need to resolve time here, otherwise, fields set for actual limit
// may cause conflict with fields previously set (but not yet resolved).
work.complete();
work.setLenient(true);
work.prepareGetActual(field, delta < 0);
// now try each value from the start to the end 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
work.set(field, startValue);
// prepareGetActual sets the first day of week in the same week with
// the first day of a month. Unlike WEEK_OF_YEAR, week number for the
// which week contains days from both previous and current month is
// not unique. For example, last several days in the previous month
// is week 5, and the rest of week is week 1.
if (work.get(field) != startValue
&& field != WEEK_OF_MONTH && delta > 0) {
return startValue;
}
int result = startValue;
do {
startValue += delta;
work.add(field, delta);
if (work.get(field) != startValue) {
break;
}
result = startValue;
} while (startValue != endValue);
return result;
}
/**
* Rolls (up/down) a single unit of time on the given field. If the
* field is rolled past its maximum allowable value, it will "wrap" back
* to its minimum and continue rolling. For
* example, to roll the current date up by one day, you can call:
*
*
* When rolling on the {@link #YEAR} field, it will roll the year
* value in the range between 1 and the value returned by calling
* {@link #getMaximum getMaximum}({@link #YEAR}).
*
* When rolling on certain fields, the values of other fields may conflict and
* need to be changed. For example, when rolling the
* Rolling up always means rolling forward in time (unless
* the limit of the field is reached, in which case it may pin or wrap), so for the
* Gregorian calendar, starting with 100 BC and rolling the year up results in 99 BC.
* When eras have a definite beginning and end (as in the Chinese calendar, or as in
* most eras in the Japanese calendar) then rolling the year past either limit of the
* era will cause the year to wrap around. When eras only have a limit at one end,
* then attempting to roll the year past that limit will result in pinning the year
* at that limit. Note that for most calendars in which era 0 years move forward in
* time (such as Buddhist, Hebrew, or Islamic), it is possible for add or roll to
* result in negative years for era 0 (that is the only way to represent years before
* the calendar epoch in such calendars).
*
* Note: Calling roll(field, true) N times is not
* necessarily equivalent to calling roll(field, N). For example,
* imagine that you start with the date Gregorian date January 31, 1995. If you call
* roll(Calendar.MONTH, 2), the result will be March 31, 1995.
* But if you call roll(Calendar.MONTH, true), the result will be
* February 28, 1995. Calling it one more time will give March 28, 1995, which
* is usually not the desired result.
*
* Note: You should always use roll and add rather
* than attempting to perform arithmetic operations directly on the fields
* of a Calendar. It is quite possible for Calendar subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' add and roll
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
*
* @param field the calendar field to roll.
*
* @param up indicates if the value of the specified time field is to be
* rolled up or rolled down. Use
* When rolling on certain fields, the values of other fields may conflict and
* need to be changed. For example, when rolling the {@link #MONTH MONTH} field
* for the Gregorian date 1/31/96 by +1, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
*
* Rolling by a positive value always means rolling forward in time (unless
* the limit of the field is reached, in which case it may pin or wrap), so for the
* Gregorian calendar, starting with 100 BC and rolling the year by + 1 results in 99 BC.
* When eras have a definite beginning and end (as in the Chinese calendar, or as in
* most eras in the Japanese calendar) then rolling the year past either limit of the
* era will cause the year to wrap around. When eras only have a limit at one end,
* then attempting to roll the year past that limit will result in pinning the year
* at that limit. Note that for most calendars in which era 0 years move forward in
* time (such as Buddhist, Hebrew, or Islamic), it is possible for add or roll to
* result in negative years for era 0 (that is the only way to represent years before
* the calendar epoch in such calendars).
*
* [icu] Note: the ICU implementation of this method is able to roll
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
* additional fields in their overrides of
* Note: You should always use roll and add rather
* than attempting to perform arithmetic operations directly on the fields
* of a Calendar. It is quite possible for Calendar subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' add and roll
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
*
* Subclassing:
* Subclasses that have fields for which the assumption of continuity breaks
* down must overide
* @param field the calendar field to roll.
* @param amount the amount by which the field should be rolled.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, boolean)
* @see #add
*/
public void roll(int field, int amount) {
if (amount == 0) {
return; // Nothing to do
}
complete();
switch (field) {
case DAY_OF_MONTH:
case AM_PM:
case MINUTE:
case SECOND:
case MILLISECOND:
case MILLISECONDS_IN_DAY:
case ERA:
// These are the standard roll instructions. These work for all
// simple cases, that is, cases in which the limits are fixed, such
// as the hour, the day of the month, and the era.
{
int min = getActualMinimum(field);
int max = getActualMaximum(field);
int gap = max - min + 1;
int value = internalGet(field) + amount;
value = (value - min) % gap;
if (value < 0) {
value += gap;
}
value += min;
set(field, value);
return;
}
case HOUR:
case HOUR_OF_DAY:
// Rolling the hour is difficult on the ONSET and CEASE days of
// daylight savings. For example, if the change occurs at
// 2 AM, we have the following progression:
// ONSET: 12 Std -> 1 Std -> 3 Dst -> 4 Dst
// CEASE: 12 Dst -> 1 Dst -> 1 Std -> 2 Std
// To get around this problem we don't use fields; we manipulate
// the time in millis directly.
{
// Assume min == 0 in calculations below
long start = getTimeInMillis();
int oldHour = internalGet(field);
int max = getMaximum(field);
int newHour = (oldHour + amount) % (max + 1);
if (newHour < 0) {
newHour += max + 1;
}
setTimeInMillis(start + ONE_HOUR * ((long)newHour - oldHour));
return;
}
case MONTH:
// Rolling the month involves both pinning the final value
// and adjusting the DAY_OF_MONTH if necessary. We only adjust the
// DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
// E.g.,
* When adding to certain fields, the values of other fields may conflict and
* need to be changed. For example, when adding one to the {@link #MONTH MONTH} field
* for the Gregorian date 1/31/96, the {@link #DAY_OF_MONTH DAY_OF_MONTH} field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
*
* Adding a positive value always means moving forward in time, so for the Gregorian
* calendar, starting with 100 BC and adding +1 to year results in 99 BC (even though
* this actually reduces the numeric value of the field itself).
*
* [icu] Note: The ICU implementation of this method is able to add to
* all fields except for {@link #ERA ERA}, {@link #DST_OFFSET DST_OFFSET},
* and {@link #ZONE_OFFSET ZONE_OFFSET}. Subclasses may, of course, add support for
* additional fields in their overrides of
* Note: You should always use roll and add rather
* than attempting to perform arithmetic operations directly on the fields
* of a Calendar. It is quite possible for Calendar subclasses
* to have fields with non-linear behavior, for example missing months
* or days during non-leap years. The subclasses' add and roll
* methods will take this into account, while simple arithmetic manipulations
* may give invalid results.
*
* Subclassing:
* Subclasses that have fields for which this assumption of continuity breaks
* down must overide
* @param field the time field.
* @param amount the amount to add to the field.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, int)
*/
@SuppressWarnings("fallthrough")
public void add(int field, int amount) {
if (amount == 0) {
return; // Do nothing!
}
// We handle most fields in the same way. The algorithm is to add
// a computed amount of millis to the current millis. The only
// wrinkle is with DST (and/or a change to the zone's UTC offset, which
// we'll include with DST) -- for some fields, like the DAY_OF_MONTH,
// we don't want the wall time to shift due to changes in DST. If the
// result of the add operation is to move from DST to Standard, or
// vice versa, we need to adjust by an hour forward or back,
// respectively. For such fields we set keepWallTimeInvariant to true.
// We only adjust the DST for fields larger than an hour. For
// fields smaller than an hour, we cannot adjust for DST without
// causing problems. for instance, if you add one hour to April 5,
// 1998, 1:00 AM, in PST, the time becomes "2:00 AM PDT" (an
// illegal value), but then the adjustment sees the change and
// compensates by subtracting an hour. As a result the time
// doesn't advance at all.
// For some fields larger than a day, such as a MONTH, we pin the
// DAY_OF_MONTH. This allows
* Subclassing:
*
* Note:
*
* @param field The calendar field whose value should be pinned.
*
* @see #getActualMinimum
* @see #getActualMaximum
*/
protected void pinField(int field) {
int max = getActualMaximum(field);
int min = getActualMinimum(field);
if (fields[field] > max) {
set(field, max);
} else if (fields[field] < min) {
set(field, min);
}
}
/**
* Returns the week number of a day, within a period. This may be the week number in
* a year or the week number in a month. Usually this will be a value >= 1, but if
* some initial days of the period are excluded from week 1, because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is > 1, then
* the week number will be zero for those
* initial days. This method requires the day number and day of week for some
* known date in the period in order to determine the day of week
* on the desired day.
*
* Subclassing:
*
* This variant is handy for computing the week number of some other
* day of a period (often the first or last day of the period) when its day
* of the week is not known but the day number and day of week for some other
* day in the period (e.g. the current date) is known.
*
* @param desiredDay The {@link #DAY_OF_YEAR DAY_OF_YEAR} or
* {@link #DAY_OF_MONTH DAY_OF_MONTH} whose week number is desired.
* Should be 1 for the first day of the period.
*
* @param dayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR}
* or {@link #DAY_OF_MONTH DAY_OF_MONTH} for a day in the period whose
* {@link #DAY_OF_WEEK DAY_OF_WEEK} is specified by the
*
* Subclassing:
*
* @param dayOfPeriod The {@link #DAY_OF_YEAR DAY_OF_YEAR} or
* {@link #DAY_OF_MONTH DAY_OF_MONTH} whose week number is desired.
* Should be 1 for the first day of the period.
*
* @param dayOfWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day
* corresponding to the As a side effect of this call, this calendar is advanced
* toward Usage: To use this method, call it first with the largest
* field of interest, then with progressively smaller fields. For
* example:
*
* Note:
* Note:This option is effective only when this calendar is {@link #isLenient() lenient}.
* When the calendar is strict, such non-existing wall time will cause an exception.
*
* @param option the behavior for handling skipped wall time at positive time zone
* offset transitions, one of The YEAR_WOY field is computed simplistically. It is equal to YEAR
* most of the time, but at the year boundary it may be adjusted to YEAR-1
* or YEAR+1 to reflect the overlap of a week into an adjacent year. In
* this case, a simple increment or decrement is performed on YEAR, even
* though this may yield an invalid YEAR value. For instance, if the YEAR
* is part of a calendar system with an N-year cycle field CYCLE, then
* incrementing the YEAR may involve incrementing CYCLE and setting YEAR
* back to 0 or 1. This is not handled by this code, and in fact cannot be
* simply handled without having subclasses define an entire parallel set of
* fields for fields larger than or equal to a year. This additional
* complexity is not warranted, since the intention of the YEAR_WOY field is
* to support ISO 8601 notation, so it will typically be used with a
* proleptic Gregorian calendar, which has no field larger than a year.
*/
private final void computeWeekFields() {
int eyear = fields[EXTENDED_YEAR];
int dayOfWeek = fields[DAY_OF_WEEK];
int dayOfYear = fields[DAY_OF_YEAR];
// WEEK_OF_YEAR start
// Compute the week of the year. For the Gregorian calendar, valid week
// numbers run from 1 to 52 or 53, depending on the year, the first day
// of the week, and the minimal days in the first week. For other
// calendars, the valid range may be different -- it depends on the year
// length. Days at the start of the year may fall into the last week of
// the previous year; days at the end of the year may fall into the
// first week of the next year. ASSUME that the year length is less than
// 7000 days.
int yearOfWeekOfYear = eyear;
int relDow = (dayOfWeek + 7 - getFirstDayOfWeek()) % 7; // 0..6
int relDowJan1 = (dayOfWeek - dayOfYear + 7001 - getFirstDayOfWeek()) % 7; // 0..6
int woy = (dayOfYear - 1 + relDowJan1) / 7; // 0..53
if ((7 - relDowJan1) >= getMinimalDaysInFirstWeek()) {
++woy;
}
// Adjust for weeks at the year end that overlap into the previous or
// next calendar year.
if (woy == 0) {
// We are the last week of the previous year.
// Check to see if we are in the last week; if so, we need
// to handle the case in which we are the first week of the
// next year.
int prevDoy = dayOfYear + handleGetYearLength(eyear - 1);
woy = weekNumber(prevDoy, dayOfWeek);
yearOfWeekOfYear--;
} else {
int lastDoy = handleGetYearLength(eyear);
// Fast check: For it to be week 1 of the next year, the DOY
// must be on or after L-5, where L is yearLength(), then it
// cannot possibly be week 1 of the next year:
// L-5 L
// doy: 359 360 361 362 363 364 365 001
// dow: 1 2 3 4 5 6 7
if (dayOfYear >= (lastDoy - 5)) {
int lastRelDow = (relDow + lastDoy - dayOfYear) % 7;
if (lastRelDow < 0) {
lastRelDow += 7;
}
if (((6 - lastRelDow) >= getMinimalDaysInFirstWeek()) &&
((dayOfYear + 7 - relDow) > lastDoy)) {
woy = 1;
yearOfWeekOfYear++;
}
}
}
fields[WEEK_OF_YEAR] = woy;
fields[YEAR_WOY] = yearOfWeekOfYear;
// WEEK_OF_YEAR end
int dayOfMonth = fields[DAY_OF_MONTH];
fields[WEEK_OF_MONTH] = weekNumber(dayOfMonth, dayOfWeek);
fields[DAY_OF_WEEK_IN_MONTH] = (dayOfMonth-1) / 7 + 1;
}
//----------------------------------------------------------------------
// Fields -> Time
//----------------------------------------------------------------------
/**
* Value to OR against resolve table field values for remapping.
* @see #resolveFields
*/
protected static final int RESOLVE_REMAP = 32;
// A power of 2 greater than or equal to MAX_FIELD_COUNT
// Default table for day in year
static final int[][][] DATE_PRECEDENCE = {
{
{ DAY_OF_MONTH },
{ WEEK_OF_YEAR, DAY_OF_WEEK },
{ WEEK_OF_MONTH, DAY_OF_WEEK },
{ DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
{ WEEK_OF_YEAR, DOW_LOCAL },
{ WEEK_OF_MONTH, DOW_LOCAL },
{ DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
{ DAY_OF_YEAR },
{ RESOLVE_REMAP | DAY_OF_MONTH, YEAR }, // if YEAR is set over YEAR_WOY use DAY_OF_MONTH
{ RESOLVE_REMAP | WEEK_OF_YEAR, YEAR_WOY }, // if YEAR_WOY is set, calc based on WEEK_OF_YEAR
},
{
{ WEEK_OF_YEAR },
{ WEEK_OF_MONTH },
{ DAY_OF_WEEK_IN_MONTH },
{ RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DAY_OF_WEEK },
{ RESOLVE_REMAP | DAY_OF_WEEK_IN_MONTH, DOW_LOCAL },
},
};
static final int[][][] DOW_PRECEDENCE = {
{
{ DAY_OF_WEEK },
{ DOW_LOCAL },
},
};
/**
* Given a precedence table, return the newest field combination in
* the table, or -1 if none is found.
*
* The precedence table is a 3-dimensional array of integers. It
* may be thought of as an array of groups. Each group is an array of
* lines. Each line is an array of field numbers. Within a line, if
* all fields are set, then the time stamp of the line is taken to be
* the stamp of the most recently set field. If any field of a line is
* unset, then the line fails to match. Within a group, the line with
* the newest time stamp is selected. The first field of the line is
* returned to indicate which line matched.
*
* In some cases, it may be desirable to map a line to field that
* whose stamp is NOT examined. For example, if the best field is
* DAY_OF_WEEK then the DAY_OF_WEEK_IN_MONTH algorithm may be used. In
* order to do this, insert the value If all lines of a group contain at least one unset field, then no
* line will match, and the group as a whole will fail to match. In
* that case, the next group will be processed. If all groups fail to
* match, then -1 is returned.
*/
protected int resolveFields(int[][][] precedenceTable) {
int bestField = -1;
int tempBestField;
for (int g=0; g In addition, subclasses should compute any subclass-specific
* fields, that is, fields from BASE_FIELD_COUNT to
* getFieldCount() - 1.
*
* The default implementation in
* Unlike the built-in division, this is mathematically well-behaved.
* E.g.,
* Unlike the built-in division, this is mathematically well-behaved.
* E.g.,
* Unlike the built-in division, this is mathematically well-behaved.
* E.g.,
* Unlike the built-in division, this is mathematically well-behaved.
* E.g., See type="old type name" for the calendar attribute of locale IDs
* at http://www.unicode.org/reports/tr35/#Key_Type_Definitions
*
* @return legacy calendar type name string
*/
public String getType() {
return "unknown";
}
/**
* Returns if two digit representation of year in this calendar type
* customarily implies a default century (i.e. 03 -> 2003).
* The default implementation returns Note: This method will be implemented in ICU 3.0; ICU 2.8
* contains a partial preview implementation. The * actual
* locale is returned correctly, but the valid locale is
* not, in most cases.
* @param type type of information requested, either {@link
* android.icu.util.ULocale#VALID_LOCALE} or {@link
* android.icu.util.ULocale#ACTUAL_LOCALE}.
* @return the information specified by type, or null if
* this object was not constructed from locale data.
* @see android.icu.util.ULocale
* @see android.icu.util.ULocale#VALID_LOCALE
* @see android.icu.util.ULocale#ACTUAL_LOCALE
* @hide draft / provisional / internal are hidden on Android
*/
public final ULocale getLocale(ULocale.Type type) {
return type == ULocale.ACTUAL_LOCALE ?
this.actualLocale : this.validLocale;
}
/**
* Set information about the locales that were used to create this
* object. If the object was not constructed from locale data,
* both arguments should be set to null. Otherwise, neither
* should be null. The actual locale must be at the same level or
* less specific than the valid locale. This method is intended
* for use by factories or other entities that create objects of
* this class.
* @param valid the most specific locale containing any resource
* data, or null
* @param actual the locale containing data used to construct this
* object, or null
* @see android.icu.util.ULocale
* @see android.icu.util.ULocale#VALID_LOCALE
* @see android.icu.util.ULocale#ACTUAL_LOCALE
*/
final void setLocale(ULocale valid, ULocale actual) {
// Change the following to an assertion later
if ((valid == null) != (actual == null)) {
///CLOVER:OFF
throw new IllegalArgumentException();
///CLOVER:ON
}
// Another check we could do is that the actual locale is at
// the same level or less specific than the valid locale.
this.validLocale = valid;
this.actualLocale = actual;
}
/**
* The most specific locale containing any resource data, or null.
* @see android.icu.util.ULocale
*/
private ULocale validLocale;
/**
* The locale containing data used to construct this object, or
* null.
* @see android.icu.util.ULocale
*/
private ULocale actualLocale;
// -------- END ULocale boilerplate --------
}
setTime
with
* Date(Long.MAX_VALUE)
or Date(Long.MIN_VALUE)
* may yield incorrect field values from {@link #get(int)}.
* @param date the given Date.
*/
public final void setTime(Date date) {
setTimeInMillis( date.getTime() );
}
/**
* Returns this Calendar's current time as a long.
* @return the current time as UTC milliseconds from the epoch.
*/
public long getTimeInMillis() {
if (!isTimeSet) updateTime();
return time;
}
/**
* Sets this Calendar's current time from the given long value.
* An IllegalIcuArgumentException is thrown when millis is outside the range permitted
* by a Calendar object when in strict mode.
* When in lenient mode the out of range values are pinned to their respective min/max.
* @param millis the new time in UTC milliseconds from the epoch.
*/
public void setTimeInMillis( long millis ) {
if (millis > MAX_MILLIS) {
if(isLenient()) {
millis = MAX_MILLIS;
} else {
throw new IllegalArgumentException("millis value greater than upper bounds for a Calendar : " + millis);
}
} else if (millis < MIN_MILLIS) {
if(isLenient()) {
millis = MIN_MILLIS;
} else {
throw new IllegalArgumentException("millis value less than lower bounds for a Calendar : " + millis);
}
}
time = millis;
areFieldsSet = areAllFieldsSet = false;
isTimeSet = areFieldsVirtuallySet = true;
for (int i=0; inull
and is a Calendar
object that
* represents the same calendar as this object.
* @param obj the object to compare with.
* @return true
if the objects are the same;
* false
otherwise.
*/
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (this.getClass() != obj.getClass()) {
return false;
}
Calendar that = (Calendar) obj;
return isEquivalentTo(that) &&
getTimeInMillis() == that.getTime().getTime();
}
/**
* [icu] Returns true if the given Calendar object is equivalent to this
* one. An equivalent Calendar will behave exactly as this one
* does, but it may be set to a different time. By contrast, for
* the equals() method to return true, the other Calendar must
* be set to the same time.
*
* @param other the Calendar to be compared with this Calendar
*/
public boolean isEquivalentTo(Calendar other) {
return this.getClass() == other.getClass() &&
isLenient() == other.isLenient() &&
getFirstDayOfWeek() == other.getFirstDayOfWeek() &&
getMinimalDaysInFirstWeek() == other.getMinimalDaysInFirstWeek() &&
getTimeZone().equals(other.getTimeZone()) &&
getRepeatedWallTimeOption() == other.getRepeatedWallTimeOption() &&
getSkippedWallTimeOption() == other.getSkippedWallTimeOption();
}
/**
* Returns a hash code for this calendar.
* @return a hash code value for this object.
*/
public int hashCode() {
/* Don't include the time because (a) we don't want the hash value to
* move around just because a calendar is set to different times, and
* (b) we don't want to trigger a time computation just to get a hash.
* Note that it is not necessary for unequal objects to always have
* unequal hashes, but equal objects must have equal hashes. */
return (lenient ? 1 : 0)
| (firstDayOfWeek << 1)
| (minimalDaysInFirstWeek << 4)
| (repeatedWallTime << 7)
| (skippedWallTime << 9)
| (zone.hashCode() << 11);
}
/**
* Returns the difference in milliseconds between the moment this
* calendar is set to and the moment the given calendar or Date object
* is set to.
*/
private long compare(Object that) {
long thatMs;
if (that instanceof Calendar) {
thatMs = ((Calendar)that).getTimeInMillis();
} else if (that instanceof Date) {
thatMs = ((Date)that).getTime();
} else {
throw new IllegalArgumentException(that + "is not a Calendar or Date");
}
return getTimeInMillis() - thatMs;
}
/**
* Compares the time field records.
* Equivalent to comparing result of conversion to UTC.
* @param when the Calendar to be compared with this Calendar.
* @return true if the current time of this Calendar is before
* the time of Calendar when; false otherwise.
*/
public boolean before(Object when) {
return compare(when) < 0;
}
/**
* Compares the time field records.
* Equivalent to comparing result of conversion to UTC.
* @param when the Calendar to be compared with this Calendar.
* @return true if the current time of this Calendar is after
* the time of Calendar when; false otherwise.
*/
public boolean after(Object when) {
return compare(when) > 0;
}
/**
* Returns the maximum value that this field could have, given the
* current date. For example, with the Gregorian date February 3, 1997
* and the {@link #DAY_OF_MONTH DAY_OF_MONTH} field, the actual maximum
* is 28; for February 3, 1996 it is 29.
*
* getActualMinimum(WEEK_OF_MONTH)
will return 1. However,
* if the first of the month is a Thursday, Friday, or Saturday, there are
* not four days in that week, so it is week number 0, and
* getActualMinimum(WEEK_OF_MONTH)
will return 0.
* roll({@link #DATE}, true)
* MONTH
field
* for the Gregorian date 1/31/96 upward, the DAY_OF_MONTH
field
* must be adjusted so that the result is 2/29/96 rather than the invalid
* 2/31/96.
* true
if rolling up,
* false
otherwise.
*
* @exception IllegalArgumentException if the field is invalid or refers
* to a field that cannot be handled by this method.
* @see #roll(int, int)
* @see #add
*/
public final void roll(int field, boolean up)
{
roll(field, up ? +1 : -1);
}
/**
* Rolls (up/down) a specified amount time on the given field. For
* example, to roll the current date up by three days, you can call
* roll(Calendar.DATE, 3)
. If the
* field is rolled past its maximum allowable value, it will "wrap" back
* to its minimum and continue rolling.
* For example, calling roll(Calendar.DATE, 10)
* on a Gregorian calendar set to 4/25/96 will result in the date 4/5/96.
* roll
.
*
* This implementation of roll
assumes that the behavior of the
* field is continuous between its minimum and maximum, which are found by
* calling {@link #getActualMinimum getActualMinimum} and {@link #getActualMaximum getActualMaximum}.
* For most such fields, simple addition, subtraction, and modulus operations
* are sufficient to perform the roll. For week-related fields,
* the results of {@link #getFirstDayOfWeek getFirstDayOfWeek} and
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} are also necessary.
* Subclasses can override these two methods if their values differ from the defaults.
* roll
to handle those fields specially.
* For example, in the Hebrew calendar the month "Adar I"
* only occurs in leap years; in other years the calendar jumps from
* Shevat (month #4) to Adar (month #6). The
* {@link HebrewCalendar#roll HebrewCalendar.roll} method takes this into account,
* so that rolling the month of Shevat by one gives the proper result (Adar) in a
* non-leap year.
* add(Calendar.DATE, 3)
.
* add
.
*
* This implementation of add
assumes that the behavior of the
* field is continuous between its minimum and maximum, which are found by
* calling {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum}.
* For such fields, simple arithmetic operations are sufficient to
* perform the add.
* add
to handle those fields specially.
* For example, in the Hebrew calendar the month "Adar I"
* only occurs in leap years; in other years the calendar jumps from
* Shevat (month #4) to Adar (month #6). The
* {@link HebrewCalendar#add HebrewCalendar.add} method takes this into account,
* so that adding one month
* to a date in Shevat gives the proper result (Adar) in a non-leap year.
* Calendar
objects.
*
* @param that the Calendar
to compare to this.
* @return 0
if the time represented by
* this Calendar
is equal to the time represented
* by that Calendar
, a value less than
* 0
if the time represented by this is before
* the time represented by that, and a value greater than
* 0
if the time represented by this
* is after the time represented by that.
* @throws NullPointerException if that
* Calendar
is null.
* @throws IllegalArgumentException if the time of that
* Calendar
can't be obtained because of invalid
* calendar values.
*/
public int compareTo(Calendar that) {
long v = getTimeInMillis() - that.getTimeInMillis();
return v < 0 ? -1 : (v > 0 ? 1 : 0);
}
//-------------------------------------------------------------------------
// Interface for creating custon DateFormats for different types of Calendars
//-------------------------------------------------------------------------
/**
* [icu] Returns a DateFormat
appropriate to this calendar.
* Subclasses wishing to specialize this behavior should override
* {@link #handleGetDateFormat}.
*/
public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, Locale loc) {
return formatHelper(this, ULocale.forLocale(loc), dateStyle, timeStyle);
}
/**
* [icu] Returns a DateFormat
appropriate to this calendar.
* Subclasses wishing to specialize this behavior should override
* {@link #handleGetDateFormat}.
*/
public DateFormat getDateTimeFormat(int dateStyle, int timeStyle, ULocale loc) {
return formatHelper(this, loc, dateStyle, timeStyle);
}
/**
* Creates a DateFormat
appropriate to this calendar.
* This is a framework method for subclasses to override. This method
* is responsible for creating the calendar-specific DateFormat and
* DateFormatSymbols objects as needed.
* @param pattern the pattern, specific to the DateFormat
* subclass
* @param locale the locale for which the symbols should be drawn
* @return a DateFormat
appropriate to this calendar
*/
protected DateFormat handleGetDateFormat(String pattern, Locale locale) {
return handleGetDateFormat(pattern, null, ULocale.forLocale(locale));
}
/**
* Creates a DateFormat
appropriate to this calendar.
* This is a framework method for subclasses to override. This method
* is responsible for creating the calendar-specific DateFormat and
* DateFormatSymbols objects as needed.
* @param pattern the pattern, specific to the DateFormat
* subclass
* @param override The override string. A numbering system override string can take one of the following forms:
* 1). If just a numbering system name is specified, it applies to all numeric fields in the date format pattern.
* 2). To specify an alternate numbering system on a field by field basis, use the field letters from the pattern
* followed by an = sign, followed by the numbering system name. For example, to specify that just the year
* be formatted using Hebrew digits, use the override "y=hebr". Multiple overrides can be specified in a single
* string by separating them with a semi-colon. For example, the override string "m=thai;y=deva" would format using
* Thai digits for the month and Devanagari digits for the year.
* @param locale the locale for which the symbols should be drawn
* @return a DateFormat
appropriate to this calendar
*/
protected DateFormat handleGetDateFormat(String pattern, String override, Locale locale) {
return handleGetDateFormat(pattern, override, ULocale.forLocale(locale));
}
/**
* Creates a DateFormat
appropriate to this calendar.
* This is a framework method for subclasses to override. This method
* is responsible for creating the calendar-specific DateFormat and
* DateFormatSymbols objects as needed.
* @param pattern the pattern, specific to the DateFormat
* subclass
* @param locale the locale for which the symbols should be drawn
* @return a DateFormat
appropriate to this calendar
*/
protected DateFormat handleGetDateFormat(String pattern, ULocale locale) {
return handleGetDateFormat(pattern, null, locale);
}
/**
* Creates a DateFormat
appropriate to this calendar.
* This is a framework method for subclasses to override. This method
* is responsible for creating the calendar-specific DateFormat and
* DateFormatSymbols objects as needed.
* @param pattern the pattern, specific to the DateFormat
* subclass
* @param locale the locale for which the symbols should be drawn
* @return a DateFormat
appropriate to this calendar
* @hide draft / provisional / internal are hidden on Android
*/
protected DateFormat handleGetDateFormat(String pattern, String override, ULocale locale) {
FormatConfiguration fmtConfig = new FormatConfiguration();
fmtConfig.pattern = pattern;
fmtConfig.override = override;
fmtConfig.formatData = new DateFormatSymbols(this, locale);
fmtConfig.loc = locale;
fmtConfig.cal = this;
return SimpleDateFormat.getInstance(fmtConfig);
}
// date format pattern cache
private static final ICUCache
* This utility method is intended for use by subclasses that need to implement
* their own overrides of {@link #roll roll} and {@link #add add}.
* pinField
is implemented in terms of
* {@link #getActualMinimum getActualMinimum}
* and {@link #getActualMaximum getActualMaximum}. If either of those methods uses
* a slow, iterative algorithm for a particular field, it would be
* unwise to attempt to call pinField
for that field. If you
* really do need to do so, you should override this method to do
* something more efficient for that field.
*
* This method is intended for use by subclasses in implementing their
* {@link #computeTime computeTime} and/or {@link #computeFields computeFields} methods.
* It is often useful in {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum} as well.
* dayOfWeek
parameter.
* Should be 1 for first day of period.
*
* @param dayOfWeek The {@link #DAY_OF_WEEK DAY_OF_WEEK} for the day
* corresponding to the dayOfPeriod
parameter.
* 1-based with 1=Sunday.
*
* @return The week number (one-based), or zero if the day falls before
* the first week because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* is more than one.
*/
protected int weekNumber(int desiredDay, int dayOfPeriod, int dayOfWeek)
{
// Determine the day of the week of the first day of the period
// in question (either a year or a month). Zero represents the
// first day of the week on this calendar.
int periodStartDayOfWeek = (dayOfWeek - getFirstDayOfWeek() - dayOfPeriod + 1) % 7;
if (periodStartDayOfWeek < 0) periodStartDayOfWeek += 7;
// Compute the week number. Initially, ignore the first week, which
// may be fractional (or may not be). We add periodStartDayOfWeek in
// order to fill out the first week, if it is fractional.
int weekNo = (desiredDay + periodStartDayOfWeek - 1)/7;
// If the first week is long enough, then count it. If
// the minimal days in the first week is one, or if the period start
// is zero, we always increment weekNo.
if ((7 - periodStartDayOfWeek) >= getMinimalDaysInFirstWeek()) ++weekNo;
return weekNo;
}
/**
* Returns the week number of a day, within a period. This may be the week number in
* a year, or the week number in a month. Usually this will be a value >= 1, but if
* some initial days of the period are excluded from week 1, because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek} is > 1,
* then the week number will be zero for those
* initial days. This method requires the day of week for the given date in order to
* determine the result.
*
* This method is intended for use by subclasses in implementing their
* {@link #computeTime computeTime} and/or {@link #computeFields computeFields} methods.
* It is often useful in {@link #getActualMinimum getActualMinimum} and
* {@link #getActualMaximum getActualMaximum} as well.
* dayOfPeriod
parameter.
* 1-based with 1=Sunday.
*
* @return The week number (one-based), or zero if the day falls before
* the first week because
* {@link #getMinimalDaysInFirstWeek getMinimalDaysInFirstWeek}
* is more than one.
*/
protected final int weekNumber(int dayOfPeriod, int dayOfWeek)
{
return weekNumber(dayOfPeriod, dayOfPeriod, dayOfWeek);
}
//-------------------------------------------------------------------------
// Constants
//-------------------------------------------------------------------------
private static final int FIELD_DIFF_MAX_INT = Integer.MAX_VALUE; // 2147483647
/**
* [icu] Returns the difference between the given time and the time this
* calendar object is set to. If this calendar is set
* before the given time, the returned value will be
* positive. If this calendar is set after the given
* time, the returned value will be negative. The
* field
parameter specifies the units of the return
* value. For example, if fieldDifference(when,
* Calendar.MONTH)
returns 3, then this calendar is set to
* 3 months before when
, and possibly some additional
* time less than one month.
*
* when
by the given amount. That is, calling
* this method has the side effect of calling add(field,
* n)
, where n
is the return value.
*
*
* int y = cal.fieldDifference(when, Calendar.YEAR);
* int m = cal.fieldDifference(when, Calendar.MONTH);
* int d = cal.fieldDifference(when, Calendar.DATE);
*
* computes the difference between cal
and
* when
in years, months, and days.
*
* fieldDifference()
is
* asymmetrical. That is, in the following code:
*
*
* cal.setTime(date1);
* int m1 = cal.fieldDifference(date2, Calendar.MONTH);
* int d1 = cal.fieldDifference(date2, Calendar.DATE);
* cal.setTime(date2);
* int m2 = cal.fieldDifference(date1, Calendar.MONTH);
* int d2 = cal.fieldDifference(date1, Calendar.DATE);
*
* one might expect that m1 == -m2 && d1 == -d2
.
* However, this is not generally the case, because of
* irregularities in the underlying calendar system (e.g., the
* Gregorian calendar has a varying number of days per month).
*
* @param when the date to compare this calendar's time to
* @param field the field in which to compute the result
* @return the difference, either positive or negative, between
* this calendar's time and when
, in terms of
* field
.
*/
public int fieldDifference(Date when, int field) {
int min = 0;
long startMs = getTimeInMillis();
long targetMs = when.getTime();
// Always add from the start millis. This accomodates
// operations like adding years from February 29, 2000 up to
// February 29, 2004. If 1, 1, 1, 1 is added to the year
// field, the DOM gets pinned to 28 and stays there, giving an
// incorrect DOM difference of 1. We have to add 1, reset, 2,
// reset, 3, reset, 4.
if (startMs < targetMs) {
int max = 1;
// Find a value that is too large
for (;;) {
setTimeInMillis(startMs);
add(field, max);
long ms = getTimeInMillis();
if (ms == targetMs) {
return max;
} else if (ms > targetMs) {
break;
} else if (max < FIELD_DIFF_MAX_INT) {
min = max;
max <<= 1;
if (max < 0) {
max = FIELD_DIFF_MAX_INT;
}
} else {
// Field difference too large to fit into int
throw new RuntimeException();
}
}
// Do a binary search
while ((max - min) > 1) {
int t = min + (max - min)/2; // make sure intermediate values don't exceed FIELD_DIFF_MAX_INT
setTimeInMillis(startMs);
add(field, t);
long ms = getTimeInMillis();
if (ms == targetMs) {
return t;
} else if (ms > targetMs) {
max = t;
} else {
min = t;
}
}
} else if (startMs > targetMs) {
//Eclipse stated the following is "dead code"
/*if (false) {
// This works, and makes the code smaller, but costs
// an extra object creation and an extra couple cycles
// of calendar computation.
setTimeInMillis(targetMs);
min = -fieldDifference(new Date(startMs), field);
}*/
int max = -1;
// Find a value that is too small
for (;;) {
setTimeInMillis(startMs);
add(field, max);
long ms = getTimeInMillis();
if (ms == targetMs) {
return max;
} else if (ms < targetMs) {
break;
} else {
min = max;
max <<= 1;
if (max == 0) {
// Field difference too large to fit into int
throw new RuntimeException();
}
}
}
// Do a binary search
while ((min - max) > 1) {
int t = min + (max - min)/2; // make sure intermediate values don't exceed FIELD_DIFF_MAX_INT
setTimeInMillis(startMs);
add(field, t);
long ms = getTimeInMillis();
if (ms == targetMs) {
return t;
} else if (ms < targetMs) {
max = t;
} else {
min = t;
}
}
}
// Set calendar to end point
setTimeInMillis(startMs);
add(field, min);
return min;
}
/**
* Sets the time zone with the given time zone value.
* @param value the given time zone.
*/
public void setTimeZone(TimeZone value)
{
zone = value;
/* 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().
*/
areFieldsSet = false;
}
/**
* Returns the time zone.
* @return the time zone object associated with this calendar.
*/
public TimeZone getTimeZone()
{
return zone;
}
/**
* Specify 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 interpretation, such dates will cause an exception to be
* thrown.
*
* @see DateFormat#setLenient
*/
public void setLenient(boolean lenient)
{
this.lenient = lenient;
}
/**
* Tell whether date/time interpretation is to be lenient.
*/
public boolean isLenient()
{
return lenient;
}
/**
* [icu]Sets the behavior for handling wall time repeating multiple times
* at negative time zone offset transitions. For example, 1:30 AM on
* November 6, 2011 in US Eastern time (Ameirca/New_York) occurs twice;
* 1:30 AM EDT, then 1:30 AM EST one hour later. When WALLTIME_FIRST
* is used, the wall time 1:30AM in this example will be interpreted as 1:30 AM EDT
* (first occurrence). When WALLTIME_LAST
is used, it will be
* interpreted as 1:30 AM EST (last occurrence). The default value is
* WALLTIME_LAST
.
*
* @param option the behavior for handling repeating wall time, either
* WALLTIME_FIRST
or WALLTIME_LAST
.
* @throws IllegalArgumentException when option
is neither
* WALLTIME_FIRST
nor WALLTIME_LAST
.
*
* @see #getRepeatedWallTimeOption()
* @see #WALLTIME_FIRST
* @see #WALLTIME_LAST
*/
public void setRepeatedWallTimeOption(int option) {
if (option != WALLTIME_LAST && option != WALLTIME_FIRST) {
throw new IllegalArgumentException("Illegal repeated wall time option - " + option);
}
repeatedWallTime = option;
}
/**
* [icu]Gets the behavior for handling wall time repeating multiple times
* at negative time zone offset transitions.
*
* @return the behavior for handling repeating wall time, either
* WALLTIME_FIRST
or WALLTIME_LAST
.
*
* @see #setRepeatedWallTimeOption(int)
* @see #WALLTIME_FIRST
* @see #WALLTIME_LAST
*/
public int getRepeatedWallTimeOption() {
return repeatedWallTime;
}
/**
* [icu]Sets the behavior for handling skipped wall time at positive time zone offset
* transitions. For example, 2:30 AM on March 13, 2011 in US Eastern time (America/New_York)
* does not exist because the wall time jump from 1:59 AM EST to 3:00 AM EDT. When
* WALLTIME_FIRST
is used, 2:30 AM is interpreted as 30 minutes before 3:00 AM
* EDT, therefore, it will be resolved as 1:30 AM EST. When WALLTIME_LAST
* is used, 2:30 AM is interpreted as 31 minutes after 1:59 AM EST, therefore, it will be
* resolved as 3:30 AM EDT. When WALLTIME_NEXT_VALID
is used, 2:30 AM will
* be resolved as next valid wall time, that is 3:00 AM EDT. The default value is
* WALLTIME_LAST
.
* WALLTIME_FIRST
, WALLTIME_LAST
and
* WALLTIME_NEXT_VALID
.
* @throws IllegalArgumentException when option
is not any of
* WALLTIME_FIRST
, WALLTIME_LAST
and WALLTIME_NEXT_VALID
.
*
* @see #getSkippedWallTimeOption()
* @see #WALLTIME_FIRST
* @see #WALLTIME_LAST
* @see #WALLTIME_NEXT_VALID
*/
public void setSkippedWallTimeOption(int option) {
if (option != WALLTIME_LAST && option != WALLTIME_FIRST && option != WALLTIME_NEXT_VALID) {
throw new IllegalArgumentException("Illegal skipped wall time option - " + option);
}
skippedWallTime = option;
}
/**
* [icu]Gets the behavior for handling skipped wall time at positive time zone offset
* transitions.
*
* @return the behavior for handling skipped wall time, one of
* WALLTIME_FIRST
, WALLTIME_LAST
and WALLTIME_NEXT_VALID
.
*
* @see #setSkippedWallTimeOption(int)
* @see #WALLTIME_FIRST
* @see #WALLTIME_LAST
* @see #WALLTIME_NEXT_VALID
*/
public int getSkippedWallTimeOption() {
return skippedWallTime;
}
/**
* Sets what the first day of the week is,
* where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
* @param value the given first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
*/
public void setFirstDayOfWeek(int value)
{
if (firstDayOfWeek != value) {
if (value < SUNDAY || value > SATURDAY) {
throw new IllegalArgumentException("Invalid day of week");
}
firstDayOfWeek = value;
areFieldsSet = false;
}
}
/**
* Returns what the first day of the week is,
* where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
* e.g., Sunday in US, Monday in France
* @return the first day of the week, where 1 = {@link #SUNDAY} and 7 = {@link #SATURDAY}.
*/
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 the 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.
*/
public void setMinimalDaysInFirstWeek(int value)
{
// Values less than 1 have the same effect as 1; values greater
// than 7 have the same effect as 7. However, we normalize values
// so operator== and so forth work.
if (value < 1) {
value = 1;
} else if (value > 7) {
value = 7;
}
if (minimalDaysInFirstWeek != value) {
minimalDaysInFirstWeek = value;
areFieldsSet = false;
}
}
/**
* Returns what the minimal days required in the first week of the year are.
* That is, if the first week is defined as one that contains the first day
* of the first month of a year, getMinimalDaysInFirstWeek returns 1. If
* the minimal days required must be a full week, getMinimalDaysInFirstWeek
* returns 7.
* @return the minimal days required in the first week of the year.
*/
public int getMinimalDaysInFirstWeek()
{
return minimalDaysInFirstWeek;
}
private static final int LIMITS[][] = {
// Minimum Greatest min Least max Greatest max
{/* */}, // ERA
{/* */}, // YEAR
{/* */}, // MONTH
{/* */}, // WEEK_OF_YEAR
{/* */}, // WEEK_OF_MONTH
{/* */}, // DAY_OF_MONTH
{/* */}, // DAY_OF_YEAR
{ 1, 1, 7, 7 }, // DAY_OF_WEEK
{/* */}, // DAY_OF_WEEK_IN_MONTH
{ 0, 0, 1, 1 }, // AM_PM
{ 0, 0, 11, 11 }, // HOUR
{ 0, 0, 23, 23 }, // HOUR_OF_DAY
{ 0, 0, 59, 59 }, // MINUTE
{ 0, 0, 59, 59 }, // SECOND
{ 0, 0, 999, 999 }, // MILLISECOND
{-12*ONE_HOUR, -12*ONE_HOUR, 12*ONE_HOUR, 12*ONE_HOUR }, // ZONE_OFFSET
{ 0, 0, 1*ONE_HOUR, 1*ONE_HOUR }, // DST_OFFSET
{/* */}, // YEAR_WOY
{ 1, 1, 7, 7 }, // DOW_LOCAL
{/* */}, // EXTENDED_YEAR
{ -0x7F000000, -0x7F000000, 0x7F000000, 0x7F000000 }, // JULIAN_DAY
{ 0, 0, 24*ONE_HOUR-1, 24*ONE_HOUR-1 }, // MILLISECONDS_IN_DAY
{ 0, 0, 1, 1 }, // IS_LEAP_MONTH
};
/**
* Subclass API for defining limits of different types.
* Subclasses must implement this method to return limits for the
* following fields:
*
* ERA
* YEAR
* MONTH
* WEEK_OF_YEAR
* WEEK_OF_MONTH
* DAY_OF_MONTH
* DAY_OF_YEAR
* DAY_OF_WEEK_IN_MONTH
* YEAR_WOY
* EXTENDED_YEAR
*
* @param field one of the above field numbers
* @param limitType one of MINIMUM
, GREATEST_MINIMUM
,
* LEAST_MAXIMUM
, or MAXIMUM
*/
abstract protected int handleGetLimit(int field, int limitType);
/**
* Returns a limit for a field.
* @param field the field, from 0..getFieldCount()-1
* @param limitType the type specifier for the limit
* @see #MINIMUM
* @see #GREATEST_MINIMUM
* @see #LEAST_MAXIMUM
* @see #MAXIMUM
*/
protected int getLimit(int field, int limitType) {
switch (field) {
case DAY_OF_WEEK:
case AM_PM:
case HOUR:
case HOUR_OF_DAY:
case MINUTE:
case SECOND:
case MILLISECOND:
case ZONE_OFFSET:
case DST_OFFSET:
case DOW_LOCAL:
case JULIAN_DAY:
case MILLISECONDS_IN_DAY:
case IS_LEAP_MONTH:
return LIMITS[field][limitType];
case WEEK_OF_MONTH:
{
int limit;
if (limitType == MINIMUM) {
limit = getMinimalDaysInFirstWeek() == 1 ? 1 : 0;
} else if (limitType == GREATEST_MINIMUM){
limit = 1;
} else {
int minDaysInFirst = getMinimalDaysInFirstWeek();
int daysInMonth = handleGetLimit(DAY_OF_MONTH, limitType);
if (limitType == LEAST_MAXIMUM) {
limit = (daysInMonth + (7 - minDaysInFirst)) / 7;
} else { // limitType == MAXIMUM
limit = (daysInMonth + 6 + (7 - minDaysInFirst)) / 7;
}
}
return limit;
}
}
return handleGetLimit(field, limitType);
}
/**
* Limit type for getLimit()
and handleGetLimit()
* indicating the minimum value that a field can take (least minimum).
* @see #getLimit
* @see #handleGetLimit
*/
protected static final int MINIMUM = 0;
/**
* Limit type for getLimit()
and handleGetLimit()
* indicating the greatest minimum value that a field can take.
* @see #getLimit
* @see #handleGetLimit
*/
protected static final int GREATEST_MINIMUM = 1;
/**
* Limit type for getLimit()
and handleGetLimit()
* indicating the least maximum value that a field can take.
* @see #getLimit
* @see #handleGetLimit
*/
protected static final int LEAST_MAXIMUM = 2;
/**
* Limit type for getLimit()
and handleGetLimit()
* indicating the maximum value that a field can take (greatest maximum).
* @see #getLimit
* @see #handleGetLimit
*/
protected static final int MAXIMUM = 3;
/**
* Returns the minimum value for the given time field.
* e.g., for Gregorian DAY_OF_MONTH, 1.
* @param field the given time field.
* @return the minimum value for the given time field.
*/
public final int getMinimum(int field) {
return getLimit(field, MINIMUM);
}
/**
* Returns the maximum value for the given time field.
* e.g. for Gregorian DAY_OF_MONTH, 31.
* @param field the given time field.
* @return the maximum value for the given time field.
*/
public final int getMaximum(int field) {
return getLimit(field, MAXIMUM);
}
/**
* Returns the highest minimum value for the given field if varies.
* Otherwise same as getMinimum(). For Gregorian, no difference.
* @param field the given time field.
* @return the highest minimum value for the given time field.
*/
public final int getGreatestMinimum(int field) {
return getLimit(field, GREATEST_MINIMUM);
}
/**
* Returns the lowest maximum value for the given field if varies.
* Otherwise same as getMaximum(). e.g., for Gregorian DAY_OF_MONTH, 28.
* @param field the given time field.
* @return the lowest maximum value for the given time field.
*/
public final int getLeastMaximum(int field) {
return getLimit(field, LEAST_MAXIMUM);
}
//-------------------------------------------------------------------------
// Weekend support -- determining which days of the week are the weekend
// in a given locale
//-------------------------------------------------------------------------
/**
* [icu] Returns whether the given day of the week is a weekday, a
* weekend day, or a day that transitions from one to the other, for the
* locale and calendar system associated with this Calendar (the locale's
* region is often the most determinant factor). If a transition occurs at
* midnight, then the days before and after the transition will have the
* type WEEKDAY or WEEKEND. If a transition occurs at a time
* other than midnight, then the day of the transition will have
* the type WEEKEND_ONSET or WEEKEND_CEASE. In this case, the
* method getWeekendTransition() will return the point of
* transition.
* @param dayOfWeek either SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
* THURSDAY, FRIDAY, or SATURDAY
* @return either WEEKDAY, WEEKEND, WEEKEND_ONSET, or
* WEEKEND_CEASE
* @exception IllegalArgumentException if dayOfWeek is not
* between SUNDAY and SATURDAY, inclusive
* @see #WEEKDAY
* @see #WEEKEND
* @see #WEEKEND_ONSET
* @see #WEEKEND_CEASE
* @see #getWeekendTransition
* @see #isWeekend(Date)
* @see #isWeekend()
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public int getDayOfWeekType(int dayOfWeek) {
if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) {
throw new IllegalArgumentException("Invalid day of week");
}
if (weekendOnset == weekendCease) {
if (dayOfWeek != weekendOnset)
return WEEKDAY;
return (weekendOnsetMillis == 0) ? WEEKEND : WEEKEND_ONSET;
}
if (weekendOnset < weekendCease) {
if (dayOfWeek < weekendOnset || dayOfWeek > weekendCease) {
return WEEKDAY;
}
} else {
if (dayOfWeek > weekendCease && dayOfWeek < weekendOnset) {
return WEEKDAY;
}
}
if (dayOfWeek == weekendOnset) {
return (weekendOnsetMillis == 0) ? WEEKEND : WEEKEND_ONSET;
}
if (dayOfWeek == weekendCease) {
return (weekendCeaseMillis >= 86400000) ? WEEKEND : WEEKEND_CEASE;
}
return WEEKEND;
}
/**
* [icu] Returns the time during the day at which the weekend begins or end in this
* calendar system. If getDayOfWeekType(dayOfWeek) == WEEKEND_ONSET return the time
* at which the weekend begins. If getDayOfWeekType(dayOfWeek) == WEEKEND_CEASE
* return the time at which the weekend ends. If getDayOfWeekType(dayOfWeek) has some
* other value, then throw an exception.
* @param dayOfWeek either SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
* THURSDAY, FRIDAY, or SATURDAY
* @return the milliseconds after midnight at which the
* weekend begins or ends
* @exception IllegalArgumentException if dayOfWeek is not
* WEEKEND_ONSET or WEEKEND_CEASE
* @see #getDayOfWeekType
* @see #isWeekend(Date)
* @see #isWeekend()
* @deprecated ICU 54 use {@link #getWeekDataForRegion(String)}, {@link #getWeekData()}, {@link #setWeekData(WeekData)}
* @hide original deprecated declaration
*/
@Deprecated
public int getWeekendTransition(int dayOfWeek) {
if (dayOfWeek == weekendOnset) {
return weekendOnsetMillis;
} else if (dayOfWeek == weekendCease) {
return weekendCeaseMillis;
}
throw new IllegalArgumentException("Not weekend transition day");
}
/**
* [icu] Returns true if the given date and time is in the weekend in this calendar
* system. Equivalent to calling setTime() followed by isWeekend(). Note: This
* method changes the time this calendar is set to.
* @param date the date and time
* @return true if the given date and time is part of the
* weekend
* @see #getDayOfWeekType
* @see #getWeekendTransition
* @see #isWeekend()
*/
public boolean isWeekend(Date date) {
setTime(date);
return isWeekend();
}
/**
* [icu] Returns true if this Calendar's current date and time is in the weekend in
* this calendar system.
* @return true if the given date and time is part of the
* weekend
* @see #getDayOfWeekType
* @see #getWeekendTransition
* @see #isWeekend(Date)
*/
public boolean isWeekend() {
int dow = get(DAY_OF_WEEK);
int dowt = getDayOfWeekType(dow);
switch (dowt) {
case WEEKDAY:
return false;
case WEEKEND:
return true;
default: // That is, WEEKEND_ONSET or WEEKEND_CEASE
// Use internalGet() because the above call to get() populated
// all fields.
// [Note: There should be a better way to get millis in day.
// For ICU4J, submit request for a MILLIS_IN_DAY field
// and a DAY_NUMBER field (could be Julian day #). - aliu]
int millisInDay = internalGet(MILLISECOND) + 1000 * (internalGet(SECOND) +
60 * (internalGet(MINUTE) + 60 * internalGet(HOUR_OF_DAY)));
int transition = getWeekendTransition(dow);
return (dowt == WEEKEND_ONSET)
? (millisInDay >= transition)
: (millisInDay < transition);
}
// (We can never reach this point.)
}
//-------------------------------------------------------------------------
// End of weekend support
//-------------------------------------------------------------------------
/**
* Overrides Cloneable
*/
public Object clone()
{
try {
Calendar other = (Calendar) super.clone();
other.fields = new int[fields.length];
other.stamp = new int[fields.length];
System.arraycopy(this.fields, 0, other.fields, 0, fields.length);
System.arraycopy(this.stamp, 0, other.stamp, 0, fields.length);
other.zone = (TimeZone) zone.clone();
return other;
}
catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new ICUCloneNotSupportedException(e);
}
}
/**
* Returns 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.
*/
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append(getClass().getName());
buffer.append("[time=");
buffer.append(isTimeSet ? String.valueOf(time) : "?");
buffer.append(",areFieldsSet=");
buffer.append(areFieldsSet);
buffer.append(",areAllFieldsSet=");
buffer.append(areAllFieldsSet);
buffer.append(",lenient=");
buffer.append(lenient);
buffer.append(",zone=");
buffer.append(zone);
buffer.append(",firstDayOfWeek=");
buffer.append(firstDayOfWeek);
buffer.append(",minimalDaysInFirstWeek=");
buffer.append(minimalDaysInFirstWeek);
buffer.append(",repeatedWallTime=");
buffer.append(repeatedWallTime);
buffer.append(",skippedWallTime=");
buffer.append(skippedWallTime);
for (int i=0; itime
to
* field values in fields[]
. This synchronizes the time
* 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 complete
method.
* @see #complete
*/
protected void computeFields() {
int offsets[] = new int[2];
getTimeZone().getOffset(time, false, offsets);
long localMillis = time + offsets[0] + offsets[1];
// Mark fields as set. Do this before calling handleComputeFields().
int mask = internalSetMask;
for (int i=0; iREMAP_RESOLVE | F
at
* the start of the line, where F
is the desired return
* field value. This field will NOT be examined; it only determines
* the return value if the other fields in the line are the newest.
*
* Calendar.validateField()
.
* @see #validateField(int, int, int)
*/
protected void validateField(int field) {
int y;
switch (field) {
case DAY_OF_MONTH:
y = handleGetExtendedYear();
validateField(field, 1, handleGetMonthLength(y, internalGet(MONTH)));
break;
case DAY_OF_YEAR:
y = handleGetExtendedYear();
validateField(field, 1, handleGetYearLength(y));
break;
case DAY_OF_WEEK_IN_MONTH:
if (internalGet(field) == 0) {
throw new IllegalArgumentException("DAY_OF_WEEK_IN_MONTH cannot be zero");
}
validateField(field, getMinimum(field), getMaximum(field));
break;
default:
validateField(field, getMinimum(field), getMaximum(field));
break;
}
}
/**
* Validate a single field of this calendar given its minimum and
* maximum allowed value. If the field is out of range, throw a
* descriptive IllegalArgumentException
. Subclasses may
* use this method in their implementation of {@link
* #validateField(int)}.
*/
protected final void validateField(int field, int min, int max) {
int value = fields[field];
if (value < min || value > max) {
throw new IllegalArgumentException(fieldName(field) +
'=' + value + ", valid range=" +
min + ".." + max);
}
}
/**
* Converts the current field values in fields[]
to the
* millisecond time value time
.
*/
protected void computeTime() {
if (!isLenient()) {
validateFields();
}
// Compute the Julian day
int julianDay = computeJulianDay();
long millis = julianDayToMillis(julianDay);
int millisInDay;
// We only use MILLISECONDS_IN_DAY if it has been set by the user.
// This makes it possible for the caller to set the calendar to a
// time and call clear(MONTH) to reset the MONTH to January. This
// is legacy behavior. Without this, clear(MONTH) has no effect,
// since the internally set JULIAN_DAY is used.
if (stamp[MILLISECONDS_IN_DAY] >= MINIMUM_USER_STAMP &&
newestStamp(AM_PM, MILLISECOND, UNSET) <= stamp[MILLISECONDS_IN_DAY]) {
millisInDay = internalGet(MILLISECONDS_IN_DAY);
} else {
millisInDay = computeMillisInDay();
}
if (stamp[ZONE_OFFSET] >= MINIMUM_USER_STAMP ||
stamp[DST_OFFSET] >= MINIMUM_USER_STAMP) {
time = millis + millisInDay - (internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET));
} else {
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
// for discussion purposes here.
//
// 1. The positive offset change such as transition into DST.
// Here, a designated time of 2:00 am - 2:59 am does not actually exist.
// For this case, skippedWallTime option specifies the behavior.
// For example, 2:30 am is interpreted as;
// - WALLTIME_LAST(default): 3:30 am (DST) (interpreting 2:30 am as 31 minutes after 1:59 am (STD))
// - WALLTIME_FIRST: 1:30 am (STD) (interpreting 2:30 am as 30 minutes before 3:00 am (DST))
// - WALLTIME_NEXT_VALID: 3:00 am (DST) (next valid time after 2:30 am on a wall clock)
// 2. The negative offset change such as transition out of DST.
// Here, a designated time of 1:00 am - 1:59 am can be in standard or DST. Both are valid
// representations (the rep jumps from 1:59:59 DST to 1:00:00 Std).
// For this case, repeatedWallTime option specifies the behavior.
// For example, 1:30 am is interpreted as;
// - WALLTIME_LAST(default): 1:30 am (STD) - latter occurrence
// - WALLTIME_FIRST: 1:30 am (DST) - former occurrence
//
// In addition to above, when calendar is strict (not default), wall time falls into
// the skipped time range will be processed as an error case.
//
// These special cases are mostly handled in #computeZoneOffset(long), except WALLTIME_NEXT_VALID
// at positive offset change. The protected method computeZoneOffset(long) is exposed to Calendar
// subclass implementations and marked as @stable. Strictly speaking, WALLTIME_NEXT_VALID
// should be also handled in the same place, but we cannot change the code flow without deprecating
// the protected method.
//
// We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
// or DST_OFFSET fields; then we use those fields.
if (!lenient || skippedWallTime == WALLTIME_NEXT_VALID) {
// When strict, invalidate a wall time falls into a skipped wall time range.
// When lenient and skipped wall time option is WALLTIME_NEXT_VALID,
// the result time will be adjusted to the next valid time (on wall clock).
int zoneOffset = computeZoneOffset(millis, millisInDay);
long tmpTime = millis + millisInDay - zoneOffset;
int zoneOffset1 = zone.getOffset(tmpTime);
// zoneOffset != zoneOffset1 only when the given wall time fall into
// a skipped wall time range caused by positive zone offset transition.
if (zoneOffset != zoneOffset1) {
if (!lenient) {
throw new IllegalArgumentException("The specified wall time does not exist due to time zone offset transition.");
}
assert skippedWallTime == WALLTIME_NEXT_VALID : skippedWallTime;
// Adjust time to the next valid wall clock time.
// At this point, tmpTime is on or after the zone offset transition causing
// the skipped time range.
Long immediatePrevTransition = getImmediatePreviousZoneTransition(tmpTime);
if (immediatePrevTransition == null) {
throw new RuntimeException("Could not locate a time zone transition before " + tmpTime);
}
time = immediatePrevTransition;
} else {
time = tmpTime;
}
} else {
time = millis + millisInDay - computeZoneOffset(millis, millisInDay);
}
}
}
/**
* Find the previous zone transtion near the given time.
*
* @param base The base time, inclusive.
* @return The time of the previous transition, or null if not found.
*/
private Long getImmediatePreviousZoneTransition(long base) {
Long transitionTime = null;
if (zone instanceof BasicTimeZone) {
TimeZoneTransition transition = ((BasicTimeZone) zone).getPreviousTransition(base, true);
if (transition != null) {
transitionTime = transition.getTime();
}
} else {
// Usually, it is enough to check past one hour because such transition is most
// likely +1 hour shift. However, there is an example jumped +24 hour in the tz database.
transitionTime = getPreviousZoneTransitionTime(zone, base, 2 * 60 * 60 * 1000); // check last 2 hours
if (transitionTime == null) {
transitionTime = getPreviousZoneTransitionTime(zone, base, 30 * 60 * 60 * 1000); // try last 30 hours
}
}
return transitionTime;
}
/**
* Find the previous zone transition within the specified duration.
* Note: This method is only used when TimeZone is NOT a BasicTimeZone.
* @param tz The time zone.
* @param base The base time, inclusive.
* @param duration The range of time evaluated.
* @return The time of the previous zone transition, or null if not available.
*/
private static Long getPreviousZoneTransitionTime(TimeZone tz, long base, long duration) {
assert duration > 0;
long upper = base;
long lower = base - duration - 1;
int offsetU = tz.getOffset(upper);
int offsetL = tz.getOffset(lower);
if (offsetU == offsetL) {
return null;
}
return findPreviousZoneTransitionTime(tz, offsetU, upper, lower);
}
/**
* The time units used by {@link #findPreviousZoneTransitionTime(TimeZone, int, long, long)}
* for optimizing transition time binary search.
*/
private static final int[] FIND_ZONE_TRANSITION_TIME_UNITS = {
60*60*1000, // 1 hour
30*60*1000, // 30 minutes
60*1000, // 1 minute
1000, // 1 second
};
/**
* Implementing binary search for zone transtion detection, used by {@link #getPreviousZoneTransitionTime(TimeZone, long, long)}
* @param tz The time zone.
* @param upperOffset The zone offset at upper
* @param upper The upper bound, inclusive.
* @param lower The lower bound, exclusive.
* @return The time of the previous zone transition, or null if not available.
*/
private static Long findPreviousZoneTransitionTime(TimeZone tz, int upperOffset, long upper, long lower) {
boolean onUnitTime = false;
long mid = 0;
for (int unit : FIND_ZONE_TRANSITION_TIME_UNITS) {
long lunits = lower/unit;
long uunits = upper/unit;
if (uunits > lunits) {
mid = ((lunits + uunits + 1) >>> 1) * unit;
onUnitTime = true;
break;
}
}
int midOffset;
if (!onUnitTime) {
mid = (upper + lower) >>> 1;
}
if (onUnitTime) {
if (mid != upper) {
midOffset = tz.getOffset(mid);
if (midOffset != upperOffset) {
return findPreviousZoneTransitionTime(tz, upperOffset, upper, mid);
}
upper = mid;
}
// check mid-1
mid--;
} else {
mid = (upper + lower) >>> 1;
}
if (mid == lower) {
return Long.valueOf(upper);
}
midOffset = tz.getOffset(mid);
if (midOffset != upperOffset) {
if (onUnitTime) {
return Long.valueOf(upper);
}
return findPreviousZoneTransitionTime(tz, upperOffset, upper, mid);
}
return findPreviousZoneTransitionTime(tz, upperOffset, mid, lower);
}
/**
* Compute the milliseconds in the day from the fields. This is a
* value from 0 to 23:59:59.999 inclusive, unless fields are out of
* range, in which case it can be an arbitrary value. This value
* reflects local zone wall time.
*/
protected int computeMillisInDay() {
// Do the time portion of the conversion.
int millisInDay = 0;
// 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 = Math.max(stamp[HOUR], stamp[AM_PM]);
int bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
// Hours
if (bestStamp != UNSET) {
if (bestStamp == hourOfDayStamp) {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay += internalGet(HOUR_OF_DAY);
} else {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay += internalGet(HOUR);
millisInDay += 12 * internalGet(AM_PM); // Default works for unset AM_PM
}
}
// We use the fact that unset == 0; we start with millisInDay
// == HOUR_OF_DAY.
millisInDay *= 60;
millisInDay += internalGet(MINUTE); // now have minutes
millisInDay *= 60;
millisInDay += internalGet(SECOND); // now have seconds
millisInDay *= 1000;
millisInDay += internalGet(MILLISECOND); // now have millis
return millisInDay;
}
/**
* This method can assume EXTENDED_YEAR has been set.
* @param millis milliseconds of the date fields (local midnight millis)
* @param millisInDay milliseconds of the time fields; may be out
* or range.
* @return total zone offset (raw + DST) for the given moment
*/
protected int computeZoneOffset(long millis, int millisInDay) {
int[] offsets = new int[2];
long wall = millis + millisInDay;
if (zone instanceof BasicTimeZone) {
int duplicatedTimeOpt = (repeatedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_FORMER : BasicTimeZone.LOCAL_LATTER;
int nonExistingTimeOpt = (skippedWallTime == WALLTIME_FIRST) ? BasicTimeZone.LOCAL_LATTER : BasicTimeZone.LOCAL_FORMER;
((BasicTimeZone)zone).getOffsetFromLocal(wall, nonExistingTimeOpt, duplicatedTimeOpt, offsets);
} else {
// By default, TimeZone#getOffset behaves WALLTIME_LAST for both.
zone.getOffset(wall, true, offsets);
boolean sawRecentNegativeShift = false;
if (repeatedWallTime == WALLTIME_FIRST) {
// Check if the given wall time falls into repeated time range
long tgmt = wall - (offsets[0] + offsets[1]);
// Any negative zone transition within last 6 hours?
// Note: The maximum historic negative zone transition is -3 hours in the tz database.
// 6 hour window would be sufficient for this purpose.
int offsetBefore6 = zone.getOffset(tgmt - 6*60*60*1000);
int offsetDelta = (offsets[0] + offsets[1]) - offsetBefore6;
assert offsetDelta < -6*60*60*1000 : offsetDelta;
if (offsetDelta < 0) {
sawRecentNegativeShift = true;
// Negative shift within last 6 hours. When WALLTIME_FIRST is used and the given wall time falls
// into the repeated time range, use offsets before the transition.
// Note: If it does not fall into the repeated time range, offsets remain unchanged below.
zone.getOffset(wall + offsetDelta, true, offsets);
}
}
if (!sawRecentNegativeShift && skippedWallTime == WALLTIME_FIRST) {
// When skipped wall time option is WALLTIME_FIRST,
// recalculate offsets from the resolved time (non-wall).
// When the given wall time falls into skipped wall time,
// the offsets will be based on the zone offsets AFTER
// the transition (which means, earliest possibe interpretation).
long tgmt = wall - (offsets[0] + offsets[1]);
zone.getOffset(tgmt, false, offsets);
}
}
return offsets[0] + offsets[1];
}
/**
* Compute the Julian day number as specified by this calendar's fields.
*/
protected int computeJulianDay() {
// We want to see if any of the date fields is newer than the
// JULIAN_DAY. If not, then we use JULIAN_DAY. If so, then we do
// the normal resolution. We only use JULIAN_DAY if it has been
// set by the user. This makes it possible for the caller to set
// the calendar to a time and call clear(MONTH) to reset the MONTH
// to January. This is legacy behavior. Without this,
// clear(MONTH) has no effect, since the internally set JULIAN_DAY
// is used.
if (stamp[JULIAN_DAY] >= MINIMUM_USER_STAMP) {
int bestStamp = newestStamp(ERA, DAY_OF_WEEK_IN_MONTH, UNSET);
bestStamp = newestStamp(YEAR_WOY, EXTENDED_YEAR, bestStamp);
if (bestStamp <= stamp[JULIAN_DAY]) {
return internalGet(JULIAN_DAY);
}
}
int bestField = resolveFields(getFieldResolutionTable());
if (bestField < 0) {
bestField = DAY_OF_MONTH;
}
return handleComputeJulianDay(bestField);
}
/**
* Returns the field resolution array for this calendar. Calendars that
* define additional fields or change the semantics of existing fields
* should override this method to adjust the field resolution semantics
* accordingly. Other subclasses should not override this method.
* @see #resolveFields
*/
protected int[][][] getFieldResolutionTable() {
return DATE_PRECEDENCE;
}
/**
* Returns the Julian day number of day before the first day of the
* given month in the given extended year. Subclasses should override
* this method to implement their calendar system.
* @param eyear the extended year
* @param month the zero-based month, or 0 if useMonth is false
* @param useMonth if false, compute the day before the first day of
* the given year, otherwise, compute the day before the first day of
* the given month
* @return the Julian day number of the day before the first
* day of the given month and year
*/
abstract protected int handleComputeMonthStart(int eyear, int month,
boolean useMonth);
/**
* Returns the extended year defined by the current fields. This will
* use the EXTENDED_YEAR field or the YEAR and supra-year fields (such
* as ERA) specific to the calendar system, depending on which set of
* fields is newer.
* @return the extended year
*/
abstract protected int handleGetExtendedYear();
// (The following method is not called because all existing subclasses
// override it. 2003-06-11 ICU 2.6 Alan)
///CLOVER:OFF
/**
* Returns the number of days in the given month of the given extended
* year of this calendar system. Subclasses should override this
* method if they can provide a more correct or more efficient
* implementation than the default implementation in Calendar.
*/
protected int handleGetMonthLength(int extendedYear, int month) {
return handleComputeMonthStart(extendedYear, month+1, true) -
handleComputeMonthStart(extendedYear, month, true);
}
///CLOVER:ON
/**
* Returns the number of days in the given extended year of this
* calendar system. Subclasses should override this method if they can
* provide a more correct or more efficient implementation than the
* default implementation in Calendar.
*/
protected int handleGetYearLength(int eyear) {
return handleComputeMonthStart(eyear+1, 0, false) -
handleComputeMonthStart(eyear, 0, false);
}
/**
* Subclasses that use additional fields beyond those defined in
* Calendar
should override this method to return an
* int[]
array of the appropriate length. The length
* must be at least BASE_FIELD_COUNT
and no more than
* MAX_FIELD_COUNT
.
*/
protected int[] handleCreateFields() {
return new int[BASE_FIELD_COUNT];
}
/**
* Subclasses may override this.
* Called by handleComputeJulianDay. Returns the default month (0-based) for the year,
* taking year and era into account. Defaults to 0 (JANUARY) for Gregorian.
* @param extendedYear the extendedYear, as returned by handleGetExtendedYear
* @return the default month
* @see #MONTH
* @hide draft / provisional / internal are hidden on Android
*/
protected int getDefaultMonthInYear(int extendedYear) {
return Calendar.JANUARY;
}
/**
* Subclasses may override this.
* Called by handleComputeJulianDay. Returns the default day (1-based) for the month,
* taking currently-set year and era into account. Defaults to 1 for Gregorian.
* @param extendedYear the extendedYear, as returned by handleGetExtendedYear
* @param month the month, as returned by getDefaultMonthInYear
* @return the default day of the month
* @see #DAY_OF_MONTH
* @hide draft / provisional / internal are hidden on Android
*/
protected int getDefaultDayInMonth(int extendedYear, int month) {
return 1;
}
/**
* Subclasses may override this. This method calls
* handleGetMonthLength() to obtain the calendar-specific month
* length.
*/
protected int handleComputeJulianDay(int bestField) {
boolean useMonth = (bestField == DAY_OF_MONTH ||
bestField == WEEK_OF_MONTH ||
bestField == DAY_OF_WEEK_IN_MONTH);
int year;
if (bestField == WEEK_OF_YEAR) {
// Nota Bene! It is critical that YEAR_WOY be used as the year here, if it is
// set. Otherwise, when WOY is the best field, the year may be wrong at the
// extreme limits of the year. If YEAR_WOY is not set then it will fall back.
// TODO: Should resolveField(YEAR_PRECEDENCE) be brought to bear?
year = internalGet(YEAR_WOY, handleGetExtendedYear());
} else {
year = handleGetExtendedYear();
}
internalSet(EXTENDED_YEAR, year);
int month = useMonth ? internalGet(MONTH, getDefaultMonthInYear(year)) : 0;
// Get the Julian day of the day BEFORE the start of this year.
// If useMonth is true, get the day before the start of the month.
int julianDay = handleComputeMonthStart(year, month, useMonth);
if (bestField == DAY_OF_MONTH) {
if(isSet(DAY_OF_MONTH)) {
return julianDay + internalGet(DAY_OF_MONTH, getDefaultDayInMonth(year, month));
} else {
return julianDay + getDefaultDayInMonth(year, month);
}
}
if (bestField == DAY_OF_YEAR) {
return julianDay + internalGet(DAY_OF_YEAR);
}
int firstDOW = getFirstDayOfWeek(); // Localized fdw
// At this point julianDay is the 0-based day BEFORE the first day of
// January 1, year 1 of the given calendar. If julianDay == 0, it
// specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
// or Gregorian).
// At this point we need to process the WEEK_OF_MONTH or
// WEEK_OF_YEAR, which are similar, or the DAY_OF_WEEK_IN_MONTH.
// First, perform initial shared computations. These locate the
// first week of the period.
// Get the 0-based localized DOW of day one of the month or year.
// Valid range 0..6.
int first = julianDayToDayOfWeek(julianDay + 1) - firstDOW;
if (first < 0) {
first += 7;
}
// Get zero-based localized DOW, valid range 0..6. This is the DOW
// we are looking for.
int dowLocal = 0;
switch (resolveFields(DOW_PRECEDENCE)) {
case DAY_OF_WEEK:
dowLocal = internalGet(DAY_OF_WEEK) - firstDOW;
break;
case DOW_LOCAL:
dowLocal = internalGet(DOW_LOCAL) - 1;
break;
}
dowLocal = dowLocal % 7;
if (dowLocal < 0) {
dowLocal += 7;
}
// Find the first target DOW (dowLocal) in the month or year.
// Actually, it may be just before the first of the month or year.
// It will be an integer from -5..7.
int date = 1 - first + dowLocal;
if (bestField == DAY_OF_WEEK_IN_MONTH) {
// Adjust the target DOW to be in the month or year.
if (date < 1) {
date += 7;
}
// The only trickiness occurs if the day-of-week-in-month is
// negative.
int dim = internalGet(DAY_OF_WEEK_IN_MONTH, 1);
if (dim >= 0) {
date += 7*(dim - 1);
} else {
// Move date to the last of this day-of-week in this month,
// then back up as needed. If dim==-1, we don't back up at
// all. If dim==-2, we back up once, etc. Don't back up
// past the first of the given day-of-week in this month.
// Note that we handle -2, -3, etc. correctly, even though
// values < -1 are technically disallowed.
int m = internalGet(MONTH, JANUARY);
int monthLength = handleGetMonthLength(year, m);
date += ((monthLength - date) / 7 + dim + 1) * 7;
}
} else {
// assert(bestField == WEEK_OF_MONTH || bestField == WEEK_OF_YEAR)
// Adjust for minimal days in first week
if ((7 - first) < getMinimalDaysInFirstWeek()) {
date += 7;
}
// Now adjust for the week number.
date += 7 * (internalGet(bestField) - 1);
}
return julianDay + date;
}
/**
* Compute the Julian day of a month of the Gregorian calendar.
* Subclasses may call this method to perform a Gregorian calendar
* fields->millis computation. To perform a Gregorian calendar
* millis->fields computation, call computeGregorianFields().
* @param year extended Gregorian year
* @param month zero-based Gregorian month
* @return the Julian day number of the day before the first
* day of the given month in the given extended year
* @see #computeGregorianFields
*/
protected int computeGregorianMonthStart(int year, int month) {
// If the month is out of range, adjust it into range, and
// modify the extended year value accordingly.
if (month < 0 || month > 11) {
int[] rem = new int[1];
year += floorDivide(month, 12, rem);
month = rem[0];
}
boolean isLeap = (year%4 == 0) && ((year%100 != 0) || (year%400 == 0));
int y = year - 1;
// This computation is actually ... + (JAN_1_1_JULIAN_DAY - 3) + 2.
// Add 2 because Gregorian calendar starts 2 days after Julian
// calendar.
int julianDay = 365*y + floorDivide(y, 4) - floorDivide(y, 100) +
floorDivide(y, 400) + JAN_1_1_JULIAN_DAY - 1;
// At this point julianDay indicates the day BEFORE the first day
// of January 1,
*
* Subclasses can refer to the DAY_OF_WEEK and DOW_LOCAL fields, which
* will be set when this method is called. Subclasses can also call
* the getGregorianXxx() methods to obtain Gregorian calendar
* equivalents for the given Julian day.
*
* Calendar
implements
* a pure proleptic Gregorian calendar.
*/
protected void handleComputeFields(int julianDay) {
internalSet(MONTH, getGregorianMonth());
internalSet(DAY_OF_MONTH, getGregorianDayOfMonth());
internalSet(DAY_OF_YEAR, getGregorianDayOfYear());
int eyear = getGregorianYear();
internalSet(EXTENDED_YEAR, eyear);
int era = GregorianCalendar.AD;
if (eyear < 1) {
era = GregorianCalendar.BC;
eyear = 1 - eyear;
}
internalSet(ERA, era);
internalSet(YEAR, eyear);
}
///CLOVER:ON
//----------------------------------------------------------------------
// Subclass API
// For subclasses to call
//----------------------------------------------------------------------
/**
* Returns the extended year on the Gregorian calendar as computed by
* computeGregorianFields()
.
* @see #computeGregorianFields
*/
protected final int getGregorianYear() {
return gregorianYear;
}
/**
* Returns the month (0-based) on the Gregorian calendar as computed by
* computeGregorianFields()
.
* @see #computeGregorianFields
*/
protected final int getGregorianMonth() {
return gregorianMonth;
}
/**
* Returns the day of year (1-based) on the Gregorian calendar as
* computed by computeGregorianFields()
.
* @see #computeGregorianFields
*/
protected final int getGregorianDayOfYear() {
return gregorianDayOfYear;
}
/**
* Returns the day of month (1-based) on the Gregorian calendar as
* computed by computeGregorianFields()
.
* @see #computeGregorianFields
*/
protected final int getGregorianDayOfMonth() {
return gregorianDayOfMonth;
}
/**
* [icu] Returns the number of fields defined by this calendar. Valid field
* arguments to set()
and get()
are
* 0..getFieldCount()-1
.
*/
public final int getFieldCount() {
return fields.length;
}
/**
* Set a field to a value. Subclasses should use this method when
* computing fields. It sets the time stamp in the
* stamp[]
array to INTERNALLY_SET
. If a
* field that may not be set by subclasses is passed in, an
* IllegalArgumentException
is thrown. This prevents
* subclasses from modifying fields that are intended to be
* calendar-system invariant.
*/
protected final void internalSet(int field, int value) {
if (((1 << field) & internalSetMask) == 0) {
throw new IllegalStateException("Subclass cannot set " +
fieldName(field));
}
fields[field] = value;
stamp[field] = INTERNALLY_SET;
}
private static final int[][] GREGORIAN_MONTH_COUNT = {
//len len2 st st2
{ 31, 31, 0, 0 }, // Jan
{ 28, 29, 31, 31 }, // Feb
{ 31, 31, 59, 60 }, // Mar
{ 30, 30, 90, 91 }, // Apr
{ 31, 31, 120, 121 }, // May
{ 30, 30, 151, 152 }, // Jun
{ 31, 31, 181, 182 }, // Jul
{ 31, 31, 212, 213 }, // Aug
{ 30, 30, 243, 244 }, // Sep
{ 31, 31, 273, 274 }, // Oct
{ 30, 30, 304, 305 }, // Nov
{ 31, 31, 334, 335 } // Dec
// len length of month
// len2 length of month in a leap year
// st days in year before start of month
// st2 days in year before month in leap year
};
/**
* Determines if the given year is a leap year. Returns true if the
* given year is a leap year.
* @param year the given year.
* @return true if the given year is a leap year; false otherwise.
*/
protected static final boolean isGregorianLeapYear(int year) {
return (year%4 == 0) && ((year%100 != 0) || (year%400 == 0));
}
/**
* Returns the length of a month of the Gregorian calendar.
* @param y the extended year
* @param m the 0-based month number
* @return the number of days in the given month
*/
protected static final int gregorianMonthLength(int y, int m) {
return GREGORIAN_MONTH_COUNT[m][isGregorianLeapYear(y)?1:0];
}
/**
* Returns the length of a previous month of the Gregorian calendar.
* @param y the extended year
* @param m the 0-based month number
* @return the number of days in the month previous to the given month
*/
protected static final int gregorianPreviousMonthLength(int y, int m) {
return (m > 0) ? gregorianMonthLength(y, m-1) : 31;
}
/**
* Divide two long integers, returning the floor of the quotient.
* -1/4
=> 0
* but floorDivide(-1,4)
=> -1.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @return the floor of the quotient.
*/
protected static final long floorDivide(long numerator, long denominator) {
// We do this computation in order to handle
// a numerator of Long.MIN_VALUE correctly
return (numerator >= 0) ?
numerator / denominator :
((numerator + 1) / denominator) - 1;
}
/**
* Divide two integers, returning the floor of the quotient.
* -1/4
=> 0
* but floorDivide(-1,4)
=> -1.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @return the floor of the quotient.
*/
protected static final int floorDivide(int numerator, int denominator) {
// We do this computation in order to handle
// a numerator of Integer.MIN_VALUE correctly
return (numerator >= 0) ?
numerator / denominator :
((numerator + 1) / denominator) - 1;
}
/**
* Divide two integers, returning the floor of the quotient, and
* the modulus remainder.
* -1/4
=> 0 and -1%4
=> -1,
* but floorDivide(-1,4)
=> -1 with remainder[0]
=> 3.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @param remainder an array of at least one element in which the value
* numerator mod denominator
is returned. Unlike numerator
* % denominator
, this will always be non-negative.
* @return the floor of the quotient.
*/
protected static final int floorDivide(int numerator, int denominator, int[] remainder) {
if (numerator >= 0) {
remainder[0] = numerator % denominator;
return numerator / denominator;
}
int quotient = ((numerator + 1) / denominator) - 1;
remainder[0] = numerator - (quotient * denominator);
return quotient;
}
/**
* Divide two integers, returning the floor of the quotient, and
* the modulus remainder.
* -1/4
=> 0 and -1%4
=> -1,
* but floorDivide(-1,4)
=> -1 with remainder[0]
=> 3.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @param remainder an array of at least one element in which the value
* numerator mod denominator
is returned. Unlike numerator
* % denominator
, this will always be non-negative.
* @return the floor of the quotient.
*/
protected static final int floorDivide(long numerator, int denominator, int[] remainder) {
if (numerator >= 0) {
remainder[0] = (int)(numerator % denominator);
return (int)(numerator / denominator);
}
int quotient = (int)(((numerator + 1) / denominator) - 1);
remainder[0] = (int)(numerator - ((long)quotient * denominator));
return quotient;
}
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", "YEAR_WOY", "DOW_LOCAL", "EXTENDED_YEAR",
"JULIAN_DAY", "MILLISECONDS_IN_DAY",
};
/**
* Returns a string name for a field, for debugging and exceptions.
*/
protected String fieldName(int field) {
try {
return FIELD_NAME[field];
} catch (ArrayIndexOutOfBoundsException e) {
return "Field " + field;
}
}
/**
* Converts time as milliseconds to Julian day.
* @param millis the given milliseconds.
* @return the Julian day number.
*/
protected static final int millisToJulianDay(long millis) {
return (int) (EPOCH_JULIAN_DAY + floorDivide(millis, ONE_DAY));
}
/**
* Converts Julian day to time as milliseconds.
* @param julian the given Julian day number.
* @return time as milliseconds.
*/
protected static final long julianDayToMillis(int julian) {
return (julian - EPOCH_JULIAN_DAY) * ONE_DAY;
}
/**
* Returns the day of week, from SUNDAY to SATURDAY, given a Julian day.
*/
protected static final int julianDayToDayOfWeek(int julian) {
// If julian is negative, then julian%7 will be negative, so we adjust
// accordingly. Julian day 0 is Monday.
int dayOfWeek = (julian + MONDAY) % 7;
if (dayOfWeek < SUNDAY) {
dayOfWeek += 7;
}
return dayOfWeek;
}
/**
* Returns the current milliseconds without recomputing.
*/
protected final long internalGetTimeInMillis() {
return time;
}
/**
* [icu] Returns the calendar type name string for this Calendar object.
* The returned string is the legacy ICU calendar attribute value,
* for example, "gregorian" or "japanese".
*
* true
. A subclass may
* return false
if such practice is not applicable (for example,
* Chinese calendar and Japanese calendar).
*
* @return true
if this calendar has a default century.
* @deprecated This API is ICU internal only.
* @hide original deprecated declaration
* @hide draft / provisional / internal are hidden on Android
*/
@Deprecated
public boolean haveDefaultCentury() {
return true;
}
// -------- BEGIN ULocale boilerplate --------
/**
* [icu] Returns the locale that was used to create this object, or null.
* This may may differ from the locale requested at the time of
* this object's creation. For example, if an object is created
* for locale en_US_CALIFORNIA, the actual data may be
* drawn from en (the actual locale), and
* en_US may be the most specific locale that exists (the
* valid locale).
*
*