4745761: (cal) RFE: Support builder for constructing Calendar
Reviewed-by: peytoia
This commit is contained in:
parent
6b32c387f1
commit
f9d0dd3d72
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -151,9 +151,9 @@ import sun.util.locale.provider.CalendarDataUtility;
|
||||
* calendar field values to determine the date and time in the
|
||||
* following way.
|
||||
*
|
||||
* <p>If there is any conflict in calendar field values,
|
||||
* <p><a name="resolution">If there is any conflict in calendar field values,
|
||||
* <code>Calendar</code> gives priorities to calendar fields that have been set
|
||||
* more recently. The following are the default combinations of the
|
||||
* more recently.</a> The following are the default combinations of the
|
||||
* calendar fields. The most recent combination, as determined by the
|
||||
* most recently set single field, will be used.
|
||||
*
|
||||
@ -1019,6 +1019,556 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
final static int ZONE_OFFSET_MASK = (1 << ZONE_OFFSET);
|
||||
final static int DST_OFFSET_MASK = (1 << DST_OFFSET);
|
||||
|
||||
/**
|
||||
* {@code Calendar.Builder} is used for creating a {@code Calendar} from
|
||||
* various date-time parameters.
|
||||
*
|
||||
* <p>There are two ways to set a {@code Calendar} to a date-time value. One
|
||||
* is to set the instant parameter to a millisecond offset from the <a
|
||||
* href="Calendar.html#Epoch">Epoch</a>. The other is to set individual
|
||||
* field parameters, such as {@link Calendar#YEAR YEAR}, to their desired
|
||||
* values. These two ways can't be mixed. Trying to set both the instant and
|
||||
* individual fields will cause an {@link IllegalStateException} to be
|
||||
* thrown. However, it is permitted to override previous values of the
|
||||
* instant or field parameters.
|
||||
*
|
||||
* <p>If no enough field parameters are given for determining date and/or
|
||||
* time, calendar specific default values are used when building a
|
||||
* {@code Calendar}. For example, if the {@link Calendar#YEAR YEAR} value
|
||||
* isn't given for the Gregorian calendar, 1970 will be used. If there are
|
||||
* any conflicts among field parameters, the <a
|
||||
* href="Calendar.html#resolution"> resolution rules</a> are applied.
|
||||
* Therefore, the order of field setting matters.
|
||||
*
|
||||
* <p>In addition to the date-time parameters,
|
||||
* the {@linkplain #setLocale(Locale) locale},
|
||||
* {@linkplain #setTimeZone(TimeZone) time zone},
|
||||
* {@linkplain #setWeekDefinition(int, int) week definition}, and
|
||||
* {@linkplain #setLenient(boolean) leniency mode} parameters can be set.
|
||||
*
|
||||
* <p><b>Examples</b>
|
||||
* <p>The following are sample usages. Sample code assumes that the
|
||||
* {@code Calendar} constants are statically imported.
|
||||
*
|
||||
* <p>The following code produces a {@code Calendar} with date 2012-12-31
|
||||
* (Gregorian) because Monday is the first day of a week with the <a
|
||||
* href="GregorianCalendar.html#iso8601_compatible_setting"> ISO 8601
|
||||
* compatible week parameters</a>.
|
||||
* <pre>
|
||||
* Calendar cal = new Calendar.Builder().setCalendarType("iso8601")
|
||||
* .setWeekDate(2013, 1, MONDAY).build();</pre>
|
||||
* <p>The following code produces a Japanese {@code Calendar} with date
|
||||
* 1989-01-08 (Gregorian), assuming that the default {@link Calendar#ERA ERA}
|
||||
* is <em>Heisei</em> that started on that day.
|
||||
* <pre>
|
||||
* Calendar cal = new Calendar.Builder().setCalendarType("japanese")
|
||||
* .setFields(YEAR, 1, DAY_OF_YEAR, 1).build();</pre>
|
||||
*
|
||||
* @since 1.8
|
||||
* @see Calendar#getInstance(TimeZone, Locale)
|
||||
* @see Calendar#fields
|
||||
*/
|
||||
public static class Builder {
|
||||
private static final int NFIELDS = FIELD_COUNT + 1; // +1 for WEEK_YEAR
|
||||
private static final int WEEK_YEAR = FIELD_COUNT;
|
||||
|
||||
private long instant;
|
||||
// Calendar.stamp[] (lower half) and Calendar.fields[] (upper half) combined
|
||||
private int[] fields;
|
||||
// Pseudo timestamp starting from MINIMUM_USER_STAMP.
|
||||
// (COMPUTED is used to indicate that the instant has been set.)
|
||||
private int nextStamp;
|
||||
// maxFieldIndex keeps the max index of fields which have been set.
|
||||
// (WEEK_YEAR is never included.)
|
||||
private int maxFieldIndex;
|
||||
private String type;
|
||||
private TimeZone zone;
|
||||
private boolean lenient = true;
|
||||
private Locale locale;
|
||||
private int firstDayOfWeek, minimalDaysInFirstWeek;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Calendar.Builder}.
|
||||
*/
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instant parameter to the given {@code instant} value that is
|
||||
* a millisecond offset from <a href="Calendar.html#Epoch">the
|
||||
* Epoch</a>.
|
||||
*
|
||||
* @param instant a millisecond offset from the Epoch
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws IllegalStateException if any of the field parameters have
|
||||
* already been set
|
||||
* @see Calendar#setTime(Date)
|
||||
* @see Calendar#setTimeInMillis(long)
|
||||
* @see Calendar#time
|
||||
*/
|
||||
public Builder setInstant(long instant) {
|
||||
if (fields != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.instant = instant;
|
||||
nextStamp = COMPUTED;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instant parameter to the {@code instant} value given by a
|
||||
* {@link Date}. This method is equivalent to a call to
|
||||
* {@link #setInstant(long) setInstant(instant.getTime())}.
|
||||
*
|
||||
* @param instant a {@code Date} representing a millisecond offset from
|
||||
* the Epoch
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws NullPointerException if {@code instant} is {@code null}
|
||||
* @throws IllegalStateException if any of the field parameters have
|
||||
* already been set
|
||||
* @see Calendar#setTime(Date)
|
||||
* @see Calendar#setTimeInMillis(long)
|
||||
* @see Calendar#time
|
||||
*/
|
||||
public Builder setInstant(Date instant) {
|
||||
return setInstant(instant.getTime()); // NPE if instant == null
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code field} parameter to the given {@code value}.
|
||||
* {@code field} is an index to the {@link Calendar#fields}, such as
|
||||
* {@link Calendar#DAY_OF_MONTH DAY_OF_MONTH}. Field value validation is
|
||||
* not performed in this method. Any out of range values are either
|
||||
* normalized in lenient mode or detected as an invalid value in
|
||||
* non-lenient mode when building a {@code Calendar}.
|
||||
*
|
||||
* @param field an index to the {@code Calendar} fields
|
||||
* @param value the field value
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws IllegalArgumentException if {@code field} is invalid
|
||||
* @throws IllegalStateException if the instant value has already been set,
|
||||
* or if fields have been set too many
|
||||
* (approximately {@link Integer#MAX_VALUE}) times.
|
||||
* @see Calendar#set(int, int)
|
||||
*/
|
||||
public Builder set(int field, int value) {
|
||||
// Note: WEEK_YEAR can't be set with this method.
|
||||
if (field < 0 || field >= FIELD_COUNT) {
|
||||
throw new IllegalArgumentException("field is invalid");
|
||||
}
|
||||
if (isInstantSet()) {
|
||||
throw new IllegalStateException("instant has been set");
|
||||
}
|
||||
allocateFields();
|
||||
internalSet(field, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets field parameters to their values given by
|
||||
* {@code fieldValuePairs} that are pairs of a field and its value.
|
||||
* For example,
|
||||
* <pre>
|
||||
* setFeilds(Calendar.YEAR, 2013,
|
||||
* Calendar.MONTH, Calendar.DECEMBER,
|
||||
* Calendar.DAY_OF_MONTH, 23);</pre>
|
||||
* is equivalent to the sequence of the following
|
||||
* {@link #set(int, int) set} calls:
|
||||
* <pre>
|
||||
* set(Calendar.YEAR, 2013)
|
||||
* .set(Calendar.MONTH, Calendar.DECEMBER)
|
||||
* .set(Calendar.DAY_OF_MONTH, 23);</pre>
|
||||
*
|
||||
* @param fieldValuePairs field-value pairs
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws NullPointerException if {@code fieldValuePairs} is {@code null}
|
||||
* @throws IllegalArgumentException if any of fields are invalid,
|
||||
* or if {@code fieldValuePairs.length} is an odd number.
|
||||
* @throws IllegalStateException if the instant value has been set,
|
||||
* or if fields have been set too many (approximately
|
||||
* {@link Integer#MAX_VALUE}) times.
|
||||
*/
|
||||
public Builder setFields(int... fieldValuePairs) {
|
||||
int len = fieldValuePairs.length;
|
||||
if ((len % 2) != 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (isInstantSet()) {
|
||||
throw new IllegalStateException("instant has been set");
|
||||
}
|
||||
if ((nextStamp + len / 2) < 0) {
|
||||
throw new IllegalStateException("stamp counter overflow");
|
||||
}
|
||||
allocateFields();
|
||||
for (int i = 0; i < len; ) {
|
||||
int field = fieldValuePairs[i++];
|
||||
// Note: WEEK_YEAR can't be set with this method.
|
||||
if (field < 0 || field >= FIELD_COUNT) {
|
||||
throw new IllegalArgumentException("field is invalid");
|
||||
}
|
||||
internalSet(field, fieldValuePairs[i++]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date field parameters to the values given by {@code year},
|
||||
* {@code month}, and {@code dayOfMonth}. This method is equivalent to
|
||||
* a call to:
|
||||
* <pre>
|
||||
* setFields(Calendar.YEAR, year,
|
||||
* Calendar.MONTH, month,
|
||||
* Calendar.DAY_OF_MONTH, dayOfMonth);</pre>
|
||||
*
|
||||
* @param year the {@link Calendar#YEAR YEAR} value
|
||||
* @param month the {@link Calendar#MONTH MONTH} value
|
||||
* (the month numbering is <em>0-based</em>).
|
||||
* @param dayOfMonth the {@link Calendar#DAY_OF_MONTH DAY_OF_MONTH} value
|
||||
* @return this {@code Calendar.Builder}
|
||||
*/
|
||||
public Builder setDate(int year, int month, int dayOfMonth) {
|
||||
return setFields(YEAR, year, MONTH, month, DAY_OF_MONTH, dayOfMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time of day field parameters to the values given by
|
||||
* {@code hourOfDay}, {@code minute}, and {@code second}. This method is
|
||||
* equivalent to a call to:
|
||||
* <pre>
|
||||
* setTimeOfDay(hourOfDay, minute, second, 0);</pre>
|
||||
*
|
||||
* @param hourOfDay the {@link Calendar#HOUR_OF_DAY HOUR_OF_DAY} value
|
||||
* (24-hour clock)
|
||||
* @param minute the {@link Calendar#MINUTE MINUTE} value
|
||||
* @param second the {@link Calendar#SECOND SECOND} value
|
||||
* @return this {@code Calendar.Builder}
|
||||
*/
|
||||
public Builder setTimeOfDay(int hourOfDay, int minute, int second) {
|
||||
return setTimeOfDay(hourOfDay, minute, second, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time of day field parameters to the values given by
|
||||
* {@code hourOfDay}, {@code minute}, {@code second}, and
|
||||
* {@code millis}. This method is equivalent to a call to:
|
||||
* <pre>
|
||||
* setFields(Calendar.HOUR_OF_DAY, hourOfDay,
|
||||
* Calendar.MINUTE, minute,
|
||||
* Calendar.SECOND, second,
|
||||
* Calendar.MILLISECOND, millis);</pre>
|
||||
*
|
||||
* @param hourOfDay the {@link Calendar#HOUR_OF_DAY HOUR_OF_DAY} value
|
||||
* (24-hour clock)
|
||||
* @param minute the {@link Calendar#MINUTE MINUTE} value
|
||||
* @param second the {@link Calendar#SECOND SECOND} value
|
||||
* @param millis the {@link Calendar#MILLISECOND MILLISECOND} value
|
||||
* @return this {@code Calendar.Builder}
|
||||
*/
|
||||
public Builder setTimeOfDay(int hourOfDay, int minute, int second, int millis) {
|
||||
return setFields(HOUR_OF_DAY, hourOfDay, MINUTE, minute,
|
||||
SECOND, second, MILLISECOND, millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the week-based date parameters to the values with the given
|
||||
* date specifiers - week year, week of year, and day of week.
|
||||
*
|
||||
* <p>If the specified calendar doesn't support week dates, the
|
||||
* {@link #build() build} method will throw an {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param weekYear the week year
|
||||
* @param weekOfYear the week number based on {@code weekYear}
|
||||
* @param dayOfWeek the day of week value: one of the constants
|
||||
* for the {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} field:
|
||||
* {@link Calendar#SUNDAY SUNDAY}, ..., {@link Calendar#SATURDAY SATURDAY}.
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @see Calendar#setWeekDate(int, int, int)
|
||||
* @see Calendar#isWeekDateSupported()
|
||||
*/
|
||||
public Builder setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) {
|
||||
allocateFields();
|
||||
internalSet(WEEK_YEAR, weekYear);
|
||||
internalSet(WEEK_OF_YEAR, weekOfYear);
|
||||
internalSet(DAY_OF_WEEK, dayOfWeek);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time zone parameter to the given {@code zone}. If no time
|
||||
* zone parameter is given to this {@code Caledar.Builder}, the
|
||||
* {@linkplain TimeZone#getDefault() default
|
||||
* <code>TimeZone</code>} will be used in the {@link #build() build}
|
||||
* method.
|
||||
*
|
||||
* @param zone the {@link TimeZone}
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws NullPointerException if {@code zone} is {@code null}
|
||||
* @see Calendar#setTimeZone(TimeZone)
|
||||
*/
|
||||
public Builder setTimeZone(TimeZone zone) {
|
||||
if (zone == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.zone = zone;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the lenient mode parameter to the value given by {@code lenient}.
|
||||
* If no lenient parameter is given to this {@code Calendar.Builder},
|
||||
* lenient mode will be used in the {@link #build() build} method.
|
||||
*
|
||||
* @param lenient {@code true} for lenient mode;
|
||||
* {@code false} for non-lenient mode
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @see Calendar#setLenient(boolean)
|
||||
*/
|
||||
public Builder setLenient(boolean lenient) {
|
||||
this.lenient = lenient;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the calendar type parameter to the given {@code type}. The
|
||||
* calendar type given by this method has precedence over any explicit
|
||||
* or implicit calendar type given by the
|
||||
* {@linkplain #setLocale(Locale) locale}.
|
||||
*
|
||||
* <p>In addition to the available calendar types returned by the
|
||||
* {@link Calendar#getAvailableCalendarTypes() Calendar.getAvailableCalendarTypes}
|
||||
* method, {@code "gregorian"} and {@code "iso8601"} as aliases of
|
||||
* {@code "gregory"} can be used with this method.
|
||||
*
|
||||
* @param type the calendar type
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws NullPointerException if {@code type} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code type} is unknown
|
||||
* @throws IllegalStateException if another calendar type has already been set
|
||||
* @see Calendar#getCalendarType()
|
||||
* @see Calendar#getAvailableCalendarTypes()
|
||||
*/
|
||||
public Builder setCalendarType(String type) {
|
||||
if (type.equals("gregorian")) { // NPE if type == null
|
||||
type = "gregory";
|
||||
}
|
||||
if (!Calendar.getAvailableCalendarTypes().contains(type)
|
||||
&& !type.equals("iso8601")) {
|
||||
throw new IllegalArgumentException("unknown calendar type: " + type);
|
||||
}
|
||||
if (this.type == null) {
|
||||
this.type = type;
|
||||
} else {
|
||||
if (!this.type.equals(type)) {
|
||||
throw new IllegalStateException("calendar type override");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the locale parameter to the given {@code locale}. If no locale
|
||||
* is given to this {@code Calendar.Builder}, the {@linkplain
|
||||
* Locale#getDefault(Locale.Category) default <code>Locale</code>}
|
||||
* for {@link Locale.Category#FORMAT} will be used.
|
||||
*
|
||||
* <p>If no calendar type is explicitly given by a call to the
|
||||
* {@link #setCalendarType(String) setCalendarType} method,
|
||||
* the {@code Locale} value is used to determine what type of
|
||||
* {@code Calendar} to be built.
|
||||
*
|
||||
* <p>If no week definition parameters are explicitly given by a call to
|
||||
* the {@link #setWeekDefinition(int,int) setWeekDefinition} method, the
|
||||
* {@code Locale}'s default values are used.
|
||||
*
|
||||
* @param locale the {@link Locale}
|
||||
* @throws NullPointerException if {@code locale} is {@code null}
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @see Calendar#getInstance(Locale)
|
||||
*/
|
||||
public Builder setLocale(Locale locale) {
|
||||
if (locale == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the week definition parameters to the values given by
|
||||
* {@code firstDayOfWeek} and {@code minimalDaysInFirstWeek} that are
|
||||
* used to determine the <a href="Calendar.html#First_Week">first
|
||||
* week</a> of a year. The parameters given by this method have
|
||||
* precedence over the default values given by the
|
||||
* {@linkplain #setLocale(Locale) locale}.
|
||||
*
|
||||
* @param firstDayOfWeek the first day of a week; one of
|
||||
* {@link Calendar#SUNDAY} to {@link Calendar#SATURDAY}
|
||||
* @param minimalDaysInFirstWeek the minimal number of days in the first
|
||||
* week (1..7)
|
||||
* @return this {@code Calendar.Builder}
|
||||
* @throws IllegalArgumentException if {@code firstDayOfWeek} or
|
||||
* {@code minimalDaysInFirstWeek} is invalid
|
||||
* @see Calendar#getFirstDayOfWeek()
|
||||
* @see Calendar#getMinimalDaysInFirstWeek()
|
||||
*/
|
||||
public Builder setWeekDefinition(int firstDayOfWeek, int minimalDaysInFirstWeek) {
|
||||
if (!isValidWeekParameter(firstDayOfWeek)
|
||||
|| !isValidWeekParameter(minimalDaysInFirstWeek)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.firstDayOfWeek = firstDayOfWeek;
|
||||
this.minimalDaysInFirstWeek = minimalDaysInFirstWeek;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Calendar} built from the parameters set by the
|
||||
* setter methods. The calendar type given by the {@link #setCalendarType(String)
|
||||
* setCalendarType} method or the {@linkplain #setLocale(Locale) locale} is
|
||||
* used to determine what {@code Calendar} to be created. If no explicit
|
||||
* calendar type is given, the locale's default calendar is created.
|
||||
*
|
||||
* <p>If the calendar type is {@code "iso8601"}, the
|
||||
* {@linkplain GregorianCalendar#setGregorianChange(Date) Gregorian change date}
|
||||
* of a {@link GregorianCalendar} is set to {@code Date(Long.MIN_VALUE)}
|
||||
* to be the <em>proleptic</em> Gregorian calendar. Its week definition
|
||||
* parameters are also set to be <a
|
||||
* href="GregorianCalendar.html#iso8601_compatible_setting">compatible
|
||||
* with the ISO 8601 standard</a>. Note that the
|
||||
* {@link GregorianCalendar#getCalendarType() getCalendarType} method of
|
||||
* a {@code GregorianCalendar} created with {@code "iso8601"} returns
|
||||
* {@code "gregory"}.
|
||||
*
|
||||
* <p>The default values are used for locale and time zone if these
|
||||
* parameters haven't been given explicitly.
|
||||
*
|
||||
* <p>Any out of range field values are either normalized in lenient
|
||||
* mode or detected as an invalid value in non-lenient mode.
|
||||
*
|
||||
* @return a {@code Calendar} built with parameters of this {@code
|
||||
* Calendar.Builder}
|
||||
* @throws IllegalArgumentException if the calendar type is unknown, or
|
||||
* if any invalid field values are given in non-lenient mode, or
|
||||
* if a week date is given for the calendar type that doesn't
|
||||
* support week dates.
|
||||
* @see Calendar#getInstance(TimeZone, Locale)
|
||||
* @see Locale#getDefault(Locale.Category)
|
||||
* @see TimeZone#getDefault()
|
||||
*/
|
||||
public Calendar build() {
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
if (zone == null) {
|
||||
zone = TimeZone.getDefault();
|
||||
}
|
||||
Calendar cal;
|
||||
if (type == null) {
|
||||
type = locale.getUnicodeLocaleType("ca");
|
||||
}
|
||||
if (type == null) {
|
||||
if (locale.getCountry() == "TH"
|
||||
&& locale.getLanguage() == "th") {
|
||||
type = "buddhist";
|
||||
} else {
|
||||
type = "gregory";
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case "gregory":
|
||||
cal = new GregorianCalendar(zone, locale, true);
|
||||
break;
|
||||
case "iso8601":
|
||||
GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);
|
||||
// make gcal a proleptic Gregorian
|
||||
gcal.setGregorianChange(new Date(Long.MIN_VALUE));
|
||||
// and week definition to be compatible with ISO 8601
|
||||
setWeekDefinition(MONDAY, 4);
|
||||
cal = gcal;
|
||||
break;
|
||||
case "buddhist":
|
||||
cal = new BuddhistCalendar(zone, locale);
|
||||
cal.clear();
|
||||
break;
|
||||
case "japanese":
|
||||
cal = new JapaneseImperialCalendar(zone, locale, true);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown calendar type: " + type);
|
||||
}
|
||||
cal.setLenient(lenient);
|
||||
if (firstDayOfWeek != 0) {
|
||||
cal.setFirstDayOfWeek(firstDayOfWeek);
|
||||
cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
|
||||
}
|
||||
if (isInstantSet()) {
|
||||
cal.setTimeInMillis(instant);
|
||||
cal.complete();
|
||||
return cal;
|
||||
}
|
||||
|
||||
if (fields != null) {
|
||||
boolean weekDate = isSet(WEEK_YEAR)
|
||||
&& fields[WEEK_YEAR] > fields[YEAR];
|
||||
if (weekDate && !cal.isWeekDateSupported()) {
|
||||
throw new IllegalArgumentException("week date is unsupported by " + type);
|
||||
}
|
||||
|
||||
// Set the fields from the min stamp to the max stamp so that
|
||||
// the fields resolution works in the Calendar.
|
||||
for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
|
||||
for (int index = 0; index <= maxFieldIndex; index++) {
|
||||
if (fields[index] == stamp) {
|
||||
cal.set(index, fields[NFIELDS + index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (weekDate) {
|
||||
int weekOfYear = isSet(WEEK_OF_YEAR) ? fields[NFIELDS + WEEK_OF_YEAR] : 1;
|
||||
int dayOfWeek = isSet(DAY_OF_WEEK)
|
||||
? fields[NFIELDS + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
|
||||
cal.setWeekDate(fields[NFIELDS + WEEK_YEAR], weekOfYear, dayOfWeek);
|
||||
}
|
||||
cal.complete();
|
||||
}
|
||||
|
||||
return cal;
|
||||
}
|
||||
|
||||
private void allocateFields() {
|
||||
if (fields == null) {
|
||||
fields = new int[NFIELDS * 2];
|
||||
nextStamp = MINIMUM_USER_STAMP;
|
||||
maxFieldIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private void internalSet(int field, int value) {
|
||||
fields[field] = nextStamp++;
|
||||
if (nextStamp < 0) {
|
||||
throw new IllegalStateException("stamp counter overflow");
|
||||
}
|
||||
fields[NFIELDS + field] = value;
|
||||
if (field > maxFieldIndex && field < WEEK_YEAR) {
|
||||
maxFieldIndex = field;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInstantSet() {
|
||||
return nextStamp == COMPUTED;
|
||||
}
|
||||
|
||||
private boolean isSet(int index) {
|
||||
return fields != null && fields[index] > UNSET;
|
||||
}
|
||||
|
||||
private boolean isValidWeekParameter(int value) {
|
||||
return value > 0 && value <= 7;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Calendar with the default time zone
|
||||
* and locale.
|
||||
@ -1109,7 +1659,7 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
Calendar cal = null;
|
||||
|
||||
if (aLocale.hasExtensions()) {
|
||||
String caltype = aLocale.getUnicodeLocaleType("ca");
|
||||
String caltype = aLocale.getUnicodeLocaleType("ca");
|
||||
if (caltype != null) {
|
||||
switch (caltype) {
|
||||
case "buddhist":
|
||||
@ -2001,6 +2551,38 @@ public abstract class Calendar implements Serializable, Cloneable, Comparable<Ca
|
||||
return (stamp_a > stamp_b) ? stamp_a : stamp_b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@code Set} containing all calendar types
|
||||
* supported by {@code Calendar} in the runtime environment. The available
|
||||
* calendar types can be used for the <a
|
||||
* href="Locale.html#def_locale_extension">Unicode locale extensions</a>.
|
||||
* The {@code Set} returned contains at least {@code "gregory"}. The
|
||||
* calendar types don't include aliases, such as {@code "gregorian"} for
|
||||
* {@code "gregory"}.
|
||||
*
|
||||
* @return an unmodifiable {@code Set} containing all available calendar types
|
||||
* @since 1.8
|
||||
* @see #getCalendarType()
|
||||
* @see Calendar.Builder#setCalendarType(String)
|
||||
* @see Locale#getUnicodeLocaleType(String)
|
||||
*/
|
||||
public static Set<String> getAvailableCalendarTypes() {
|
||||
return AvailableCalendarTypes.SET;
|
||||
}
|
||||
|
||||
private static class AvailableCalendarTypes {
|
||||
private static final Set<String> SET;
|
||||
static {
|
||||
Set<String> set = new HashSet<>(3);
|
||||
set.add("gregory");
|
||||
set.add("buddhist");
|
||||
set.add("japanese");
|
||||
SET = Collections.unmodifiableSet(set);
|
||||
}
|
||||
private AvailableCalendarTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the calendar type of this {@code Calendar}. Calendar types are
|
||||
* defined by the <em>Unicode Locale Data Markup Language (LDML)</em>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -40,7 +40,6 @@ package java.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import sun.util.locale.provider.CalendarDataUtility;
|
||||
import sun.util.calendar.BaseCalendar;
|
||||
import sun.util.calendar.CalendarDate;
|
||||
import sun.util.calendar.CalendarSystem;
|
||||
@ -722,6 +721,18 @@ public class GregorianCalendar extends Calendar {
|
||||
this.internalSet(MILLISECOND, millis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty GregorianCalendar.
|
||||
*
|
||||
* @param zone the given time zone
|
||||
* @param aLocale the given locale
|
||||
* @param flag the flag requesting an empty instance
|
||||
*/
|
||||
GregorianCalendar(TimeZone zone, Locale locale, boolean flag) {
|
||||
super(zone, locale);
|
||||
gdate = (BaseCalendar.Date) gcal.newCalendarDate(getZone());
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// Public methods
|
||||
/////////////////
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -301,6 +301,18 @@ class JapaneseImperialCalendar extends Calendar {
|
||||
setTimeInMillis(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an "empty" {@code JapaneseImperialCalendar}.
|
||||
*
|
||||
* @param zone the given time zone
|
||||
* @param aLocale the given locale
|
||||
* @param flag the flag requesting an empty instance
|
||||
*/
|
||||
JapaneseImperialCalendar(TimeZone zone, Locale aLocale, boolean flag) {
|
||||
super(zone, aLocale);
|
||||
jdate = jcal.newCalendarDate(zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code "japanese"} as the calendar type of this {@code
|
||||
* JapaneseImperialCalendar}.
|
||||
|
289
jdk/test/java/util/Calendar/Builder/BuilderTest.java
Normal file
289
jdk/test/java/util/Calendar/Builder/BuilderTest.java
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4745761
|
||||
* @summary Unit test for Calendar.Builder.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import static java.util.Calendar.*;
|
||||
|
||||
public class BuilderTest {
|
||||
private static final Locale jaJPJP = new Locale("ja", "JP", "JP");
|
||||
private static final Locale thTH = new Locale("th", "TH");
|
||||
private static final TimeZone LA = TimeZone.getTimeZone("America/Los_Angeles");
|
||||
private static final TimeZone TOKYO = TimeZone.getTimeZone("Asia/Tokyo");
|
||||
private static int error;
|
||||
|
||||
public static void main(String[] args) {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
Locale loc = Locale.getDefault();
|
||||
try {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
|
||||
Locale.setDefault(Locale.US);
|
||||
Calendar.Builder calb;
|
||||
Calendar cal, expected;
|
||||
|
||||
// set instant
|
||||
calb = builder();
|
||||
long time = System.currentTimeMillis();
|
||||
cal = calb.setInstant(time).build();
|
||||
expected = new GregorianCalendar();
|
||||
expected.setTimeInMillis(time);
|
||||
check(cal, expected);
|
||||
|
||||
calb = builder();
|
||||
cal = calb.setInstant(new Date(time)).build();
|
||||
check(cal, expected);
|
||||
|
||||
// set time zone
|
||||
calb = builder();
|
||||
cal = calb.setTimeZone(LA).setInstant(time).build();
|
||||
expected = new GregorianCalendar(LA, Locale.US);
|
||||
expected.setTimeInMillis(time);
|
||||
check(cal, expected);
|
||||
|
||||
calb = builder();
|
||||
cal = calb.setTimeZone(TOKYO).setInstant(time).build();
|
||||
expected = new GregorianCalendar(TOKYO, Locale.US);
|
||||
expected.setTimeInMillis(time);
|
||||
check(cal, expected);
|
||||
|
||||
// set vs. setFields
|
||||
calb = builder();
|
||||
cal = calb.set(YEAR, 2013).set(MONTH, JANUARY).set(DAY_OF_MONTH, 31)
|
||||
.set(HOUR_OF_DAY, 10).set(MINUTE, 20).set(SECOND, 30).set(MILLISECOND, 40).build();
|
||||
expected = new GregorianCalendar(2013, JANUARY, 31, 10, 20, 30);
|
||||
expected.set(MILLISECOND, 40);
|
||||
check(cal, expected);
|
||||
|
||||
calb = builder();
|
||||
cal = calb.setFields(YEAR, 2013, MONTH, JANUARY, DAY_OF_MONTH, 31,
|
||||
HOUR_OF_DAY, 10, MINUTE, 20, SECOND, 30, MILLISECOND, 40).build();
|
||||
check(cal, expected);
|
||||
|
||||
// field resolution
|
||||
calb = builder();
|
||||
cal = calb.setFields(YEAR, 2013, MONTH, DECEMBER, DAY_OF_MONTH, 31,
|
||||
HOUR_OF_DAY, 10, MINUTE, 20, SECOND, 30, MILLISECOND, 40)
|
||||
.set(DAY_OF_YEAR, 31).build(); // DAY_OF_YEAR wins.
|
||||
check(cal, expected);
|
||||
|
||||
// setDate/setTimeOfDay
|
||||
calb = builder();
|
||||
cal = calb.setDate(2013, JANUARY, 31).setTimeOfDay(10, 20, 30, 40).build();
|
||||
check(cal, expected);
|
||||
|
||||
// week date (ISO 8601)
|
||||
calb = builder().setCalendarType("iso8601");
|
||||
cal = calb.setWeekDate(2013, 1, MONDAY).setTimeOfDay(10, 20, 30).build();
|
||||
expected = getISO8601();
|
||||
expected.set(2012, DECEMBER, 31, 10, 20, 30);
|
||||
check(cal, expected);
|
||||
|
||||
// default YEAR == 1970
|
||||
cal = builder().setFields(MONTH, JANUARY,
|
||||
DAY_OF_MONTH, 9).build();
|
||||
check(cal, new GregorianCalendar(1970, JANUARY, 9));
|
||||
|
||||
// no parameters are given.
|
||||
calb = builder();
|
||||
cal = calb.build();
|
||||
expected = new GregorianCalendar();
|
||||
expected.clear();
|
||||
check(cal, expected);
|
||||
|
||||
// Thai Buddhist calendar
|
||||
calb = builder();
|
||||
cal = calb.setCalendarType("buddhist").setDate(2556, JANUARY, 31).build();
|
||||
expected = Calendar.getInstance(thTH);
|
||||
expected.clear();
|
||||
expected.set(2556, JANUARY, 31);
|
||||
check(cal, expected);
|
||||
// setLocale
|
||||
calb = builder();
|
||||
cal = calb.setLocale(thTH).setDate(2556, JANUARY, 31).build();
|
||||
check(cal, expected);
|
||||
|
||||
// Japanese Imperial calendar
|
||||
cal = builder().setCalendarType("japanese")
|
||||
.setFields(YEAR, 1, DAY_OF_YEAR, 1).build();
|
||||
expected = Calendar.getInstance(jaJPJP);
|
||||
expected.clear();
|
||||
expected.set(1, JANUARY, 8);
|
||||
check(cal, expected);
|
||||
// setLocale
|
||||
calb = builder();
|
||||
cal = calb.setLocale(jaJPJP).setFields(YEAR, 1, DAY_OF_YEAR, 1).build();
|
||||
check(cal, expected);
|
||||
|
||||
testExceptions();
|
||||
} finally {
|
||||
// Restore default Locale and TimeZone
|
||||
Locale.setDefault(loc);
|
||||
TimeZone.setDefault(tz);
|
||||
}
|
||||
if (error > 0) {
|
||||
throw new RuntimeException("Failed");
|
||||
}
|
||||
}
|
||||
|
||||
private static void testExceptions() {
|
||||
Calendar.Builder calb;
|
||||
Calendar cal;
|
||||
|
||||
// NPE
|
||||
try {
|
||||
calb = builder().setInstant((Date)null);
|
||||
noException("setInstant((Date)null)");
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
try {
|
||||
calb = builder().setCalendarType(null);
|
||||
noException("setCalendarType(null)");
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
try {
|
||||
calb = builder().setLocale(null);
|
||||
noException("setLocale(null)");
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
try {
|
||||
calb = builder().setTimeZone(null);
|
||||
noException("setTimeZone(null)");
|
||||
} catch (NullPointerException npe) {
|
||||
}
|
||||
|
||||
// IllegalArgumentException
|
||||
try {
|
||||
// invalid field index in set
|
||||
calb = builder().set(100, 2013);
|
||||
noException("set(100, 2013)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
// invalid field index in setField
|
||||
calb = builder().setFields(100, 2013);
|
||||
noException("setFields(100, 2013)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
// odd number of arguments
|
||||
calb = builder().setFields(YEAR, 2013, MONTH);
|
||||
noException("setFields(YEAR, 2013, MONTH)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
// unknown calendar type
|
||||
calb = builder().setCalendarType("foo");
|
||||
noException("setCalendarType(\"foo\")");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
// invalid week definition parameter
|
||||
calb = builder().setWeekDefinition(8, 1);
|
||||
noException("setWeekDefinition(8, 1)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
// invalid week definition parameter
|
||||
calb = builder().setWeekDefinition(SUNDAY, 0);
|
||||
noException("setWeekDefinition(8, 1)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
// sets both instant and field parameters
|
||||
calb = builder().setInstant(new Date()).setDate(2013, JANUARY, 1);
|
||||
noException("setInstant(new Date()).setDate(2013, JANUARY, 1)");
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
// sets both field parameters and instant
|
||||
calb = builder().setDate(2013, JANUARY, 1).setInstant(new Date());
|
||||
noException("setDate(2013, JANUARY, 1).setInstant(new Date())");
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
try {
|
||||
// sets inconsistent calendar types
|
||||
calb = builder().setCalendarType("iso8601").setCalendarType("japanese");
|
||||
noException("setCalendarType(\"iso8601\").setCalendarType(\"japanese\")");
|
||||
} catch (IllegalStateException e) {
|
||||
}
|
||||
|
||||
// IllegalArgumentException in build()
|
||||
calb = nonLenientBuilder().set(MONTH, 100);
|
||||
checkException(calb, IllegalArgumentException.class);
|
||||
calb = nonLenientBuilder().setTimeOfDay(23, 59, 70);
|
||||
checkException(calb, IllegalArgumentException.class);
|
||||
calb = builder().setCalendarType("japanese").setWeekDate(2013, 1, MONDAY);
|
||||
checkException(calb, IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
private static Calendar.Builder builder() {
|
||||
return new Calendar.Builder();
|
||||
}
|
||||
|
||||
private static Calendar.Builder nonLenientBuilder() {
|
||||
return builder().setLenient(false);
|
||||
}
|
||||
|
||||
private static Calendar getISO8601() {
|
||||
GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.setFirstDayOfWeek(MONDAY);
|
||||
cal.setMinimalDaysInFirstWeek(4);
|
||||
cal.setGregorianChange(new Date(Long.MIN_VALUE));
|
||||
cal.clear();
|
||||
return cal;
|
||||
}
|
||||
|
||||
private static void check(Calendar cal, Calendar expected) {
|
||||
if (!cal.equals(expected)) {
|
||||
error++;
|
||||
System.err.println("FAILED:");
|
||||
System.err.println("\t cal = "+cal.getTime());
|
||||
System.err.println("\texpected = "+expected.getTime());
|
||||
System.err.printf("\tcal = %s%n\texp = %s%n", cal, expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkException(Calendar.Builder calb, Class<? extends Exception> exception) {
|
||||
try {
|
||||
Calendar cal = calb.build();
|
||||
error++;
|
||||
System.err.println("expected exception: " + exception);
|
||||
} catch (Exception e) {
|
||||
if (!e.getClass().equals(exception)) {
|
||||
error++;
|
||||
System.err.println("unexpected exception: " + e.getClass() + ", expected: " + exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void noException(String msg) {
|
||||
error++;
|
||||
System.err.println("no exception with "+msg);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 7151414
|
||||
* @bug 7151414 4745761
|
||||
* @summary Unit test for calendar types
|
||||
*/
|
||||
|
||||
@ -39,20 +39,21 @@ public class CalendarTypeTest {
|
||||
new Locale("ja", "JP", "JP"),
|
||||
Locale.forLanguageTag("en-US-u-ca-japanese"),
|
||||
};
|
||||
static String[] types = new String[] {
|
||||
"gregory",
|
||||
static final String[] TYPES = new String[] {
|
||||
"gregory",
|
||||
"buddhist",
|
||||
"buddhist",
|
||||
"japanese",
|
||||
"japanese",
|
||||
};
|
||||
static final String[] ALIASES = new String[] {
|
||||
"gregorian",
|
||||
"iso8601",
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < locales.length; i++) {
|
||||
Calendar cal = Calendar.getInstance(locales[i]);
|
||||
String type = cal.getCalendarType();
|
||||
checkValue("bad calendar type", type, types[i]);
|
||||
checkValue("bad calendar type", type, TYPES[i/2]);
|
||||
}
|
||||
|
||||
GregorianCalendar gcal = new GregorianCalendar();
|
||||
@ -63,6 +64,21 @@ public class CalendarTypeTest {
|
||||
|
||||
Calendar k = new Koyomi();
|
||||
checkValue("bad class name", k.getCalendarType(), k.getClass().getName());
|
||||
|
||||
Set<String> types = Calendar.getAvailableCalendarTypes();
|
||||
if (types.size() != 3) {
|
||||
throw new RuntimeException("size != 3");
|
||||
}
|
||||
for (String s : TYPES) {
|
||||
if (!types.contains(s)) {
|
||||
throw new RuntimeException(s + " not contained");
|
||||
}
|
||||
}
|
||||
for (String s : ALIASES) {
|
||||
if (types.contains(s)) {
|
||||
throw new RuntimeException("alias " + s + " contained");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkValue(String msg, String got, String expected) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user