src/share/classes/java/util/Calendar.java
Print this page
rev 5615 : 6336885: RFE: Locale Data Deployment Enhancements
4609153: Provide locale data for Indic locales
5104387: Support for gl_ES locale (galician language)
6337471: desktop/system locale preferences support
7056139: (cal) SPI support for locale-dependent Calendar parameters
7058206: Provide CalendarData SPI for week params and display field value names
7073852: Support multiple scripts for digits and decimal symbols per locale
7079560: [Fmt-Da] Context dependent month names support in SimpleDateFormat
7171324: getAvailableLocales() of locale sensitive services should return the actual availability of locales
7151414: (cal) Support calendar type identification
7168528: LocaleServiceProvider needs to be aware of Locale extensions
7171372: (cal) locale's default Calendar should be created if unknown calendar is specified
Summary: JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data (part 1 w/o Jigsaw. by Naoto Sato and Masayoshi Okutsu)
*** 51,63 ****
import java.security.ProtectionDomain;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import sun.util.BuddhistCalendar;
import sun.util.calendar.ZoneInfo;
! import sun.util.resources.LocaleData;
/**
* The <code>Calendar</code> class is an abstract class that provides methods
* for converting between a specific instant in time and a set of {@link
* #fields calendar fields} such as <code>YEAR</code>, <code>MONTH</code>,
--- 51,65 ----
import java.security.ProtectionDomain;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+ import java.util.spi.CalendarDataProvider;
import sun.util.BuddhistCalendar;
+ import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.calendar.ZoneInfo;
! import sun.util.locale.provider.CalendarDataUtility;
/**
* The <code>Calendar</code> class is an abstract class that provides methods
* for converting between a specific instant in time and a set of {@link
* #fields calendar fields} such as <code>YEAR</code>, <code>MONTH</code>,
*** 705,740 ****
/**
* A style specifier for {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating names in all styles, such as
* "January" and "Jan".
*
* @see #SHORT
* @see #LONG
* @since 1.6
*/
public static final int ALL_STYLES = 0;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
! * getDisplayNames} indicating a short name, such as "Jan".
*
* @see #LONG
* @since 1.6
*/
public static final int SHORT = 1;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
! * getDisplayNames} indicating a long name, such as "January".
*
* @see #SHORT
* @since 1.6
*/
public static final int LONG = 2;
// Internal notes:
// Calendar contains two kinds of time representations: current "time" in
// milliseconds, and a set of calendar "fields" representing the current time.
// The two representations are usually in sync, but can get out of sync
// as follows.
--- 707,800 ----
/**
* A style specifier for {@link #getDisplayNames(int, int, Locale)
* getDisplayNames} indicating names in all styles, such as
* "January" and "Jan".
*
+ * @see #SHORT_FORMAT
+ * @see #LONG_FORMAT
+ * @see #SHORT_STANDALONE
+ * @see #LONG_STANDALONE
* @see #SHORT
* @see #LONG
* @since 1.6
*/
public static final int ALL_STYLES = 0;
+ static final int STANDALONE_MASK = 0x8000;
+
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
! * getDisplayNames} equivalent to {@link #SHORT_FORMAT}.
*
+ * @see #SHORT_STANDALONE
* @see #LONG
* @since 1.6
*/
public static final int SHORT = 1;
/**
* A style specifier for {@link #getDisplayName(int, int, Locale)
* getDisplayName} and {@link #getDisplayNames(int, int, Locale)
! * getDisplayNames} equivalent to {@link #LONG_FORMAT}.
*
+ * @see #LONG_STANDALONE
* @see #SHORT
* @since 1.6
*/
public static final int LONG = 2;
+ /**
+ * A style specifier for {@link #getDisplayName(int, int, Locale)
+ * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
+ * getDisplayNames} indicating a short name used for format.
+ *
+ * @see #SHORT_STANDALONE
+ * @see #LONG_FORMAT
+ * @see #LONG_STANDALONE
+ * @since 1.8
+ */
+ public static final int SHORT_FORMAT = 1;
+
+ /**
+ * A style specifier for {@link #getDisplayName(int, int, Locale)
+ * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
+ * getDisplayNames} indicating a long name used for format.
+ *
+ * @see #LONG_STANDALONE
+ * @see #SHORT_FORMAT
+ * @see #SHORT_STANDALONE
+ * @since 1.8
+ */
+ public static final int LONG_FORMAT = 2;
+
+ /**
+ * A style specifier for {@link #getDisplayName(int, int, Locale)
+ * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
+ * getDisplayNames} indicating a short name used independently,
+ * such as a month abbreviation as calendar headers.
+ *
+ * @see #SHORT_FORMAT
+ * @see #LONG_FORMAT
+ * @see #LONG_STANDALONE
+ * @since 1.8
+ */
+ public static final int SHORT_STANDALONE = SHORT | STANDALONE_MASK;
+
+ /**
+ * A style specifier for {@link #getDisplayName(int, int, Locale)
+ * getDisplayName} and {@link #getDisplayNames(int, int, Locale)
+ * getDisplayNames} indicating a long name used independently,
+ * such as a month name as calendar headers.
+ *
+ * @see #LONG_FORMAT
+ * @see #SHORT_FORMAT
+ * @see #SHORT_STANDALONE
+ * @since 1.8
+ */
+ public static final int LONG_STANDALONE = LONG | STANDALONE_MASK;
+
// Internal notes:
// Calendar contains two kinds of time representations: current "time" in
// milliseconds, and a set of calendar "fields" representing the current time.
// The two representations are usually in sync, but can get out of sync
// as follows.
*** 748,757 ****
--- 808,818 ----
* The calendar field values for the currently set time for this calendar.
* This is an array of <code>FIELD_COUNT</code> integers, with index values
* <code>ERA</code> through <code>DST_OFFSET</code>.
* @serial
*/
+ @SuppressWarnings("ProtectedField")
protected int fields[];
/**
* The flags which tell if a specified calendar field for the calendar is set.
* A new object has no fields set. After the first call to a method
*** 758,767 ****
--- 819,829 ----
* which generates the fields, they all remain set after that.
* This is an array of <code>FIELD_COUNT</code> booleans, with index values
* <code>ERA</code> through <code>DST_OFFSET</code>.
* @serial
*/
+ @SuppressWarnings("ProtectedField")
protected boolean isSet[];
/**
* Pseudo-time-stamps which specify when each field was set. There
* are two special values, UNSET and COMPUTED. Values from
*** 773,799 ****
--- 835,864 ----
* The currently set time for this calendar, expressed in milliseconds after
* January 1, 1970, 0:00:00 GMT.
* @see #isTimeSet
* @serial
*/
+ @SuppressWarnings("ProtectedField")
protected long time;
/**
* True if then the value of <code>time</code> is valid.
* The time is made invalid by a change to an item of <code>field[]</code>.
* @see #time
* @serial
*/
+ @SuppressWarnings("ProtectedField")
protected boolean isTimeSet;
/**
* True if <code>fields[]</code> 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
* <code>time</code>.
* @serial
*/
+ @SuppressWarnings("ProtectedField")
protected boolean areFieldsSet;
/**
* True if all fields have been set.
* @serial
*** 908,917 ****
--- 973,983 ----
// Proclaim serialization compatibility with JDK 1.1
static final long serialVersionUID = -1807547505821590642L;
// Mask values for calendar fields
+ @SuppressWarnings("PointlessBitwiseExpression")
final static int ERA_MASK = (1 << ERA);
final static int YEAR_MASK = (1 << YEAR);
final static int MONTH_MASK = (1 << MONTH);
final static int WEEK_OF_YEAR_MASK = (1 << WEEK_OF_YEAR);
final static int WEEK_OF_MONTH_MASK = (1 << WEEK_OF_MONTH);
*** 1016,1046 ****
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
Calendar cal = null;
String caltype = aLocale.getUnicodeLocaleType("ca");
! if (caltype == null) {
! // Calendar type is not specified.
! // If the specified locale is a Thai locale,
! // returns a BuddhistCalendar instance.
! if ("th".equals(aLocale.getLanguage())
! && ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
! } else {
cal = new GregorianCalendar(zone, aLocale);
}
! } else if (caltype.equals("japanese")) {
! cal = new JapaneseImperialCalendar(zone, aLocale);
! } else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
- // Unsupported calendar type.
- // Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
}
!
return cal;
}
/**
* Returns an array of all locales for which the <code>getInstance</code>
--- 1082,1123 ----
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
Calendar cal = null;
+ if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
! if (caltype != null) {
! switch (caltype) {
! case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
! break;
! case "japanese":
! cal = new JapaneseImperialCalendar(zone, aLocale);
! break;
! case "gregory":
cal = new GregorianCalendar(zone, aLocale);
+ break;
}
! }
! }
! if (cal == null) {
! // If no known calendar type is explicitly specified,
! // perform the traditional way to create a Calendar:
! // create a BuddhistCalendar for th_TH locale,
! // a JapaneseImperialCalendar for ja_JP_JP locale, or
! // a GregorianCalendar for any other locales.
! // NOTE: The language, country and variant strings are interned.
! if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
+ } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
+ && aLocale.getCountry() == "JP") {
+ cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
! }
return cal;
}
/**
* Returns an array of all locales for which the <code>getInstance</code>
*** 1391,1404 ****
*
* @param field
* the calendar field for which the string representation
* is returned
* @param style
! * the style applied to the string representation; one of
! * {@link #SHORT} or {@link #LONG}.
* @param locale
* the locale for the string representation
* @return the string representation of the given
* <code>field</code> in the given <code>style</code>, or
* <code>null</code> if no string representation is
* applicable.
* @exception IllegalArgumentException
--- 1468,1483 ----
*
* @param field
* the calendar field for which the string representation
* is returned
* @param style
! * the style applied to the string representation; one of {@link
! * #SHORT_FORMAT} ({@link #SHORT}), {@link #SHORT_STANDALONE},
! * {@link #LONG_FORMAT} ({@link #LONG}) or {@link #LONG_STANDALONE}.
* @param locale
* the locale for the string representation
+ * (any calendar types specified by {@code locale} are ignored)
* @return the string representation of the given
* <code>field</code> in the given <code>style</code>, or
* <code>null</code> if no string representation is
* applicable.
* @exception IllegalArgumentException
*** 1408,1422 ****
* @exception NullPointerException
* if <code>locale</code> is null
* @since 1.6
*/
public String getDisplayName(int field, int style, Locale locale) {
! if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
String[] strings = getFieldStrings(field, style, symbols);
if (strings != null) {
int fieldValue = get(field);
if (fieldValue < strings.length) {
--- 1487,1508 ----
* @exception NullPointerException
* if <code>locale</code> is null
* @since 1.6
*/
public String getDisplayName(int field, int style, Locale locale) {
! if (!checkDisplayNameParams(field, style, SHORT, LONG, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
+ // the standalone styles are supported only through CalendarDataProviders.
+ if (isStandaloneStyle(style)) {
+ return CalendarDataUtility.retrieveFieldValueName(getCalendarType(),
+ field, get(field),
+ style, locale);
+ }
+
DateFormatSymbols symbols = DateFormatSymbols.getInstance(locale);
String[] strings = getFieldStrings(field, style, symbols);
if (strings != null) {
int fieldValue = get(field);
if (fieldValue < strings.length) {
*** 1451,1462 ****
* and {@link DateFormatSymbols#getMonths()}.
*
* @param field
* the calendar field for which the display names are returned
* @param style
! * the style applied to the display names; one of {@link
! * #SHORT}, {@link #LONG}, or {@link #ALL_STYLES}.
* @param locale
* the locale for the display names
* @return a <code>Map</code> containing all display names in
* <code>style</code> and <code>locale</code> and their
* field values, or <code>null</code> if no display names
--- 1537,1549 ----
* and {@link DateFormatSymbols#getMonths()}.
*
* @param field
* the calendar field for which the display names are returned
* @param style
! * the style applied to the string representation; one of {@link
! * #SHORT_FORMAT} ({@link #SHORT}), {@link #SHORT_STANDALONE},
! * {@link #LONG_FORMAT} ({@link #LONG}) or {@link #LONG_STANDALONE}.
* @param locale
* the locale for the display names
* @return a <code>Map</code> containing all display names in
* <code>style</code> and <code>locale</code> and their
* field values, or <code>null</code> if no display names
*** 1472,1498 ****
public Map<String, Integer> getDisplayNames(int field, int style, Locale locale) {
if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
!
! // ALL_STYLES
! if (style == ALL_STYLES) {
! Map<String,Integer> shortNames = getDisplayNamesImpl(field, SHORT, locale);
! if (field == ERA || field == AM_PM) {
! return shortNames;
}
- Map<String,Integer> longNames = getDisplayNamesImpl(field, LONG, locale);
- if (shortNames == null) {
- return longNames;
- }
- if (longNames != null) {
- shortNames.putAll(longNames);
- }
- return shortNames;
- }
-
// SHORT or LONG
return getDisplayNamesImpl(field, style, locale);
}
private Map<String,Integer> getDisplayNamesImpl(int field, int style, Locale locale) {
--- 1559,1571 ----
public Map<String, Integer> getDisplayNames(int field, int style, Locale locale) {
if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
ERA_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
return null;
}
! if (style == ALL_STYLES || isStandaloneStyle(style)) {
! return CalendarDataUtility.retrieveFieldValueNames(getCalendarType(), field, style, locale);
}
// SHORT or LONG
return getDisplayNamesImpl(field, style, locale);
}
private Map<String,Integer> getDisplayNamesImpl(int field, int style, Locale locale) {
*** 1511,1543 ****
return null;
}
boolean checkDisplayNameParams(int field, int style, int minStyle, int maxStyle,
Locale locale, int fieldMask) {
if (field < 0 || field >= fields.length ||
! style < minStyle || style > maxStyle) {
throw new IllegalArgumentException();
}
if (locale == null) {
throw new NullPointerException();
}
return isFieldSet(fieldMask, field);
}
private String[] getFieldStrings(int field, int style, DateFormatSymbols symbols) {
String[] strings = null;
switch (field) {
case ERA:
strings = symbols.getEras();
break;
case MONTH:
! strings = (style == LONG) ? symbols.getMonths() : symbols.getShortMonths();
break;
case DAY_OF_WEEK:
! strings = (style == LONG) ? symbols.getWeekdays() : symbols.getShortWeekdays();
break;
case AM_PM:
strings = symbols.getAmPmStrings();
break;
--- 1584,1618 ----
return null;
}
boolean checkDisplayNameParams(int field, int style, int minStyle, int maxStyle,
Locale locale, int fieldMask) {
+ int baseStyle = getBaseStyle(style); // Ignore the standalone mask
if (field < 0 || field >= fields.length ||
! baseStyle < minStyle || baseStyle > maxStyle) {
throw new IllegalArgumentException();
}
if (locale == null) {
throw new NullPointerException();
}
return isFieldSet(fieldMask, field);
}
private String[] getFieldStrings(int field, int style, DateFormatSymbols symbols) {
+ int baseStyle = getBaseStyle(style); // ignore the standalone mask
String[] strings = null;
switch (field) {
case ERA:
strings = symbols.getEras();
break;
case MONTH:
! strings = (baseStyle == LONG) ? symbols.getMonths() : symbols.getShortMonths();
break;
case DAY_OF_WEEK:
! strings = (baseStyle == LONG) ? symbols.getWeekdays() : symbols.getShortWeekdays();
break;
case AM_PM:
strings = symbols.getAmPmStrings();
break;
*** 1552,1563 ****
* calendar field values. Then, the {@link #computeFields()} method is
* called to calculate all calendar field values.
*/
protected void complete()
{
! if (!isTimeSet)
updateTime();
if (!areFieldsSet || !areAllFieldsSet) {
computeFields(); // fills in unset fields
areAllFieldsSet = areFieldsSet = true;
}
}
--- 1627,1639 ----
* calendar field values. Then, the {@link #computeFields()} method is
* called to calculate all calendar field values.
*/
protected void complete()
{
! if (!isTimeSet) {
updateTime();
+ }
if (!areFieldsSet || !areAllFieldsSet) {
computeFields(); // fills in unset fields
areAllFieldsSet = areFieldsSet = true;
}
}
*** 1687,1697 ****
/**
* Returns whether the specified <code>field</code> is on in the
* <code>fieldMask</code>.
*/
! static final boolean isFieldSet(int fieldMask, int field) {
return (fieldMask & (1 << field)) != 0;
}
/**
* Returns a field mask indicating which calendar field values
--- 1763,1773 ----
/**
* Returns whether the specified <code>field</code> is on in the
* <code>fieldMask</code>.
*/
! static boolean isFieldSet(int fieldMask, int field) {
return (fieldMask & (1 << field)) != 0;
}
/**
* Returns a field mask indicating which calendar field values
*** 1863,1886 ****
}
return fieldMask;
}
/**
* Returns the pseudo-time-stamp for two fields, given their
* individual pseudo-time-stamps. If either of the fields
* is unset, then the aggregate is unset. Otherwise, the
* aggregate is the later of the two stamps.
*/
! private static final int aggregateStamp(int stamp_a, int stamp_b) {
if (stamp_a == UNSET || stamp_b == UNSET) {
return UNSET;
}
return (stamp_a > stamp_b) ? stamp_a : stamp_b;
}
/**
* Compares this <code>Calendar</code> to the specified
* <code>Object</code>. The result is <code>true</code> if and only if
* the argument is a <code>Calendar</code> object of the same calendar
* system that represents the same time value (millisecond offset from the
* <a href="#Epoch">Epoch</a>) under the same
--- 1939,1991 ----
}
return fieldMask;
}
+ int getBaseStyle(int style) {
+ return style & ~STANDALONE_MASK;
+ }
+
+ boolean isStandaloneStyle(int style) {
+ return (style & STANDALONE_MASK) != 0;
+ }
+
/**
* Returns the pseudo-time-stamp for two fields, given their
* individual pseudo-time-stamps. If either of the fields
* is unset, then the aggregate is unset. Otherwise, the
* aggregate is the later of the two stamps.
*/
! private static int aggregateStamp(int stamp_a, int stamp_b) {
if (stamp_a == UNSET || stamp_b == UNSET) {
return UNSET;
}
return (stamp_a > stamp_b) ? stamp_a : stamp_b;
}
/**
+ * Returns the calendar type of this {@code Calendar}. Calendar types are
+ * defined by the <em>Unicode Locale Data Markup Language (LDML)</em>
+ * specification.
+ *
+ * <p>The default implementation of this method returns the class name of
+ * this {@code Calendar} instance. Any subclasses that implement
+ * LDML-defined calendar systems should override this method to return
+ * appropriate calendar types.
+ *
+ * @return the LDML-defined calendar type or the class name of this
+ * {@code Calendar} instance
+ * @since 1.8
+ * @see <a href="Locale.html#def_extensions">Locale extensions</a>
+ * @see Locale.Builder#setLocale(Locale)
+ * @see Locale.Builder#setUnicodeLocaleKeyword(String, String)
+ */
+ public String getCalendarType() {
+ return this.getClass().getName();
+ }
+
+ /**
* Compares this <code>Calendar</code> to the specified
* <code>Object</code>. The result is <code>true</code> if and only if
* the argument is a <code>Calendar</code> object of the same calendar
* system that represents the same time value (millisecond offset from the
* <a href="#Epoch">Epoch</a>) under the same
*** 1898,1910 ****
*
* @param obj the object to compare with.
* @return <code>true</code> if this object is equal to <code>obj</code>;
* <code>false</code> otherwise.
*/
public boolean equals(Object obj) {
! if (this == obj)
return true;
try {
Calendar that = (Calendar)obj;
return compareTo(getMillisOf(that)) == 0 &&
lenient == that.lenient &&
firstDayOfWeek == that.firstDayOfWeek &&
--- 2003,2018 ----
*
* @param obj the object to compare with.
* @return <code>true</code> if this object is equal to <code>obj</code>;
* <code>false</code> otherwise.
*/
+ @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
+ @Override
public boolean equals(Object obj) {
! if (this == obj) {
return true;
+ }
try {
Calendar that = (Calendar)obj;
return compareTo(getMillisOf(that)) == 0 &&
lenient == that.lenient &&
firstDayOfWeek == that.firstDayOfWeek &&
*** 1922,1931 ****
--- 2030,2040 ----
* Returns a hash code for this calendar.
*
* @return a hash code value for this object.
* @since 1.2
*/
+ @Override
public int hashCode() {
// 'otheritems' represents the hash code for the previous versions.
int otheritems = (lenient ? 1 : 0)
| (firstDayOfWeek << 1)
| (minimalDaysInFirstWeek << 4)
*** 1993,2002 ****
--- 2102,2112 ----
* @exception IllegalArgumentException if the time value of the
* specified <code>Calendar</code> object can't be obtained due to
* any invalid calendar values.
* @since 1.5
*/
+ @Override
public int compareTo(Calendar anotherCalendar) {
return compareTo(getMillisOf(anotherCalendar));
}
/**
*** 2466,2477 ****
Calendar work = (Calendar)this.clone();
work.setLenient(true);
// if we're counting weeks, set the day of the week to Sunday. We know the
// last week of a month or year will contain the first day of the week.
! if (field == WEEK_OF_YEAR || field == WEEK_OF_MONTH)
work.set(DAY_OF_WEEK, firstDayOfWeek);
// now try each value from getLeastMaximum() to getMaximum() one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
int result = fieldValue;
--- 2576,2588 ----
Calendar work = (Calendar)this.clone();
work.setLenient(true);
// if we're counting weeks, set the day of the week to Sunday. We know the
// last week of a month or year will contain the first day of the week.
! if (field == WEEK_OF_YEAR || field == WEEK_OF_MONTH) {
work.set(DAY_OF_WEEK, firstDayOfWeek);
+ }
// now try each value from getLeastMaximum() to getMaximum() one by one until
// we get a value that normalizes to another value. The last value that
// normalizes to itself is the actual maximum for the current date
int result = fieldValue;
*** 2492,2501 ****
--- 2603,2613 ----
/**
* Creates and returns a copy of this object.
*
* @return a copy of this object.
*/
+ @Override
public Object clone()
{
try {
Calendar other = (Calendar) super.clone();
*** 2529,2539 ****
* @param field the calendar field
* @return the calendar field name
* @exception IndexOutOfBoundsException if <code>field</code> is negative,
* equal to or greater then <code>FIELD_COUNT</code>.
*/
! static final String getFieldName(int field) {
return FIELD_NAME[field];
}
/**
* Return a string representation of this calendar. This method
--- 2641,2651 ----
* @param field the calendar field
* @return the calendar field name
* @exception IndexOutOfBoundsException if <code>field</code> is negative,
* equal to or greater then <code>FIELD_COUNT</code>.
*/
! static String getFieldName(int field) {
return FIELD_NAME[field];
}
/**
* Return a string representation of this calendar. This method
*** 2541,2550 ****
--- 2653,2663 ----
* format of the returned string may vary between implementations.
* The returned string may be empty but may not be <code>null</code>.
*
* @return a string representation of this calendar.
*/
+ @Override
public String toString() {
// NOTE: BuddhistCalendar.toString() interprets the string
// produced by this method so that the Gregorian year number
// is substituted by its B.E. year value. It relies on
// "...,YEAR=<year>,..." or "...,YEAR=?,...".
*** 2565,2575 ****
return buffer.toString();
}
// =======================privates===============================
! private static final void appendValue(StringBuilder sb, String item, boolean valid, long value) {
sb.append(item).append('=');
if (valid) {
sb.append(value);
} else {
sb.append('?');
--- 2678,2688 ----
return buffer.toString();
}
// =======================privates===============================
! private static void appendValue(StringBuilder sb, String item, boolean valid, long value) {
sb.append(item).append('=');
if (valid) {
sb.append(value);
} else {
sb.append('?');
*** 2585,2598 ****
private void setWeekCountData(Locale desiredLocale)
{
/* try to get the Locale data from the cache */
int[] data = cachedLocaleData.get(desiredLocale);
if (data == null) { /* cache miss */
! ResourceBundle bundle = LocaleData.getCalendarData(desiredLocale);
data = new int[2];
! data[0] = Integer.parseInt(bundle.getString("firstDayOfWeek"));
! data[1] = Integer.parseInt(bundle.getString("minimalDaysInFirstWeek"));
cachedLocaleData.putIfAbsent(desiredLocale, data);
}
firstDayOfWeek = data[0];
minimalDaysInFirstWeek = data[1];
}
--- 2698,2713 ----
private void setWeekCountData(Locale desiredLocale)
{
/* try to get the Locale data from the cache */
int[] data = cachedLocaleData.get(desiredLocale);
if (data == null) { /* cache miss */
! LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(CalendarDataProvider.class, desiredLocale);
! CalendarDataProvider provider = adapter.getCalendarDataProvider();
data = new int[2];
! data[0] = provider.getFirstDayOfWeek(desiredLocale);
! data[1] = provider.getMinimalDaysInFirstWeek(desiredLocale);
! assert data[0] != 0 && data[1] != 0;
cachedLocaleData.putIfAbsent(desiredLocale, data);
}
firstDayOfWeek = data[0];
minimalDaysInFirstWeek = data[1];
}
*** 2612,2622 ****
private int compareTo(long t) {
long thisTime = getMillisOf(this);
return (thisTime > t) ? 1 : (thisTime == t) ? 0 : -1;
}
! private static final long getMillisOf(Calendar calendar) {
if (calendar.isTimeSet) {
return calendar.time;
}
Calendar cal = (Calendar) calendar.clone();
cal.setLenient(true);
--- 2727,2737 ----
private int compareTo(long t) {
long thisTime = getMillisOf(this);
return (thisTime > t) ? 1 : (thisTime == t) ? 0 : -1;
}
! private static long getMillisOf(Calendar calendar) {
if (calendar.isTimeSet) {
return calendar.time;
}
Calendar cal = (Calendar) calendar.clone();
cal.setLenient(true);
*** 2625,2635 ****
/**
* Adjusts the stamp[] values before nextStamp overflow. nextStamp
* is set to the next stamp value upon the return.
*/
! private final void adjustStamp() {
int max = MINIMUM_USER_STAMP;
int newStamp = MINIMUM_USER_STAMP;
for (;;) {
int min = Integer.MAX_VALUE;
--- 2740,2750 ----
/**
* Adjusts the stamp[] values before nextStamp overflow. nextStamp
* is set to the next stamp value upon the return.
*/
! private void adjustStamp() {
int max = MINIMUM_USER_STAMP;
int newStamp = MINIMUM_USER_STAMP;
for (;;) {
int min = Integer.MAX_VALUE;
*** 2750,2760 ****
--- 2865,2877 ----
perms.add(perm);
INSTANCE = new AccessControlContext(new ProtectionDomain[] {
new ProtectionDomain(null, perms)
});
}
+ private CalendarAccessControlContext() {
}
+ }
/**
* Reconstitutes this object from a stream (i.e., deserialize it).
*/
private void readObject(ObjectInputStream stream)
*** 2769,2794 ****
// fields[], isSet[], isTimeSet, and areFieldsSet may not be
// streamed out anymore. We expect 'time' to be correct.
if (serialVersionOnStream >= 2)
{
isTimeSet = true;
! if (fields == null) fields = new int[FIELD_COUNT];
! if (isSet == null) isSet = new boolean[FIELD_COUNT];
}
else if (serialVersionOnStream >= 0)
{
! for (int i=0; i<FIELD_COUNT; ++i)
stamp[i] = isSet[i] ? COMPUTED : UNSET;
}
serialVersionOnStream = currentSerialVersion;
// If there's a ZoneInfo object, use it for zone.
ZoneInfo zi = null;
try {
zi = AccessController.doPrivileged(
new PrivilegedExceptionAction<ZoneInfo>() {
public ZoneInfo run() throws Exception {
return (ZoneInfo) input.readObject();
}
},
CalendarAccessControlContext.INSTANCE);
--- 2886,2917 ----
// fields[], isSet[], isTimeSet, and areFieldsSet may not be
// streamed out anymore. We expect 'time' to be correct.
if (serialVersionOnStream >= 2)
{
isTimeSet = true;
! if (fields == null) {
! fields = new int[FIELD_COUNT];
}
+ if (isSet == null) {
+ isSet = new boolean[FIELD_COUNT];
+ }
+ }
else if (serialVersionOnStream >= 0)
{
! for (int i=0; i<FIELD_COUNT; ++i) {
stamp[i] = isSet[i] ? COMPUTED : UNSET;
}
+ }
serialVersionOnStream = currentSerialVersion;
// If there's a ZoneInfo object, use it for zone.
ZoneInfo zi = null;
try {
zi = AccessController.doPrivileged(
new PrivilegedExceptionAction<ZoneInfo>() {
+ @Override
public ZoneInfo run() throws Exception {
return (ZoneInfo) input.readObject();
}
},
CalendarAccessControlContext.INSTANCE);