All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ibm.icu.util.Calendar Maven / Gradle / Ivy

Go to download

International Component for Unicode for Java (ICU4J) is a mature, widely used Java library providing Unicode and Globalization support

There is a newer version: 76.1
Show newest version
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
 *   Copyright (C) 1996-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 */

package com.ibm.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 com.ibm.icu.impl.CalType;
import com.ibm.icu.impl.CalendarUtil;
import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.SoftCache;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.ULocale.Category;

/**
 * {@icuenhanced java.util.Calendar}.{@icu _usage_}
 *
 * 

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:

*
Calendar cal = Calendar.getInstance(new ULocale("en_US@calendar=japanese"));
*
will return an instance of JapaneseCalendar (using en_US conventions for * minimum days in first week, start day of week, et cetera). * *

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. * *

*
 * 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
*
* * For the time of day: * *
*
 * 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: *

    *
  1. 24:00:00 "belongs" to the following day. That is, * 23:59 on Dec 31, 1969 < 24:00 on Jan 1, 1970 < 24:01:00 on Jan 1, 1970 * *
  2. Although historically not precise, midnight also belongs to "am", * and noon belongs to "pm", so on the same day, * 12:00 am (midnight) < 12:01 am, and 12:00 pm (noon) < 12:01 pm *
* *

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 field f before the * call is delta, modulo any overflow that has occurred in * field f. 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 com.ibm.icu.util.Calendar differs from * java.util.Calendar. *

* *

Changes

* *

Overview of changes between the classic Calendar * architecture and the new architecture. * *

    * *
  • The 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.
  • * *
  • The 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.
  • * *
  • The scope of responsibility of subclasses has been drastically * reduced. As much functionality as possible is implemented in the * 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.
  • * *
  • The subclassing API has been completely redesigned.
  • * *
  • The 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.
  • * *
  • The 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.
  • * *
  • The range of handled dates has been narrowed from +/- * ~300,000,000 years to +/- ~5,000,000 years. In practical terms * this should not affect clients. However, it does mean that client * code cannot be guaranteed well-behaved results with dates such as * 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.
  • * *
  • New fields are implemented: {@link #JULIAN_DAY} defines * single-field specification of the * date. {@link #MILLISECONDS_IN_DAY} defines a single-field * specification of the wall time. {@link #DOW_LOCAL} and * {@link #YEAR_WOY} implement localized day-of-week and * week-of-year behavior.
  • * *
  • Subclasses can access protected millisecond constants * defined in Calendar.
  • * *
  • New API has been added to support calendar-specific subclasses * of DateFormat.
  • * *
  • Several subclasses have been implemented, representing * various international calendar systems.
  • * *
* *

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. * *

    * *
  • Subclasses should implement {@link #handleComputeFields} * to compute the {@link #ERA}, {@link #YEAR}, * {@link #EXTENDED_YEAR}, {@link #MONTH}, * {@link #DAY_OF_MONTH}, and {@link #DAY_OF_YEAR} fields, * based on the value of the {@link #JULIAN_DAY} field. If there * are calendar-specific fields not defined by 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. * *

    * *
  • Subclasses should implement {@link #handleGetExtendedYear} * to return the extended year for this calendar system, based on the * {@link #YEAR}, {@link #EXTENDED_YEAR}, and any fields that * the calendar system uses that are larger than a year, such as * {@link #ERA}.
  • * *
  • Subclasses should implement {@link #handleComputeMonthStart} * to return the Julian day number * associated with a month and extended year. This is the Julian day * number of the day before the first day of the month. The month * number is zero-based. This computation should not depend on any * field values.
  • * *
* *

Other methods * *

    * *
  • Subclasses should implement {@link #handleGetMonthLength} * to return the number of days in a * given month of a given extended year. The month number, as always, * is zero-based.
  • * *
  • Subclasses should implement {@link #handleGetYearLength} * to return the number of days in the given * extended year. This method is used by * computeWeekFields to compute the * {@link #WEEK_OF_YEAR} and {@link #YEAR_WOY} fields.
  • * *
  • Subclasses should implement {@link #handleGetLimit} * to return the protected values of a field, depending on the value of * 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.
  • * *
  • Optionally, subclasses may override {@link #validateField} * to check any subclass-specific fields. If the * field's value is out of range, the method should throw an * 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).
  • * *
  • Optionally, subclasses may override * {@link #handleCreateFields} to create an 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.
  • * *
  • Optionally, subclasses may override * {@link #handleGetDateFormat} to create a * 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.
  • * *
  • Optionally, subclasses may override {@link #roll roll} and * {@link #add add} to handle fields that are discontinuous. 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} and {@link * HebrewCalendar#roll HebrewCalendar.roll} methods take this into * account, so that adding 1 month to Shevat gives the proper result * (Adar) in a non-leap year. The protected utility method {@link * #pinField pinField} is often useful when implementing these two * methods.
  • * *
* *

Normalized behavior * *

The behavior of certain fields has been made consistent across all * calendar systems and implemented in Calendar. * *

    * *
  • Time is normalized. Even though some calendar systems transition * between days at sunset or at other times, all ICU4J calendars * transition between days at local zone midnight. This * allows ICU4J to centralize the time computations in * 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}.
  • * *
  • DST behavior is normalized. Daylight savings time behavior is * computed the same for all calendar systems, and depends on the * value of several 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.
  • * *
  • Weeks are normalized. Although locales define the week * differently, in terms of the day on which it starts, and the * designation of week number one of a month or year, they all use a * common mechanism. Furthermore, the day of the week has a simple * and consistent definition throughout history. For example, * although the Gregorian calendar introduced a discontinuity when * first instituted, the day of week was not disrupted. For this * reason, the fields {@link #DAY_OF_WEEK}, 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 * *

    * *
  • Calendars implementations are proleptic. For example, * even though the Gregorian calendar was not instituted until the * 16th century, the 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.
  • * *
  • Calendar system subclasses compute a extended * year. This differs from the {@link #YEAR} field in that * it ranges over all integer values, including zero and negative * values, and it encapsulates the information of the * {@link #YEAR} field and all larger fields. Thus, for the * Gregorian calendar, the {@link #EXTENDED_YEAR} is computed as * 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.
  • * *
* * @see Date * @see GregorianCalendar * @see TimeZone * @see DateFormat * @author Mark Davis, Deborah Goldsmith, Chen-Lieh Huang, Alan Liu, Laura Werner * @stable ICU 2.0 */ public abstract class Calendar implements Serializable, Cloneable, Comparable { // Data flow in Calendar // --------------------- // The current time is represented in two ways by Calendar: as UTC // milliseconds from the epoch start (1 January 1970 0:00 UTC), and as local // fields such as MONTH, HOUR, AM_PM, etc. It is possible to compute the // millis from the fields, and vice versa. The data needed to do this // conversion is encapsulated by a TimeZone object owned by the Calendar. // The data provided by the TimeZone object may also be overridden if the // user sets the ZONE_OFFSET and/or DST_OFFSET fields directly. The class // keeps track of what information was most recently set by the caller, and // uses that to compute any other information as needed. // If the user sets the fields using set(), the data flow is as follows. // This is implemented by the Calendar subclass's computeTime() method. // During this process, certain fields may be ignored. The disambiguation // algorithm for resolving which fields to pay attention to is described // above. // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) // | // | Using Calendar-specific algorithm // V // local standard millis // | // | Using TimeZone or user-set ZONE_OFFSET / DST_OFFSET // V // UTC millis (in time data member) // If the user sets the UTC millis using setTime(), the data flow is as // follows. This is implemented by the Calendar subclass's computeFields() // method. // UTC millis (in time data member) // | // | Using TimeZone getOffset() // V // local standard millis // | // | Using Calendar-specific algorithm // V // local fields (YEAR, MONTH, DATE, HOUR, MINUTE, etc.) // In general, a round trip from fields, through local and UTC millis, and // back out to fields is made when necessary. This is implemented by the // complete() method. Resolving a partial set of fields into a UTC millis // value allows all remaining fields to be generated from that value. If // the Calendar is lenient, the fields are also renormalized to standard // ranges when they are regenerated. /** * Field number for 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 * @stable ICU 2.0 */ public final static int ERA = 0; /** * Field number for get and set indicating the * year. This is a calendar-specific value; see subclass documentation. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ public final static int MILLISECOND = 14; /** * Field number for get and set indicating the * raw offset from GMT in milliseconds. * @stable ICU 2.0 */ public final static int ZONE_OFFSET = 15; /** * Field number for get and set indicating the * daylight savings offset in milliseconds. * @stable ICU 2.0 */ 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}. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 4.4 */ 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. * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ @Deprecated protected static final int BASE_FIELD_COUNT = 23; /** * The maximum number of fields possible. Subclasses must not define * more total fields than this number. * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420. */ @Deprecated protected static final int MAX_FIELD_COUNT = 32; /** * Value of the DAY_OF_WEEK field indicating * Sunday. * @stable ICU 2.0 */ public final static int SUNDAY = 1; /** * Value of the DAY_OF_WEEK field indicating * Monday. * @stable ICU 2.0 */ public final static int MONDAY = 2; /** * Value of the DAY_OF_WEEK field indicating * Tuesday. * @stable ICU 2.0 */ public final static int TUESDAY = 3; /** * Value of the DAY_OF_WEEK field indicating * Wednesday. * @stable ICU 2.0 */ public final static int WEDNESDAY = 4; /** * Value of the DAY_OF_WEEK field indicating * Thursday. * @stable ICU 2.0 */ public final static int THURSDAY = 5; /** * Value of the DAY_OF_WEEK field indicating * Friday. * @stable ICU 2.0 */ public final static int FRIDAY = 6; /** * Value of the DAY_OF_WEEK field indicating * Saturday. * @stable ICU 2.0 */ public final static int SATURDAY = 7; /** * Value of the MONTH field indicating the * first month of the year. * @stable ICU 2.0 */ public final static int JANUARY = 0; /** * Value of the MONTH field indicating the * second month of the year. * @stable ICU 2.0 */ public final static int FEBRUARY = 1; /** * Value of the MONTH field indicating the * third month of the year. * @stable ICU 2.0 */ public final static int MARCH = 2; /** * Value of the MONTH field indicating the * fourth month of the year. * @stable ICU 2.0 */ public final static int APRIL = 3; /** * Value of the MONTH field indicating the * fifth month of the year. * @stable ICU 2.0 */ public final static int MAY = 4; /** * Value of the MONTH field indicating the * sixth month of the year. * @stable ICU 2.0 */ public final static int JUNE = 5; /** * Value of the MONTH field indicating the * seventh month of the year. * @stable ICU 2.0 */ public final static int JULY = 6; /** * Value of the MONTH field indicating the * eighth month of the year. * @stable ICU 2.0 */ public final static int AUGUST = 7; /** * Value of the MONTH field indicating the * ninth month of the year. * @stable ICU 2.0 */ public final static int SEPTEMBER = 8; /** * Value of the MONTH field indicating the * tenth month of the year. * @stable ICU 2.0 */ public final static int OCTOBER = 9; /** * Value of the MONTH field indicating the * eleventh month of the year. * @stable ICU 2.0 */ public final static int NOVEMBER = 10; /** * Value of the MONTH field indicating the * twelfth month of the year. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ public final static int UNDECIMBER = 12; /** * Value of the AM_PM field indicating the * period of the day from midnight to just before noon. * @stable ICU 2.0 */ public final static int AM = 0; /** * Value of the AM_PM field indicating the * period of the day from noon to just before midnight. * @stable ICU 2.0 */ 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)} */ @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)} */ @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)} */ @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)} */ @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() * @stable ICU 49 */ 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() * @stable ICU 49 */ 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() * @stable ICU 49 */ public static final int WALLTIME_NEXT_VALID = 2; /** * The number of milliseconds in one second. * @stable ICU 2.0 */ protected static final int ONE_SECOND = 1000; /** * The number of milliseconds in one minute. * @stable ICU 2.0 */ protected static final int ONE_MINUTE = 60*ONE_SECOND; /** * The number of milliseconds in one hour. * @stable ICU 2.0 */ 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). * @stable ICU 2.0 */ 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). * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ protected static final int MIN_JULIAN = -0x7F000000; /** * The minimum supported epoch milliseconds. This value is equivalent * to {@link #MIN_JULIAN} and {@link #MIN_DATE}. * @stable ICU 2.0 */ 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}. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ protected static final int MAX_JULIAN = +0x7F000000; /** * The maximum supported epoch milliseconds. This value is equivalent * to {@link #MAX_JULIAN} and {@link #MAX_DATE}. * @stable ICU 2.0 */ 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}. * @stable ICU 2.0 */ protected static final Date MAX_DATE = new Date(MAX_MILLIS); /** * The maximum supported hours for millisecond calculations */ private static final int MAX_HOURS = 548; // 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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: *
*
0 or not present on stream
*
* JDK 1.1.5 or earlier. *
*
1
*
* JDK 1.1.6 or later. Writes a correct 'time' value * as well as compatible values for other fields. This is a * transitional format. *
*
* When streaming out this class, the most recent format * and the highest allowable serialVersionOnStream * is written. * @serial * @since JDK1.1.6 */ // private int serialVersionOnStream = currentSerialVersion; // Proclaim serialization compatibility with JDK 1.1 // static final long serialVersionUID = -1807547505821590642L; // 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 3.2 */ 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; i values = new ArrayList<>(); UResourceBundle rb = UResourceBundle.getBundleInstance( ICUData.ICU_BASE_NAME, "supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER); UResourceBundle calPref = rb.get("calendarPreferenceData"); UResourceBundle order = null; try { order = calPref.get(prefRegion); } catch (MissingResourceException mre) { // use "001" as fallback order = calPref.get("001"); } String[] caltypes = order.getStringArray(); if (commonlyUsed) { // we have all commonly used calendar for the target region return caltypes; } // if not commonlyUsed, add all preferred calendars in the order for (int i = 0; i < caltypes.length; i++) { values.add(caltypes[i]); } // then, add other available clanedars for (CalType t : CalType.values()) { if (!values.contains(t.getId())) { values.add(t.getId()); } } return values.toArray(new String[values.size()]); } /** * Returns this Calendar's current time. * @return the current time. * @stable ICU 2.0 */ public final Date getTime() { return new Date( getTimeInMillis() ); } /** * Sets this Calendar's current time with the given Date. * *

Note: Calling 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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; inot trigger any calculations. * @param field the given time field. * @return the value for the given time field. * @stable ICU 2.0 */ protected final int internalGet(int field) { return fields[field]; } /** * Returns the value for a given time field, or return the given default * value if the field is not set. This is an internal method for * subclasses that does not trigger any calculations. * @param field the given time field. * @param defaultValue value to return if field is not set * @return the value for the given time field of defaultValue if the * field is unset * @stable ICU 2.0 */ protected final int internalGet(int field, int defaultValue) { return (stamp[field] > UNSET) ? fields[field] : defaultValue; } /** * Sets the time field with the given value. * @param field the given time field. * @param value the value to be set for the given time field. * @stable ICU 2.0 */ public final void set(int field, int value) { if (areFieldsVirtuallySet) { computeFields(); } fields[field] = value; /* Ensure that the fNextStamp value doesn't go pass max value for 32 bit integer */ if (nextStamp == STAMP_MAX) { recalculateStamp(); } stamp[field] = nextStamp++; isTimeSet = areFieldsSet = areFieldsVirtuallySet = false; } /** * Sets the values for the fields year, month, and date. * Previous values of other fields are retained. If this is not desired, * call {@link #clear()} first. * @param year the value used to set the YEAR time field. * @param month the value used to set the MONTH time field. * Month value is 0-based. e.g., 0 for January. * @param date the value used to set the DATE time field. * @stable ICU 2.0 */ public final void set(int year, int month, int date) { set(YEAR, year); set(MONTH, month); set(DATE, date); } /** * Sets the values for the fields year, month, date, hour, and minute. * Previous values of other fields are retained. If this is not desired, * call {@link #clear()} first. * @param year the value used to set the YEAR time field. * @param month the value used to set the MONTH time field. * Month value is 0-based. e.g., 0 for January. * @param date the value used to set the DATE time field. * @param hour the value used to set the HOUR_OF_DAY time field. * @param minute the value used to set the MINUTE time field. * @stable ICU 2.0 */ public final void set(int year, int month, int date, int hour, int minute) { set(YEAR, year); set(MONTH, month); set(DATE, date); set(HOUR_OF_DAY, hour); set(MINUTE, minute); } /** * Sets the values for the fields year, month, date, hour, minute, and second. * Previous values of other fields are retained. If this is not desired, * call {@link #clear} first. * @param year the value used to set the YEAR time field. * @param month the value used to set the MONTH time field. * Month value is 0-based. e.g., 0 for January. * @param date the value used to set the DATE time field. * @param hour the value used to set the HOUR_OF_DAY time field. * @param minute the value used to set the MINUTE time field. * @param second the value used to set the SECOND time field. * @stable ICU 2.0 */ public final void set(int year, int month, int date, int hour, int minute, int second) { set(YEAR, year); set(MONTH, month); set(DATE, date); set(HOUR_OF_DAY, hour); set(MINUTE, minute); set(SECOND, second); } // ------------------------------------- // For now the full getRelatedYear implementation is here; // per #10752 move the non-default implementation to subclasses // (default implementation will do no year adjustment) /** * utility function for getRelatedYear */ private static int gregoYearFromIslamicStart(int year) { // ad hoc conversion, improve under #10752 // rough est for now, ok for grego 1846-2138, // otherwise occasionally wrong (for 3% of years) int cycle, offset, shift = 0; if (year >= 1397) { cycle = (year - 1397) / 67; offset = (year - 1397) % 67; shift = 2*cycle + ((offset >= 33)? 1: 0); } else { cycle = (year - 1396) / 67 - 1; offset = -(year - 1396) % 67; shift = 2*cycle + ((offset <= 33)? 1: 0); } return year + 579 - shift; } /** * @internal * @deprecated This API is ICU internal only. */ @Deprecated public final int getRelatedYear() { int year = get(EXTENDED_YEAR); CalType type = CalType.GREGORIAN; String typeString = getType(); for (CalType testType : CalType.values()) { if (typeString.equals(testType.getId())) { type = testType; break; } } switch (type) { case PERSIAN: year += 622; break; case HEBREW: year -= 3760; break; case CHINESE: year -= 2637; break; case INDIAN: year += 79; break; case COPTIC: year += 284; break; case ETHIOPIC: year += 8; break; case ETHIOPIC_AMETE_ALEM: year -=5492; break; case DANGI: year -= 2333; break; case ISLAMIC_CIVIL: case ISLAMIC: case ISLAMIC_UMALQURA: case ISLAMIC_TBLA: case ISLAMIC_RGSA: year = gregoYearFromIslamicStart(year); break; // case GREGORIAN: // case JAPANESE: // case BUDDHIST: // case ROC: // case ISO8601: default: // do nothing, EXTENDED_YEAR same as Gregorian break; } return year; } // ------------------------------------- // For now the full setRelatedYear implementation is here; // per #10752 move the non-default implementation to subclasses // (default implementation will do no year adjustment) /** * utility function for setRelatedYear */ private static int firstIslamicStartYearFromGrego(int year) { // ad hoc conversion, improve under #10752 // rough est for now, ok for grego 1846-2138, // otherwise occasionally wrong (for 3% of years) int cycle, offset, shift = 0; if (year >= 1977) { cycle = (year - 1977) / 65; offset = (year - 1977) % 65; shift = 2*cycle + ((offset >= 32)? 1: 0); } else { cycle = (year - 1976) / 65 - 1; offset = -(year - 1976) % 65; shift = 2*cycle + ((offset <= 32)? 1: 0); } return year - 579 + shift; } /** * @internal * @deprecated This API is ICU internal only. */ @Deprecated public final void setRelatedYear(int year) { CalType type = CalType.GREGORIAN; String typeString = getType(); for (CalType testType : CalType.values()) { if (typeString.equals(testType.getId())) { type = testType; break; } } switch (type) { case PERSIAN: year -= 622; break; case HEBREW: year += 3760; break; case CHINESE: year += 2637; break; case INDIAN: year -= 79; break; case COPTIC: year -= 284; break; case ETHIOPIC: year -= 8; break; case ETHIOPIC_AMETE_ALEM: year +=5492; break; case DANGI: year += 2333; break; case ISLAMIC_CIVIL: case ISLAMIC: case ISLAMIC_UMALQURA: case ISLAMIC_TBLA: case ISLAMIC_RGSA: year = firstIslamicStartYearFromGrego(year); break; // case GREGORIAN: // case JAPANESE: // case BUDDHIST: // case ROC: // case ISO8601: default: // do nothing, EXTENDED_YEAR same as Gregorian break; } set(EXTENDED_YEAR, year); } /** * Clears the values of all the time fields. * @stable ICU 2.0 */ public final void clear() { for (int i=0; itrue if and only if the argument is * not null 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. * @stable ICU 2.0 */ @Override 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 * @stable ICU 2.4 */ 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. * @stable ICU 2.0 */ @Override 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * *

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 * @stable ICU 2.0 */ 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 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. *

* @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 * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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: *

* roll({@link #DATE}, true) *

* 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 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. *

* 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 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 * @stable ICU 2.0 */ 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. *

* 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). *

* {@icunote} 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 roll. *

* 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:
* 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. *

* Subclasses that have fields for which the assumption of continuity breaks * down must overide 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. *

* @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 * @stable ICU 2.0 */ 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., .roll(MONTH, 1) -> or . { int max = getActualMaximum(MONTH); int mon = (internalGet(MONTH) + amount) % (max+1); if (mon < 0) { mon += (max + 1); } set(MONTH, mon); // Keep the day of month in range. We don't want to spill over // into the next month; e.g., we don't want jan31 + 1 mo -> feb31 -> // mar3. pinField(DAY_OF_MONTH); return; } case YEAR: case YEAR_WOY: // * If era==0 and years go backwards in time, change sign of amount. // * Until we have new API per #9393, we temporarily hardcode knowledge of // which calendars have era 0 years that go backwards. { boolean era0WithYearsThatGoBackwards = false; int era = get(ERA); if (era == 0) { String calType = getType(); if (calType.equals("gregorian") || calType.equals("roc") || calType.equals("coptic")) { amount = -amount; era0WithYearsThatGoBackwards = true; } } int newYear = internalGet(field) + amount; if (era > 0 || newYear >= 1) { int maxYear = getActualMaximum(field); if (maxYear < 32768) { // this era has real bounds, roll should wrap years if (newYear < 1) { newYear = maxYear - ((-newYear) % maxYear); } else if (newYear > maxYear) { newYear = ((newYear - 1) % maxYear) + 1; } // else era is unbounded, just pin low year instead of wrapping } else if (newYear < 1) { newYear = 1; } // else we are in era 0 with newYear < 1; // calendars with years that go backwards must pin the year value at 0, // other calendars can have years < 0 in era 0 } else if (era0WithYearsThatGoBackwards) { newYear = 1; } set(field, newYear); pinField(MONTH); pinField(DAY_OF_MONTH); return; } case EXTENDED_YEAR: // Rolling the year can involve pinning the DAY_OF_MONTH. set(field, internalGet(field) + amount); pinField(MONTH); pinField(DAY_OF_MONTH); return; case WEEK_OF_MONTH: { // This is tricky, because during the roll we may have to shift // to a different day of the week. For example: // s m t w r f s // 1 2 3 4 5 // 6 7 8 9 10 11 12 // When rolling from the 6th or 7th back one week, we go to the // 1st (assuming that the first partial week counts). The same // thing happens at the end of the month. // The other tricky thing is that we have to figure out whether // the first partial week actually counts or not, based on the // minimal first days in the week. And we have to use the // correct first day of the week to delineate the week // boundaries. // Here's our algorithm. First, we find the real boundaries of // the month. Then we discard the first partial week if it // doesn't count in this locale. Then we fill in the ends with // phantom days, so that the first partial week and the last // partial week are full weeks. We then have a nice square // block of weeks. We do the usual rolling within this block, // as is done elsewhere in this method. If we wind up on one of // the phantom days that we added, we recognize this and pin to // the first or the last day of the month. Easy, eh? // Normalize the DAY_OF_WEEK so that 0 is the first day of the week // in this locale. We have dow in 0..6. int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); if (dow < 0) dow += 7; // Find the day of the week (normalized for locale) for the first // of the month. int fdm = (dow - internalGet(DAY_OF_MONTH) + 1) % 7; if (fdm < 0) fdm += 7; // Get the first day of the first full week of the month, // including phantom days, if any. Figure out if the first week // counts or not; if it counts, then fill in phantom days. If // not, advance to the first real full week (skip the partial week). int start; if ((7 - fdm) < getMinimalDaysInFirstWeek()) start = 8 - fdm; // Skip the first partial week else start = 1 - fdm; // This may be zero or negative // Get the day of the week (normalized for locale) for the last // day of the month. int monthLen = getActualMaximum(DAY_OF_MONTH); int ldm = (monthLen - internalGet(DAY_OF_MONTH) + dow) % 7; // We know monthLen >= DAY_OF_MONTH so we skip the += 7 step here. // Get the limit day for the blocked-off rectangular month; that // is, the day which is one past the last day of the month, // after the month has already been filled in with phantom days // to fill out the last week. This day has a normalized DOW of 0. int limit = monthLen + 7 - ldm; // Now roll between start and (limit - 1). int gap = limit - start; int day_of_month = (internalGet(DAY_OF_MONTH) + amount*7 - start) % gap; if (day_of_month < 0) day_of_month += gap; day_of_month += start; // Finally, pin to the real start and end of the month. if (day_of_month < 1) day_of_month = 1; if (day_of_month > monthLen) day_of_month = monthLen; // Set the DAY_OF_MONTH. We rely on the fact that this field // takes precedence over everything else (since all other fields // are also set at this point). If this fact changes (if the // disambiguation algorithm changes) then we will have to unset // the appropriate fields here so that DAY_OF_MONTH is attended // to. set(DAY_OF_MONTH, day_of_month); return; } case WEEK_OF_YEAR: { // This follows the outline of WEEK_OF_MONTH, except it applies // to the whole year. Please see the comment for WEEK_OF_MONTH // for general notes. // Normalize the DAY_OF_WEEK so that 0 is the first day of the week // in this locale. We have dow in 0..6. int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek(); if (dow < 0) dow += 7; // Find the day of the week (normalized for locale) for the first // of the year. int fdy = (dow - internalGet(DAY_OF_YEAR) + 1) % 7; if (fdy < 0) fdy += 7; // Get the first day of the first full week of the year, // including phantom days, if any. Figure out if the first week // counts or not; if it counts, then fill in phantom days. If // not, advance to the first real full week (skip the partial week). int start; if ((7 - fdy) < getMinimalDaysInFirstWeek()) start = 8 - fdy; // Skip the first partial week else start = 1 - fdy; // This may be zero or negative // Get the day of the week (normalized for locale) for the last // day of the year. int yearLen = getActualMaximum(DAY_OF_YEAR); int ldy = (yearLen - internalGet(DAY_OF_YEAR) + dow) % 7; // We know yearLen >= DAY_OF_YEAR so we skip the += 7 step here. // Get the limit day for the blocked-off rectangular year; that // is, the day which is one past the last day of the year, // after the year has already been filled in with phantom days // to fill out the last week. This day has a normalized DOW of 0. int limit = yearLen + 7 - ldy; // Now roll between start and (limit - 1). int gap = limit - start; int day_of_year = (internalGet(DAY_OF_YEAR) + amount*7 - start) % gap; if (day_of_year < 0) day_of_year += gap; day_of_year += start; // Finally, pin to the real start and end of the month. if (day_of_year < 1) day_of_year = 1; if (day_of_year > yearLen) day_of_year = yearLen; // Make sure that the year and day of year are attended to by // clearing other fields which would normally take precedence. // If the disambiguation algorithm is changed, this section will // have to be updated as well. set(DAY_OF_YEAR, day_of_year); clear(MONTH); return; } case DAY_OF_YEAR: { // Roll the day of year using millis. Compute the millis for // the start of the year, and get the length of the year. long delta = amount * ONE_DAY; // Scale up from days to millis long min2 = time - (internalGet(DAY_OF_YEAR) - 1) * ONE_DAY; int yearLength = getActualMaximum(DAY_OF_YEAR); time = (time + delta - min2) % (yearLength*ONE_DAY); if (time < 0) time += yearLength*ONE_DAY; setTimeInMillis(time + min2); return; } case DAY_OF_WEEK: case DOW_LOCAL: { // Roll the day of week using millis. Compute the millis for // the start of the week, using the first day of week setting. // Restrict the millis to [start, start+7days). long delta = amount * ONE_DAY; // Scale up from days to millis // Compute the number of days before the current day in this // week. This will be a value 0..6. int leadDays = internalGet(field); leadDays -= (field == DAY_OF_WEEK) ? getFirstDayOfWeek() : 1; if (leadDays < 0) leadDays += 7; long min2 = time - leadDays * ONE_DAY; time = (time + delta - min2) % ONE_WEEK; if (time < 0) time += ONE_WEEK; setTimeInMillis(time + min2); return; } case DAY_OF_WEEK_IN_MONTH: { // Roll the day of week in the month using millis. Determine // the first day of the week in the month, and then the last, // and then roll within that range. long delta = amount * ONE_WEEK; // Scale up from weeks to millis // Find the number of same days of the week before this one // in this month. int preWeeks = (internalGet(DAY_OF_MONTH) - 1) / 7; // Find the number of same days of the week after this one // in this month. int postWeeks = (getActualMaximum(DAY_OF_MONTH) - internalGet(DAY_OF_MONTH)) / 7; // From these compute the min and gap millis for rolling. long min2 = time - preWeeks * ONE_WEEK; long gap2 = ONE_WEEK * (preWeeks + postWeeks + 1); // Must add 1! // Roll within this range time = (time + delta - min2) % gap2; if (time < 0) time += gap2; setTimeInMillis(time + min2); return; } case JULIAN_DAY: set(field, internalGet(field) + amount); return; default: // Other fields cannot be rolled by this method throw new IllegalArgumentException("Calendar.roll(" + fieldName(field) + ") not supported"); } } /** * Add a signed amount to a specified field, using this calendar's rules. * For example, to add three days to the current date, you can call * add(Calendar.DATE, 3). *

* 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). *

* {@icunote} 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 add. *

* 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:
* 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. *

* Subclasses that have fields for which this assumption of continuity breaks * down must overide 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. *

* @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) * @stable ICU 2.0 */ @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 .add(MONTH, 1) to be // , rather than => . long delta = amount; // delta in ms boolean keepWallTimeInvariant = true; switch (field) { case ERA: set(field, get(field) + amount); pinField(ERA); return; case YEAR: case YEAR_WOY: // * If era=0 and years go backwards in time, change sign of amount. // * Until we have new API per #9393, we temporarily hardcode knowledge of // which calendars have era 0 years that go backwards. // * Note that for YEAR (but not YEAR_WOY) we could instead handle // this by applying the amount to the EXTENDED_YEAR field; but since // we would still need to handle YEAR_WOY as below, might as well // also handle YEAR the same way. { int era = get(ERA); if (era == 0) { String calType = getType(); if (calType.equals("gregorian") || calType.equals("roc") || calType.equals("coptic")) { amount = -amount; } } } // Fall through into standard handling case EXTENDED_YEAR: case MONTH: { boolean oldLenient = isLenient(); setLenient(true); set(field, get(field) + amount); pinField(DAY_OF_MONTH); if(oldLenient==false) { complete(); setLenient(oldLenient); } } return; case WEEK_OF_YEAR: case WEEK_OF_MONTH: case DAY_OF_WEEK_IN_MONTH: delta *= ONE_WEEK; break; case AM_PM: delta *= 12 * ONE_HOUR; break; case DAY_OF_MONTH: case DAY_OF_YEAR: case DAY_OF_WEEK: case DOW_LOCAL: case JULIAN_DAY: delta *= ONE_DAY; break; case HOUR_OF_DAY: case HOUR: delta *= ONE_HOUR; keepWallTimeInvariant = false; break; case MINUTE: delta *= ONE_MINUTE; keepWallTimeInvariant = false; break; case SECOND: delta *= ONE_SECOND; keepWallTimeInvariant = false; break; case MILLISECOND: case MILLISECONDS_IN_DAY: keepWallTimeInvariant = false; break; default: throw new IllegalArgumentException("Calendar.add(" + fieldName(field) + ") not supported"); } // In order to keep the wall time invariant (for fields where this is // appropriate), check the combined DST & ZONE offset before and // after the add() operation. If it changes, then adjust the millis // to compensate. int prevOffset = 0; int prevWallTime = 0; if (keepWallTimeInvariant) { prevOffset = get(DST_OFFSET) + get(ZONE_OFFSET); prevWallTime = get(MILLISECONDS_IN_DAY); } setTimeInMillis(getTimeInMillis() + delta); if (keepWallTimeInvariant) { int newWallTime = get(MILLISECONDS_IN_DAY); if (newWallTime != prevWallTime) { // There is at least one zone transition between the base // time and the result time. As the result, wall time has // changed. long t = internalGetTimeInMillis(); int newOffset = get(DST_OFFSET) + get(ZONE_OFFSET); if (newOffset != prevOffset) { // When the difference of the previous UTC offset and // the new UTC offset exceeds 1 full day, we do not want // to roll over/back the date. For now, this only happens // in Samoa (Pacific/Apia) on Dec 30, 2011. See ticket:9452. long adjAmount = (prevOffset - newOffset) % ONE_DAY; if (adjAmount != 0) { setTimeInMillis(t + adjAmount); newWallTime = get(MILLISECONDS_IN_DAY); } if (newWallTime != prevWallTime) { // The result wall time or adjusted wall time was shifted because // the target wall time does not exist on the result date. switch (skippedWallTime) { case WALLTIME_FIRST: if (adjAmount > 0) { setTimeInMillis(t); } break; case WALLTIME_LAST: if (adjAmount < 0) { setTimeInMillis(t); } break; case WALLTIME_NEXT_VALID: long tmpT = adjAmount > 0 ? internalGetTimeInMillis() : t; Long immediatePrevTrans = getImmediatePreviousZoneTransition(tmpT); if (immediatePrevTrans != null) { setTimeInMillis(immediatePrevTrans); } else { throw new RuntimeException("Could not locate a time zone transition before " + tmpT); } break; } } } } } } /** * Returns the name of this calendar in the language of the given locale. * @stable ICU 2.0 */ public String getDisplayName(Locale loc) { return this.getClass().getName(); } /** * Returns the name of this calendar in the language of the given locale. * @stable ICU 3.2 */ public String getDisplayName(ULocale loc) { return this.getClass().getName(); } /** * Compares the times (in millis) represented by two * 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. * @stable ICU 3.4 */ @Override 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}. * @stable ICU 2.0 */ 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}. * @stable ICU 3.2 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 4.2 */ 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 * @stable ICU 2.0 */ 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 * @draft ICU 3.2 (retain) * @provisional This API might change or be removed in a future release. */ 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 PATTERN_CACHE = new SimpleCache<>(); // final fallback patterns private static final String[] DEFAULT_PATTERNS = { "HH:mm:ss z", "HH:mm:ss z", "HH:mm:ss", "HH:mm", "EEEE, yyyy MMMM dd", "yyyy MMMM d", "yyyy MMM d", "yy/MM/dd", "{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}" }; static private DateFormat formatHelper(Calendar cal, ULocale loc, int dateStyle, int timeStyle) { if (timeStyle < DateFormat.NONE || timeStyle > DateFormat.SHORT) { throw new IllegalArgumentException("Illegal time style " + timeStyle); } if (dateStyle < DateFormat.NONE || dateStyle > DateFormat.SHORT) { throw new IllegalArgumentException("Illegal date style " + dateStyle); } PatternData patternData = PatternData.make(cal, loc); String override = null; // Resolve a pattern for the date/time style String pattern = null; if ((timeStyle >= 0) && (dateStyle >= 0)) { pattern = SimpleFormatterImpl.formatRawPattern( patternData.getDateTimePattern(dateStyle), 2, 2, patternData.patterns[timeStyle], patternData.patterns[dateStyle + 4]); // Might need to merge the overrides from the date and time into a single // override string TODO: Right now we are forcing the date's override into the // time style. if ( patternData.overrides != null ) { String dateOverride = patternData.overrides[dateStyle + 4]; String timeOverride = patternData.overrides[timeStyle]; override = mergeOverrideStrings( patternData.patterns[dateStyle+4], patternData.patterns[timeStyle], dateOverride, timeOverride); } } else if (timeStyle >= 0) { pattern = patternData.patterns[timeStyle]; if ( patternData.overrides != null ) { override = patternData.overrides[timeStyle]; } } else if (dateStyle >= 0) { pattern = patternData.patterns[dateStyle + 4]; if ( patternData.overrides != null ) { override = patternData.overrides[dateStyle + 4]; } } else { throw new IllegalArgumentException("No date or time style specified"); } DateFormat result = cal.handleGetDateFormat(pattern, override, loc); result.setCalendar(cal); return result; } static class PatternData { // TODO make this even more object oriented private String[] patterns; private String[] overrides; public PatternData(String[] patterns, String[] overrides) { this.patterns = patterns; this.overrides = overrides; } private String getDateTimePattern(int dateStyle) { int glueIndex = 8; if (patterns.length >= 13) { glueIndex += (dateStyle + 1); } final String dateTimePattern = patterns[glueIndex]; return dateTimePattern; } private static PatternData make(Calendar cal, ULocale loc) { // First, try to get a pattern from PATTERN_CACHE String calType = cal.getType(); String key = loc.getBaseName() + "+" + calType; PatternData patternData = PATTERN_CACHE.get(key); if (patternData == null) { // Cache missed. Get one from bundle try { patternData = getPatternData(loc, calType); } catch (MissingResourceException e) { patternData = new PatternData(DEFAULT_PATTERNS, null); } PATTERN_CACHE.put(key, patternData); } return patternData; } } /** * Retrieves the DateTime patterns and overrides from the resource bundle and generates a * new PatternData object. * @param locale Locale to retrieve. * @param calType Calendar type to retrieve. If not found will fallback to gregorian. * @return PatternData object for this locale and calendarType. */ private static PatternData getPatternData(ULocale locale, String calType) { ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale); ICUResourceBundle dtPatternsRb = rb.findWithFallback("calendar/" + calType + "/DateTimePatterns"); if (dtPatternsRb == null) { dtPatternsRb = rb.getWithFallback("calendar/gregorian/DateTimePatterns"); } int patternsSize = dtPatternsRb.getSize(); String[] dateTimePatterns = new String[patternsSize]; String[] dateTimePatternsOverrides = new String[patternsSize]; for (int i = 0; i < patternsSize; i++) { ICUResourceBundle concatenationPatternRb = (ICUResourceBundle) dtPatternsRb.get(i); switch (concatenationPatternRb.getType()) { case UResourceBundle.STRING: dateTimePatterns[i] = concatenationPatternRb.getString(); break; case UResourceBundle.ARRAY: dateTimePatterns[i] = concatenationPatternRb.getString(0); dateTimePatternsOverrides[i] = concatenationPatternRb.getString(1); break; } } return new PatternData(dateTimePatterns, dateTimePatternsOverrides); } /** * @internal * @deprecated This API is ICU internal only. */ @Deprecated public static String getDateTimePattern(Calendar cal, ULocale uLocale, int dateStyle) { PatternData patternData = PatternData.make(cal, uLocale); return patternData.getDateTimePattern(dateStyle); } private static String mergeOverrideStrings( String datePattern, String timePattern, String dateOverride, String timeOverride ) { if ( dateOverride == null && timeOverride == null ) { return null; } if ( dateOverride == null ) { return expandOverride(timePattern,timeOverride); } if ( timeOverride == null ) { return expandOverride(datePattern,dateOverride); } if ( dateOverride.equals(timeOverride) ) { return dateOverride; } return (expandOverride(datePattern,dateOverride)+";"+ expandOverride(timePattern,timeOverride)); } private static final char QUOTE = '\''; private static String expandOverride(String pattern, String override) { if (override.indexOf('=') >= 0) { return override; } boolean inQuotes = false; char prevChar = ' '; StringBuilder result = new StringBuilder(); StringCharacterIterator it = new StringCharacterIterator(pattern); for (char c = it.first(); c!= StringCharacterIterator.DONE; c = it.next()) { if ( c == QUOTE ) { inQuotes = !inQuotes; prevChar = c; continue; } if ( !inQuotes && c != prevChar ) { if (result.length() > 0) { result.append(";"); } result.append(c); result.append("="); result.append(override); } prevChar = c; } return result.toString(); } /** * An instance of FormatConfiguration represents calendar specific * date format configuration and used for calling the ICU private * SimpleDateFormat factory method. * * @internal * @deprecated This API is ICU internal only. */ @Deprecated public static class FormatConfiguration { private String pattern; private String override; private DateFormatSymbols formatData; private Calendar cal; private ULocale loc; // Only Calendar can instantiate private FormatConfiguration() { } /** * Returns the pattern string * @return the format pattern string * @internal * @deprecated This API is ICU internal only. */ @Deprecated public String getPatternString() { return pattern; } /** * @internal * @deprecated This API is ICU internal only. */ @Deprecated public String getOverrideString() { return override; } /** * Returns the calendar * @return the calendar * @internal * @deprecated This API is ICU internal only. */ @Deprecated public Calendar getCalendar() { return cal; } /** * Returns the locale * @return the locale * @internal * @deprecated This API is ICU internal only. */ @Deprecated public ULocale getLocale() { return loc; } /** * Returns the format symbols * @return the format symbols * @internal * @deprecated This API is ICU internal only. */ @Deprecated public DateFormatSymbols getDateFormatSymbols() { return formatData; } } //------------------------------------------------------------------------- // Protected utility methods for use by subclasses. These are very handy // for implementing add, roll, and computeFields. //------------------------------------------------------------------------- /** * Adjust the specified field so that it is within * the allowable range for the date to which this calendar is set. * For example, in a Gregorian calendar pinning the {@link #DAY_OF_MONTH DAY_OF_MONTH} * field for a calendar set to April 31 would cause it to be set * to April 30. *

* Subclassing: *
* This utility method is intended for use by subclasses that need to implement * their own overrides of {@link #roll roll} and {@link #add add}. *

* Note: * 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. *

* @param field The calendar field whose value should be pinned. * * @see #getActualMinimum * @see #getActualMaximum * @stable ICU 2.0 */ 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 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. *

* 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 * 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. * @stable ICU 2.0 */ 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. *

* Subclassing: *
* 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. *

* @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 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. * @stable ICU 2.0 */ 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. * *

As a side effect of this call, this calendar is advanced * toward 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. * *

Usage: To use this method, call it first with the largest * field of interest, then with progressively smaller fields. For * example: * *

     * 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. * *

Note: 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ public void setLenient(boolean lenient) { this.lenient = lenient; } /** * Tell whether date/time interpretation is to be lenient. * @stable ICU 2.0 */ 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 * * @stable ICU 49 */ 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 * * @stable ICU 49 */ 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. *

* 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 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 * * @stable ICU 49 */ 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 * * @stable ICU 49 */ 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}. * @stable ICU 2.0 */ 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}. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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)} */ @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)} */ @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() * @stable ICU 2.0 */ 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) * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ @Override 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. * @stable ICU 2.0 */ @Override 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; i { /* (non-Javadoc) * @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object) */ @Override protected WeekData createInstance(String key, String data) { return getWeekDataForRegionInternal(key); } } private static final WeekDataCache WEEK_DATA_CACHE = new WeekDataCache(); /* * Set this calendar to contain week and weekend data for the given region. */ private void setWeekData(String region) { if (region == null) { region = "001"; } WeekData wdata = WEEK_DATA_CACHE.getInstance(region, region); setWeekData(wdata); } /** * Recompute the time and update the status fields isTimeSet * and areFieldsSet. Callers should check isTimeSet and only * call this method if isTimeSet is false. */ private void updateTime() { computeTime(); // If we are lenient, we need to recompute the fields to normalize // the values. Also, if we haven't set all the fields yet (i.e., // in a newly-created object), we need to fill in the fields. [LIU] if (isLenient() || !areAllFieldsSet) areFieldsSet = false; isTimeSet = true; areFieldsVirtuallySet = false; } /** * Save the state of this object to a stream (i.e., serialize it). */ private void writeObject(ObjectOutputStream stream) throws IOException { // Try to compute the time correctly, for the future (stream // version 2) in which we don't write out fields[] or isSet[]. if (!isTimeSet) { try { updateTime(); } catch (IllegalArgumentException e) {} } // Write out the 1.1 FCS object. stream.defaultWriteObject(); } /** * Reconstitute this object from a stream (i.e., deserialize it). */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); initInternal(); isTimeSet = true; areFieldsSet = areAllFieldsSet = false; areFieldsVirtuallySet = true; // cause fields to be recalculated if requested. nextStamp = MINIMUM_USER_STAMP; } //---------------------------------------------------------------------- // Time -> Fields //---------------------------------------------------------------------- /** * Converts the current millisecond time value time 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 * @stable ICU 2.0 */ 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; i>= 1; } // We used to check for and correct extreme millis values (near // Long.MIN_VALUE or Long.MAX_VALUE) here. Such values would cause // overflows from positive to negative (or vice versa) and had to // be manually tweaked. We no longer need to do this because we // have limited the range of supported dates to those that have a // Julian day that fits into an int. This allows us to implement a // JULIAN_DAY field and also removes some inelegant code. - Liu // 11/6/00 long days = floorDivide(localMillis, ONE_DAY); fields[JULIAN_DAY] = (int) days + EPOCH_JULIAN_DAY; computeGregorianAndDOWFields(fields[JULIAN_DAY]); // Call framework method to have subclass compute its fields. // These must include, at a minimum, MONTH, DAY_OF_MONTH, // EXTENDED_YEAR, YEAR, DAY_OF_YEAR. This method will call internalSet(), // which will update stamp[]. handleComputeFields(fields[JULIAN_DAY]); // Compute week-related fields, based on the subclass-computed // fields computed by handleComputeFields(). computeWeekFields(); // Compute time-related fields. These are indepent of the date and // of the subclass algorithm. They depend only on the local zone // wall milliseconds in day. int millisInDay = (int) (localMillis - (days * ONE_DAY)); fields[MILLISECONDS_IN_DAY] = millisInDay; fields[MILLISECOND] = millisInDay % 1000; millisInDay /= 1000; fields[SECOND] = millisInDay % 60; millisInDay /= 60; fields[MINUTE] = millisInDay % 60; millisInDay /= 60; fields[HOUR_OF_DAY] = millisInDay; fields[AM_PM] = millisInDay / 12; // Assume AM == 0 fields[HOUR] = millisInDay % 12; fields[ZONE_OFFSET] = offsets[0]; fields[DST_OFFSET] = offsets[1]; } /** * Compute the Gregorian calendar year, month, and day of month from * the given Julian day. These values are not stored in fields, but in * member variables gregorianXxx. Also compute the DAY_OF_WEEK and * DOW_LOCAL fields. */ private final void computeGregorianAndDOWFields(int julianDay) { computeGregorianFields(julianDay); // Compute day of week: JD 0 = Monday int dow = fields[DAY_OF_WEEK] = julianDayToDayOfWeek(julianDay); // Calculate 1-based localized day of week int dowLocal = dow - getFirstDayOfWeek() + 1; if (dowLocal < 1) { dowLocal += 7; } fields[DOW_LOCAL] = dowLocal; } /** * Compute the Gregorian calendar year, month, and day of month from the * Julian day. These values are not stored in fields, but in member * variables gregorianXxx. They are used for time zone computations and by * subclasses that are Gregorian derivatives. Subclasses may call this * method to perform a Gregorian calendar millis->fields computation. * To perform a Gregorian calendar fields->millis computation, call * computeGregorianMonthStart(). * @see #computeGregorianMonthStart * @stable ICU 2.0 */ protected final void computeGregorianFields(int julianDay) { int year, month, dayOfMonth, dayOfYear; // The Gregorian epoch day is zero for Monday January 1, year 1. long gregorianEpochDay = julianDay - JAN_1_1_JULIAN_DAY; // Here we convert from the day number to the multiple radix // representation. We use 400-year, 100-year, and 4-year cycles. // For example, the 4-year cycle has 4 years + 1 leap day; giving // 1461 == 365*4 + 1 days. int[] rem = new int[1]; int n400 = floorDivide(gregorianEpochDay, 146097, rem); // 400-year cycle length int n100 = floorDivide(rem[0], 36524, rem); // 100-year cycle length int n4 = floorDivide(rem[0], 1461, rem); // 4-year cycle length int n1 = floorDivide(rem[0], 365, rem); year = 400*n400 + 100*n100 + 4*n4 + n1; dayOfYear = rem[0]; // zero-based day of year if (n100 == 4 || n1 == 4) { dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle } else { ++year; } boolean isLeap = ((year&0x3) == 0) && // equiv. to (year%4 == 0) (year%100 != 0 || year%400 == 0); int correction = 0; int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1 if (dayOfYear >= march1) correction = isLeap ? 1 : 2; month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month dayOfMonth = dayOfYear - GREGORIAN_MONTH_COUNT[month][isLeap?3:2] + 1; // one-based DOM gregorianYear = year; gregorianMonth = month; // 0-based already gregorianDayOfMonth = dayOfMonth; // 1-based already gregorianDayOfYear = dayOfYear + 1; // Convert from 0-based to 1-based } /** * Compute the fields WEEK_OF_YEAR, YEAR_WOY, WEEK_OF_MONTH, * DAY_OF_WEEK_IN_MONTH, and DOW_LOCAL from EXTENDED_YEAR, YEAR, * DAY_OF_WEEK, and DAY_OF_YEAR. The latter fields are computed by the * subclass based on the calendar system. * *

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 * @stable ICU 2.0 */ 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 REMAP_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. * *

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. * @stable ICU 2.0 */ protected int resolveFields(int[][][] precedenceTable) { int bestField = -1; int tempBestField; for (int g=0; g=RESOLVE_REMAP)?1:0; i bestStamp) { tempBestField = line[0]; // First field refers to entire line if (tempBestField >= RESOLVE_REMAP) { tempBestField &= (RESOLVE_REMAP-1); // This check is needed to resolve some issues with UCAL_YEAR precedence mapping if (tempBestField != DATE || (stamp[WEEK_OF_MONTH] < stamp[tempBestField])) { bestField = tempBestField; } } else { bestField = tempBestField; } if (bestField == tempBestField) { bestStamp = lineStamp; } } } } return (bestField>=RESOLVE_REMAP)?(bestField&(RESOLVE_REMAP-1)):bestField; } /** * Returns the newest stamp of a given range of fields. * @stable ICU 2.0 */ protected int newestStamp(int first, int last, int bestStampSoFar) { int bestStamp = bestStampSoFar; for (int i=first; i<=last; ++i) { if (stamp[i] > bestStamp) { bestStamp = stamp[i]; } } return bestStamp; } /** * Returns the timestamp of a field. * @stable ICU 2.0 */ protected final int getStamp(int field) { return stamp[field]; } /** * Returns the field that is newer, either defaultField, or * alternateField. If neither is newer or neither is set, return defaultField. * @stable ICU 2.0 */ protected int newerField(int defaultField, int alternateField) { if (stamp[alternateField] > stamp[defaultField]) { return alternateField; } return defaultField; } /** * Ensure that each field is within its valid range by calling {@link * #validateField(int)} on each field that has been set. This method * should only be called if this calendar is not lenient. * @see #isLenient * @see #validateField(int) * @stable ICU 2.0 */ protected void validateFields() { for (int field = 0; field < fields.length; field++) { if (stamp[field] >= MINIMUM_USER_STAMP) { validateField(field); } } } /** * Validate a single field of this calendar. Subclasses should * override this method to validate any calendar-specific fields. * Generic fields can be handled by * Calendar.validateField(). * @see #validateField(int, int, int) * @stable ICU 2.0 */ 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)}. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ protected void computeTime() { if (!isLenient()) { validateFields(); } // Compute the Julian day int julianDay = computeJulianDay(); long millis = julianDayToMillis(julianDay); long 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 { int hour = Math.abs(internalGet(HOUR_OF_DAY)); hour = Math.max(hour, Math.abs(internalGet(HOUR))); // if hour field value is greater than 596, then the // milliseconds value exceeds integer range, hence // using a conservative estimate of 548, we invoke // the long return version of the compute millis method if // the hour value exceeds 548 if (hour > MAX_HOURS) { millisInDay = computeMillisInDayLong(); } 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. * @deprecated ICU 60 */ @Deprecated 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; } /** * Compute the milliseconds in the day from the fields. The standard * value range is from 0 to 23:59:59.999 inclusive. This value * reflects local zone wall time. * @internal * @deprecated This API is ICU internal only. */ @Deprecated protected long computeMillisInDayLong() { // Do the time portion of the conversion. long 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 * @deprecated ICU 60 */ @Deprecated 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]; } /** * 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 * @return total zone offset (raw + DST) for the given moment * @internal * @deprecated This API is ICU internal only. */ @Deprecated protected int computeZoneOffset(long millis, long 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @draft ICU 3.6 (retain) * @provisional This API might change or be removed in a future release. * @see #MONTH */ 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 * @draft ICU 3.6 (retain) * @provisional This API might change or be removed in a future release. * @see #DAY_OF_MONTH */ protected int getDefaultDayInMonth(int extendedYear, int month) { return 1; } /** * Subclasses may override this. This method calls * handleGetMonthLength() to obtain the calendar-specific month * length. * @stable ICU 2.0 */ 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 && newerField(YEAR_WOY, YEAR) == YEAR_WOY) { // 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); } 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 * @stable ICU 2.0 */ 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, of the Gregorian calendar. if (month != 0) { julianDay += GREGORIAN_MONTH_COUNT[month][isLeap?3:2]; } return julianDay; } //---------------------------------------------------------------------- // Subclass API // For subclasses to override //---------------------------------------------------------------------- // (The following method is not called because all existing subclasses // override it. 2003-06-11 ICU 2.6 Alan) ///CLOVER:OFF /** * Subclasses may override this method to compute several fields * specific to each calendar system. These are: * *

  • ERA *
  • YEAR *
  • MONTH *
  • DAY_OF_MONTH *
  • DAY_OF_YEAR *
  • EXTENDED_YEAR
* * 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. * *

In addition, subclasses should compute any subclass-specific * fields, that is, fields from BASE_FIELD_COUNT to * getFieldCount() - 1. * *

The default implementation in Calendar implements * a pure proleptic Gregorian calendar. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ protected final int getGregorianYear() { return gregorianYear; } /** * Returns the month (0-based) on the Gregorian calendar as computed by * computeGregorianFields(). * @see #computeGregorianFields * @stable ICU 2.0 */ protected final int getGregorianMonth() { return gregorianMonth; } /** * Returns the day of year (1-based) on the Gregorian calendar as * computed by computeGregorianFields(). * @see #computeGregorianFields * @stable ICU 2.0 */ protected final int getGregorianDayOfYear() { return gregorianDayOfYear; } /** * Returns the day of month (1-based) on the Gregorian calendar as * computed by computeGregorianFields(). * @see #computeGregorianFields * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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 * @stable ICU 2.0 */ 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. *

* Unlike the built-in division, this is mathematically well-behaved. * E.g., -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. * @stable ICU 2.0 */ 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. *

* Unlike the built-in division, this is mathematically well-behaved. * E.g., -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. * @stable ICU 2.0 */ 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. *

* Unlike the built-in division, this is mathematically well-behaved. * E.g., -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. * @stable ICU 2.0 */ 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. *

* Unlike the built-in division, this is mathematically well-behaved. * E.g., -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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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. * @stable ICU 2.0 */ 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". * *

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 * @stable ICU 3.8 */ 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 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. * @internal * @deprecated This API is ICU internal only. */ @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). * *

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 * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}. * @return the information specified by type, or null if * this object was not constructed from locale data. * @see com.ibm.icu.util.ULocale * @see com.ibm.icu.util.ULocale#VALID_LOCALE * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE * @draft ICU 2.8 (retain) * @provisional This API might change or be removed in a future release. */ 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 com.ibm.icu.util.ULocale * @see com.ibm.icu.util.ULocale#VALID_LOCALE * @see com.ibm.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 com.ibm.icu.util.ULocale */ private ULocale validLocale; /** * The locale containing data used to construct this object, or * null. * @see com.ibm.icu.util.ULocale */ private ULocale actualLocale; // -------- END ULocale boilerplate -------- }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy