src/share/classes/java/time/chrono/Chronology.java
Print this page
*** 59,100 ****
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
- import java.time.chrono.HijrahChronology;
- import java.time.chrono.JapaneseChronology;
- import java.time.chrono.MinguoChronology;
- import java.time.chrono.ThaiBuddhistChronology;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
! import java.time.temporal.Queries;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.ValueRange;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A calendar system, used to organize and identify dates.
* <p>
* The main date and time API is built on the ISO calendar system.
* This class operates behind the scenes to represent the general concept of a calendar system.
--- 59,119 ----
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package java.time.chrono;
+ import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
+ import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
+ import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
+ import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
+ import static java.time.temporal.ChronoField.DAY_OF_MONTH;
+ import static java.time.temporal.ChronoField.DAY_OF_WEEK;
+ import static java.time.temporal.ChronoField.DAY_OF_YEAR;
+ import static java.time.temporal.ChronoField.EPOCH_DAY;
+ import static java.time.temporal.ChronoField.ERA;
+ import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
+ import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
+ import static java.time.temporal.ChronoField.YEAR;
+ import static java.time.temporal.ChronoField.YEAR_OF_ERA;
+ import static java.time.temporal.TemporalAdjuster.nextOrSame;
+
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
+ import java.io.Serializable;
import java.time.Clock;
import java.time.DateTimeException;
+ import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatterBuilder;
+ import java.time.format.ResolverStyle;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
! import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
+ import java.time.temporal.UnsupportedTemporalTypeException;
import java.time.temporal.ValueRange;
+ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+ import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+ import sun.util.logging.PlatformLogger;
+
/**
* A calendar system, used to organize and identify dates.
* <p>
* The main date and time API is built on the ISO calendar system.
* This class operates behind the scenes to represent the general concept of a calendar system.
*** 104,114 ****
* linked to the cycles of the Earth around the Sun, and the Moon around the Earth.
* These shared concepts are defined by {@link ChronoField} and are available
* for use by any {@code Chronology} implementation:
* <pre>
* LocalDate isoDate = ...
! * ChronoLocalDate<ThaiBuddhistChronology> thaiDate = ...
* int isoYear = isoDate.get(ChronoField.YEAR);
* int thaiYear = thaiDate.get(ChronoField.YEAR);
* </pre>
* As shown, although the date objects are in different calendar systems, represented by different
* {@code Chronology} instances, both can be queried using the same constant on {@code ChronoField}.
--- 123,133 ----
* linked to the cycles of the Earth around the Sun, and the Moon around the Earth.
* These shared concepts are defined by {@link ChronoField} and are available
* for use by any {@code Chronology} implementation:
* <pre>
* LocalDate isoDate = ...
! * ThaiBuddhistDate thaiDate = ...
* int isoYear = isoDate.get(ChronoField.YEAR);
* int thaiYear = thaiDate.get(ChronoField.YEAR);
* </pre>
* As shown, although the date objects are in different calendar systems, represented by different
* {@code Chronology} instances, both can be queried using the same constant on {@code ChronoField}.
*** 152,163 ****
* For lookup by id or calendarType, the system provided calendars are found
* first followed by application provided calendars.
* <p>
* Each chronology must define a chronology ID that is unique within the system.
* If the chronology represents a calendar system defined by the
! * <em>Unicode Locale Data Markup Language (LDML)</em> specification then that
! * calendar type should also be specified.
*
* <h3>Specification for implementors</h3>
* This class must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
--- 171,182 ----
* For lookup by id or calendarType, the system provided calendars are found
* first followed by application provided calendars.
* <p>
* Each chronology must define a chronology ID that is unique within the system.
* If the chronology represents a calendar system defined by the
! * CLDR specification then the calendar type is the concatenation of the
! * CLDR type and, if applicable, the CLDR variant,
*
* <h3>Specification for implementors</h3>
* This class must be implemented with care to ensure other classes operate correctly.
* All implementations that can be instantiated must be final, immutable and thread-safe.
* Subclasses should be Serializable wherever possible.
*** 165,196 ****
* @since 1.8
*/
public abstract class Chronology implements Comparable<Chronology> {
/**
* Map of available calendars by ID.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>();
/**
* Map of available calendars by calendar type.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>();
/**
* Register a Chronology by ID and type for lookup by {@link #of(java.lang.String)}.
* Chronos must not be registered until they are completely constructed.
* Specifically, not in the constructor of Chronology.
* @param chrono the chronology to register; not null
*/
! private static void registerChrono(Chronology chrono) {
! Chronology prev = CHRONOS_BY_ID.putIfAbsent(chrono.getId(), chrono);
if (prev == null) {
String type = chrono.getCalendarType();
if (type != null) {
CHRONOS_BY_TYPE.putIfAbsent(type, chrono);
}
}
}
/**
* Initialization of the maps from id and type to Chronology.
* The ServiceLoader is used to find and register any implementations
--- 184,261 ----
* @since 1.8
*/
public abstract class Chronology implements Comparable<Chronology> {
/**
+ * ChronoLocalDate order constant.
+ */
+ static final Comparator<ChronoLocalDate<?>> DATE_ORDER =
+ (Comparator<ChronoLocalDate<?>> & Serializable) (date1, date2) -> {
+ return Long.compare(date1.toEpochDay(), date2.toEpochDay());
+ };
+ /**
+ * ChronoLocalDateTime order constant.
+ */
+ static final Comparator<ChronoLocalDateTime<?>> DATE_TIME_ORDER =
+ (Comparator<ChronoLocalDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
+ int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
+ if (cmp == 0) {
+ cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
+ }
+ return cmp;
+ };
+ /**
+ * ChronoZonedDateTime order constant.
+ */
+ static final Comparator<ChronoZonedDateTime<?>> INSTANT_ORDER =
+ (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
+ int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
+ if (cmp == 0) {
+ cmp = Long.compare(dateTime1.toLocalTime().getNano(), dateTime2.toLocalTime().getNano());
+ }
+ return cmp;
+ };
+
+ /**
* Map of available calendars by ID.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>();
/**
* Map of available calendars by calendar type.
*/
private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>();
/**
+ * Register a Chronology by its ID and type for lookup by {@link #of(java.lang.String)}.
+ * Chronologies must not be registered until they are completely constructed.
+ * Specifically, not in the constructor of Chronology.
+ *
+ * @param chrono the chronology to register; not null
+ * @return the already registered Chronology if any, may be null
+ */
+ static Chronology registerChrono(Chronology chrono) {
+ return registerChrono(chrono, chrono.getId());
+ }
+
+ /**
* Register a Chronology by ID and type for lookup by {@link #of(java.lang.String)}.
* Chronos must not be registered until they are completely constructed.
* Specifically, not in the constructor of Chronology.
+ *
* @param chrono the chronology to register; not null
+ * @param id the ID to register the chronology; not null
+ * @return the already registered Chronology if any, may be null
*/
! static Chronology registerChrono(Chronology chrono, String id) {
! Chronology prev = CHRONOS_BY_ID.putIfAbsent(id, chrono);
if (prev == null) {
String type = chrono.getCalendarType();
if (type != null) {
CHRONOS_BY_TYPE.putIfAbsent(type, chrono);
}
}
+ return prev;
}
/**
* Initialization of the maps from id and type to Chronology.
* The ServiceLoader is used to find and register any implementations
*** 207,228 ****
* @return true if the cache was initialized
*/
private static boolean initCache() {
if (CHRONOS_BY_ID.get("ISO") == null) {
// Initialization is incomplete
- @SuppressWarnings("rawtypes")
- ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class, null);
- for (Chronology chrono : loader) {
- registerChrono(chrono);
- }
! // Register these calendars; the ServiceLoader configuration is not used
registerChrono(HijrahChronology.INSTANCE);
registerChrono(JapaneseChronology.INSTANCE);
registerChrono(MinguoChronology.INSTANCE);
registerChrono(ThaiBuddhistChronology.INSTANCE);
// finally, register IsoChronology to mark initialization is complete
registerChrono(IsoChronology.INSTANCE);
return true;
}
return false;
--- 272,300 ----
* @return true if the cache was initialized
*/
private static boolean initCache() {
if (CHRONOS_BY_ID.get("ISO") == null) {
// Initialization is incomplete
! // Register built-in Chronologies
registerChrono(HijrahChronology.INSTANCE);
registerChrono(JapaneseChronology.INSTANCE);
registerChrono(MinguoChronology.INSTANCE);
registerChrono(ThaiBuddhistChronology.INSTANCE);
+ // Register Chronologies from the ServiceLoader
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class, null);
+ for (Chronology chrono : loader) {
+ String id = chrono.getId();
+ if (id.equals("ISO") || registerChrono(chrono) != null) {
+ // Log the attempt to replace an existing Chronology
+ PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono");
+ logger.warning("Ignoring duplicate Chronology, from ServiceLoader configuration " + id);
+ }
+ }
+
// finally, register IsoChronology to mark initialization is complete
registerChrono(IsoChronology.INSTANCE);
return true;
}
return false;
*** 234,244 ****
* <p>
* This obtains a chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code Chronology}.
* <p>
! * The conversion will obtain the chronology using {@link Queries#chronology()}.
* If the specified temporal object does not have a chronology, {@link IsoChronology} is returned.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code Chronology::from}.
*
--- 306,316 ----
* <p>
* This obtains a chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code Chronology}.
* <p>
! * The conversion will obtain the chronology using {@link TemporalQuery#chronology()}.
* If the specified temporal object does not have a chronology, {@link IsoChronology} is returned.
* <p>
* This method matches the signature of the functional interface {@link TemporalQuery}
* allowing it to be used in queries via method reference, {@code Chronology::from}.
*
*** 246,256 ****
* @return the chronology, not null
* @throws DateTimeException if unable to convert to an {@code Chronology}
*/
public static Chronology from(TemporalAccessor temporal) {
Objects.requireNonNull(temporal, "temporal");
! Chronology obj = temporal.query(Queries.chronology());
return (obj != null ? obj : IsoChronology.INSTANCE);
}
//-----------------------------------------------------------------------
/**
--- 318,328 ----
* @return the chronology, not null
* @throws DateTimeException if unable to convert to an {@code Chronology}
*/
public static Chronology from(TemporalAccessor temporal) {
Objects.requireNonNull(temporal, "temporal");
! Chronology obj = temporal.query(TemporalQuery.chronology());
return (obj != null ? obj : IsoChronology.INSTANCE);
}
//-----------------------------------------------------------------------
/**
*** 264,280 ****
* for localizing an application. This includes the language and region,
* such as "en-GB" for English as used in Great Britain.
* <p>
* The {@code Locale} class also supports an extension mechanism that
* can be used to identify a calendar system. The mechanism is a form
! * of key-value pairs, where the calendar system has the key "ca".
* For example, the locale "en-JP-u-ca-japanese" represents the English
* language as used in Japan with the Japanese calendar system.
* <p>
* This method finds the desired calendar system by in a manner equivalent
* to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}.
* If the "ca" key is not present, then {@code IsoChronology} is returned.
* <p>
* Note that the behavior of this method differs from the older
* {@link java.util.Calendar#getInstance(Locale)} method.
* If that method receives a locale of "th_TH" it will return {@code BuddhistCalendar}.
* By contrast, this method will return {@code IsoChronology}.
--- 336,355 ----
* for localizing an application. This includes the language and region,
* such as "en-GB" for English as used in Great Britain.
* <p>
* The {@code Locale} class also supports an extension mechanism that
* can be used to identify a calendar system. The mechanism is a form
! * of key-value pairs, where the calendar system has the key "ca"
! * and an optional variant key "cv".
* For example, the locale "en-JP-u-ca-japanese" represents the English
* language as used in Japan with the Japanese calendar system.
* <p>
* This method finds the desired calendar system by in a manner equivalent
* to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}.
* If the "ca" key is not present, then {@code IsoChronology} is returned.
+ * The variant, if present, is appended to the "ca" value separated by "-"
+ * and the concatenated value is used to find the calendar system by type.
* <p>
* Note that the behavior of this method differs from the older
* {@link java.util.Calendar#getInstance(Locale)} method.
* If that method receives a locale of "th_TH" it will return {@code BuddhistCalendar}.
* By contrast, this method will return {@code IsoChronology}.
*** 297,325 ****
Objects.requireNonNull(locale, "locale");
String type = locale.getUnicodeLocaleType("ca");
if (type == null || "iso".equals(type) || "iso8601".equals(type)) {
return IsoChronology.INSTANCE;
}
// Not pre-defined; lookup by the type
do {
Chronology chrono = CHRONOS_BY_TYPE.get(type);
if (chrono != null) {
return chrono;
}
// If not found, do the initialization (once) and repeat the lookup
} while (initCache());
throw new DateTimeException("Unknown calendar system: " + type);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a chronology ID or
* calendar system type.
* <p>
* This returns a chronology based on either the ID or the type.
* The {@link #getId() chronology ID} uniquely identifies the chronology.
! * The {@link #getCalendarType() calendar system type} is defined by the LDML specification.
* <p>
* The chronology may be a system chronology or a chronology
* provided by the application via ServiceLoader configuration.
* <p>
* Since some calendars can be customized, the ID or type typically refers
--- 372,415 ----
Objects.requireNonNull(locale, "locale");
String type = locale.getUnicodeLocaleType("ca");
if (type == null || "iso".equals(type) || "iso8601".equals(type)) {
return IsoChronology.INSTANCE;
}
+ String variant = locale.getUnicodeLocaleType("cv");
+ if (variant != null && !variant.isEmpty()) {
+ type = type + '-' + variant;
+ }
// Not pre-defined; lookup by the type
do {
Chronology chrono = CHRONOS_BY_TYPE.get(type);
if (chrono != null) {
return chrono;
}
// If not found, do the initialization (once) and repeat the lookup
} while (initCache());
+
+ // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader
+ // Application provided Chronologies must not be cached
+ @SuppressWarnings("rawtypes")
+ ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
+ for (Chronology chrono : loader) {
+ if (type.equals(chrono.getCalendarType())) {
+ return chrono;
+ }
+ }
throw new DateTimeException("Unknown calendar system: " + type);
}
//-----------------------------------------------------------------------
/**
* Obtains an instance of {@code Chronology} from a chronology ID or
* calendar system type.
* <p>
* This returns a chronology based on either the ID or the type.
* The {@link #getId() chronology ID} uniquely identifies the chronology.
! * The {@link #getCalendarType() calendar system type} is defined by the
! * CLDR specification.
* <p>
* The chronology may be a system chronology or a chronology
* provided by the application via ServiceLoader configuration.
* <p>
* Since some calendars can be customized, the ID or type typically refers
*** 377,387 ****
*
* @return the independent, modifiable set of the available chronology IDs, not null
*/
public static Set<Chronology> getAvailableChronologies() {
initCache(); // force initialization
! HashSet<Chronology> chronos = new HashSet(CHRONOS_BY_ID.values());
/// Add in Chronologies from the ServiceLoader configuration
@SuppressWarnings("rawtypes")
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
for (Chronology chrono : loader) {
--- 467,477 ----
*
* @return the independent, modifiable set of the available chronology IDs, not null
*/
public static Set<Chronology> getAvailableChronologies() {
initCache(); // force initialization
! HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
/// Add in Chronologies from the ServiceLoader configuration
@SuppressWarnings("rawtypes")
ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
for (Chronology chrono : loader) {
*** 404,416 ****
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDate}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
* or the chronology is not equal this Chronology
*/
! ChronoLocalDate ensureChronoLocalDate(Temporal temporal) {
@SuppressWarnings("unchecked")
! ChronoLocalDate other = (ChronoLocalDate) temporal;
if (this.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, expected: " + getId() + ", actual: " + other.getChronology().getId());
}
return other;
}
--- 494,506 ----
* @param temporal a date-time to cast, not null
* @return the date-time checked and cast to {@code ChronoLocalDate}, not null
* @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
* or the chronology is not equal this Chronology
*/
! ChronoLocalDate<?> ensureChronoLocalDate(Temporal temporal) {
@SuppressWarnings("unchecked")
! ChronoLocalDate<?> other = (ChronoLocalDate<?>) temporal;
if (this.equals(other.getChronology()) == false) {
throw new ClassCastException("Chronology mismatch, expected: " + getId() + ", actual: " + other.getChronology().getId());
}
return other;
}
*** 462,480 ****
* @see #getCalendarType()
*/
public abstract String getId();
/**
! * Gets the calendar type of the underlying calendar system.
* <p>
! * The calendar type is an identifier defined by the
! * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
! * It can be used to lookup the {@code Chronology} using {@link #of(String)}.
! * It can also be used as part of a locale, accessible via
! * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
*
! * @return the calendar system type, null if the calendar is not defined by LDML
* @see #getId()
*/
public abstract String getCalendarType();
//-----------------------------------------------------------------------
--- 552,571 ----
* @see #getCalendarType()
*/
public abstract String getId();
/**
! * Gets the calendar type of the calendar system.
* <p>
! * The calendar type is an identifier defined by the CLDR and
! * <em>Unicode Locale Data Markup Language (LDML)</em> specifications
! * to uniquely identification a calendar.
! * The {@code getCalendarType} is the concatenation of the CLDR calendar type
! * and the variant, if applicable, is appended separated by "-".
! * The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
*
! * @return the calendar system type, null if the calendar is not defined
* @see #getId()
*/
public abstract String getCalendarType();
//-----------------------------------------------------------------------
*** 486,497 ****
* @param yearOfEra the chronology year-of-era
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in this chronology from the proleptic-year,
--- 577,589 ----
* @param yearOfEra the chronology year-of-era
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
! public ChronoLocalDate<?> date(Era era, int yearOfEra, int month, int dayOfMonth) {
return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
}
/**
* Obtains a local date in this chronology from the proleptic-year,
*** 501,523 ****
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public abstract ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth);
/**
* Obtains a local date in this chronology from the era, year-of-era and
* day-of-year fields.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in this chronology from the proleptic-year and
--- 593,616 ----
* @param month the chronology month-of-year
* @param dayOfMonth the chronology day-of-month
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public abstract ChronoLocalDate<?> date(int prolepticYear, int month, int dayOfMonth);
/**
* Obtains a local date in this chronology from the era, year-of-era and
* day-of-year fields.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
+ * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
! public ChronoLocalDate<?> dateYearDay(Era era, int yearOfEra, int dayOfYear) {
return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
}
/**
* Obtains a local date in this chronology from the proleptic-year and
*** 526,536 ****
* @param prolepticYear the chronology proleptic-year
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public abstract ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear);
//-----------------------------------------------------------------------
/**
* Obtains the current local date in this chronology from the system clock in the default time-zone.
* <p>
--- 619,641 ----
* @param prolepticYear the chronology proleptic-year
* @param dayOfYear the chronology day-of-year
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public abstract ChronoLocalDate<?> dateYearDay(int prolepticYear, int dayOfYear);
!
! /**
! * Obtains a local date in this chronology from the epoch-day.
! * <p>
! * The definition of {@link ChronoField#EPOCH_DAY EPOCH_DAY} is the same
! * for all calendar systems, thus it can be used for conversion.
! *
! * @param epochDay the epoch day
! * @return the local date in this chronology, not null
! * @throws DateTimeException if unable to create the date
! */
! public abstract ChronoLocalDate<?> dateEpochDay(long epochDay);
//-----------------------------------------------------------------------
/**
* Obtains the current local date in this chronology from the system clock in the default time-zone.
* <p>
*** 543,553 ****
* This implementation uses {@link #dateNow(Clock)}.
*
* @return the current local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate dateNow() {
return dateNow(Clock.systemDefaultZone());
}
/**
* Obtains the current local date in this chronology from the system clock in the specified time-zone.
--- 648,658 ----
* This implementation uses {@link #dateNow(Clock)}.
*
* @return the current local date using the system clock and default time-zone, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate<?> dateNow() {
return dateNow(Clock.systemDefaultZone());
}
/**
* Obtains the current local date in this chronology from the system clock in the specified time-zone.
*** 560,570 ****
*
* @param zone the zone ID to use, not null
* @return the current local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
/**
* Obtains the current local date in this chronology from the specified clock.
--- 665,675 ----
*
* @param zone the zone ID to use, not null
* @return the current local date using the system clock, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate<?> dateNow(ZoneId zone) {
return dateNow(Clock.system(zone));
}
/**
* Obtains the current local date in this chronology from the specified clock.
*** 575,594 ****
*
* @param clock the clock to use, not null
* @return the current local date, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate dateNow(Clock clock) {
Objects.requireNonNull(clock, "clock");
return date(LocalDate.now(clock));
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in this chronology from another temporal object.
* <p>
! * This creates a date in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
--- 680,699 ----
*
* @param clock the clock to use, not null
* @return the current local date, not null
* @throws DateTimeException if unable to create the date
*/
! public ChronoLocalDate<?> dateNow(Clock clock) {
Objects.requireNonNull(clock, "clock");
return date(LocalDate.now(clock));
}
//-----------------------------------------------------------------------
/**
* Obtains a local date in this chronology from another temporal object.
* <p>
! * This obtains a date in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDate}.
* <p>
* The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
* field, which is standardized across calendar systems.
*** 597,613 ****
* allowing it to be used as a query via method reference, {@code aChronology::date}.
*
* @param temporal the temporal object to convert, not null
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
*/
! public abstract ChronoLocalDate date(TemporalAccessor temporal);
/**
* Obtains a local date-time in this chronology from another temporal object.
* <p>
! * This creates a date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDateTime}.
* <p>
* The conversion extracts and combines the {@code ChronoLocalDate} and the
* {@code LocalTime} from the temporal object.
--- 702,719 ----
* allowing it to be used as a query via method reference, {@code aChronology::date}.
*
* @param temporal the temporal object to convert, not null
* @return the local date in this chronology, not null
* @throws DateTimeException if unable to create the date
+ * @see ChronoLocalDate#from(TemporalAccessor)
*/
! public abstract ChronoLocalDate<?> date(TemporalAccessor temporal);
/**
* Obtains a local date-time in this chronology from another temporal object.
* <p>
! * This obtains a date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoLocalDateTime}.
* <p>
* The conversion extracts and combines the {@code ChronoLocalDate} and the
* {@code LocalTime} from the temporal object.
*** 619,628 ****
--- 725,735 ----
* allowing it to be used as a query via method reference, {@code aChronology::localDateTime}.
*
* @param temporal the temporal object to convert, not null
* @return the local date-time in this chronology, not null
* @throws DateTimeException if unable to create the date-time
+ * @see ChronoLocalDateTime#from(TemporalAccessor)
*/
public ChronoLocalDateTime<?> localDateTime(TemporalAccessor temporal) {
try {
return date(temporal).atTime(LocalTime.from(temporal));
} catch (DateTimeException ex) {
*** 631,641 ****
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
* <p>
! * This creates a zoned date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoZonedDateTime}.
* <p>
* The conversion will first obtain a {@code ZoneId} from the temporal object,
* falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
--- 738,748 ----
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
* <p>
! * This obtains a zoned date-time in this chronology based on the specified temporal.
* A {@code TemporalAccessor} represents an arbitrary set of date and time information,
* which this factory converts to an instance of {@code ChronoZonedDateTime}.
* <p>
* The conversion will first obtain a {@code ZoneId} from the temporal object,
* falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
*** 650,668 ****
--- 757,777 ----
* allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}.
*
* @param temporal the temporal object to convert, not null
* @return the zoned date-time in this chronology, not null
* @throws DateTimeException if unable to create the date-time
+ * @see ChronoZonedDateTime#from(TemporalAccessor)
*/
public ChronoZonedDateTime<?> zonedDateTime(TemporalAccessor temporal) {
try {
ZoneId zone = ZoneId.from(temporal);
try {
Instant instant = Instant.from(temporal);
return zonedDateTime(instant, zone);
} catch (DateTimeException ex1) {
+ @SuppressWarnings("rawtypes")
ChronoLocalDateTimeImpl cldt = ensureChronoLocalDateTime(localDateTime(temporal));
return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
}
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
*** 670,680 ****
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
* <p>
! * This creates a zoned date-time with the same instant as that specified.
*
* @param instant the instant to create the date-time from, not null
* @param zone the time-zone, not null
* @return the zoned date-time, not null
* @throws DateTimeException if the result exceeds the supported range
--- 779,789 ----
}
/**
* Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
* <p>
! * This obtains a zoned date-time with the same instant as that specified.
*
* @param instant the instant to create the date-time from, not null
* @param zone the time-zone, not null
* @return the zoned date-time, not null
* @throws DateTimeException if the result exceeds the supported range
*** 701,715 ****
/**
* Calculates the proleptic-year given the era and year-of-era.
* <p>
* This combines the era and year-of-era into the single proleptic-year field.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @return the proleptic-year
! * @throws DateTimeException if unable to convert
*/
public abstract int prolepticYear(Era era, int yearOfEra);
/**
* Creates the chronology era object from the numeric value.
--- 810,830 ----
/**
* Calculates the proleptic-year given the era and year-of-era.
* <p>
* This combines the era and year-of-era into the single proleptic-year field.
+ * <p>
+ * If the chronology makes active use of eras, such as {@code JapaneseChronology}
+ * then the year-of-era will be validated against the era.
+ * For other chronologies, validation is optional.
*
* @param era the era of the correct type for the chronology, not null
* @param yearOfEra the chronology year-of-era
* @return the proleptic-year
! * @throws DateTimeException if unable to convert to a proleptic-year,
! * such as if the year is invalid for the era
! * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
*/
public abstract int prolepticYear(Era era, int yearOfEra);
/**
* Creates the chronology era object from the numeric value.
*** 773,800 ****
* @param style the style of the text required, not null
* @param locale the locale to use, not null
* @return the text value of the chronology, not null
*/
public String getDisplayName(TextStyle style, Locale locale) {
! return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(new TemporalAccessor() {
@Override
public boolean isSupported(TemporalField field) {
return false;
}
@Override
public long getLong(TemporalField field) {
! throw new DateTimeException("Unsupported field: " + field);
}
@SuppressWarnings("unchecked")
@Override
public <R> R query(TemporalQuery<R> query) {
! if (query == Queries.chronology()) {
return (R) Chronology.this;
}
return TemporalAccessor.super.query(query);
}
! });
}
//-----------------------------------------------------------------------
/**
* Compares this chronology to another chronology.
--- 888,1066 ----
* @param style the style of the text required, not null
* @param locale the locale to use, not null
* @return the text value of the chronology, not null
*/
public String getDisplayName(TextStyle style, Locale locale) {
! return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(toTemporal());
! }
!
! /**
! * Converts this chronology to a {@code TemporalAccessor}.
! * <p>
! * A {@code Chronology} can be fully represented as a {@code TemporalAccessor}.
! * However, the interface is not implemented by this class as most of the
! * methods on the interface have no meaning to {@code Chronology}.
! * <p>
! * The returned temporal has no supported fields, with the query method
! * supporting the return of the chronology using {@link TemporalQuery#chronology()}.
! *
! * @return a temporal equivalent to this chronology, not null
! */
! private TemporalAccessor toTemporal() {
! return new TemporalAccessor() {
@Override
public boolean isSupported(TemporalField field) {
return false;
}
@Override
public long getLong(TemporalField field) {
! throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
}
@SuppressWarnings("unchecked")
@Override
public <R> R query(TemporalQuery<R> query) {
! if (query == TemporalQuery.chronology()) {
return (R) Chronology.this;
}
return TemporalAccessor.super.query(query);
}
! };
! }
!
! //-----------------------------------------------------------------------
! /**
! * Resolves parsed {@code ChronoField} values into a date during parsing.
! * <p>
! * Most {@code TemporalField} implementations are resolved using the
! * resolve method on the field. By contrast, the {@code ChronoField} class
! * defines fields that only have meaning relative to the chronology.
! * As such, {@code ChronoField} date fields are resolved here in the
! * context of a specific chronology.
! * <p>
! * The default implementation is suitable for most calendar systems.
! * If {@link ChronoField#YEAR_OF_ERA} is found without an {@link ChronoField#ERA}
! * then the last era in {@link #eras()} is used.
! * The implementation assumes a 7 day week, that the first day-of-month
! * has the value 1, and that first day-of-year has the value 1.
! *
! * @param fieldValues the map of fields to values, which can be updated, not null
! * @param resolverStyle the requested type of resolve, not null
! * @return the resolved date, null if insufficient information to create a date
! * @throws DateTimeException if the date cannot be resolved, typically
! * because of a conflict in the input data
! */
! public ChronoLocalDate<?> resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
! // check epoch-day before inventing era
! if (fieldValues.containsKey(EPOCH_DAY)) {
! return dateEpochDay(fieldValues.remove(EPOCH_DAY));
! }
!
! // fix proleptic month before inventing era
! Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
! if (pMonth != null) {
! // first day-of-month is likely to be safest for setting proleptic-month
! // cannot add to year zero, as not all chronologies have a year zero
! ChronoLocalDate<?> chronoDate = dateNow()
! .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
! addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
! addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
! }
!
! // invent era if necessary to resolve year-of-era
! Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
! if (yoeLong != null) {
! Long eraLong = fieldValues.remove(ERA);
! int yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
! if (eraLong != null) {
! Era eraObj = eraOf(Math.toIntExact(eraLong));
! addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
! } else if (fieldValues.containsKey(YEAR)) {
! int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
! ChronoLocalDate<?> chronoDate = dateYearDay(year, 1);
! addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
! } else {
! List<Era> eras = eras();
! if (eras.isEmpty()) {
! addFieldValue(fieldValues, YEAR, yoe);
! } else {
! Era eraObj = eras.get(eras.size() - 1);
! addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
! }
! }
! }
!
! // build date
! if (fieldValues.containsKey(YEAR)) {
! if (fieldValues.containsKey(MONTH_OF_YEAR)) {
! if (fieldValues.containsKey(DAY_OF_MONTH)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
! int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
! return date(y, moy, dom);
! }
! if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
! if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
! int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
! int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
! ChronoLocalDate<?> chronoDate = date(y, moy, 1);
! return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
! }
! if (fieldValues.containsKey(DAY_OF_WEEK)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
! int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
! int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
! ChronoLocalDate<?> chronoDate = date(y, moy, 1);
! return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
! }
! }
! }
! if (fieldValues.containsKey(DAY_OF_YEAR)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
! return dateYearDay(y, doy);
! }
! if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
! if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
! int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
! ChronoLocalDate<?> chronoDate = dateYearDay(y, 1);
! return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
! }
! if (fieldValues.containsKey(DAY_OF_WEEK)) {
! int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
! int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
! int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
! ChronoLocalDate<?> chronoDate = dateYearDay(y, 1);
! return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
! }
! }
! }
! return null;
! }
!
! /**
! * Adds a field-value pair to the map, checking for conflicts.
! * <p>
! * If the field is not already present, then the field-value pair is added to the map.
! * If the field is already present and it has the same value as that specified, no action occurs.
! * If the field is already present and it has a different value to that specified, then
! * an exception is thrown.
! *
! * @param field the field to add, not null
! * @param value the value to add, not null
! * @throws DateTimeException if the field is already present with a different value
! */
! void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
! Long old = fieldValues.get(field); // check first for better error message
! if (old != null && old.longValue() != value) {
! throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
! }
! fieldValues.put(field, value);
}
//-----------------------------------------------------------------------
/**
* Compares this chronology to another chronology.
*** 859,878 ****
return getId();
}
//-----------------------------------------------------------------------
/**
! * Writes the object using a
! * <a href="../../../serialized-form.html#java.time.temporal.Ser">dedicated serialized form</a>.
* <pre>
! * out.writeByte(7); // identifies this as a Chronology
! * out.writeUTF(chronoId);
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
! private Object writeReplace() {
return new Ser(Ser.CHRONO_TYPE, this);
}
/**
* Defend against malicious streams.
--- 1125,1144 ----
return getId();
}
//-----------------------------------------------------------------------
/**
! * Writes the Chronology using a
! * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
* <pre>
! * out.writeByte(1); // identifies this as a Chronology
! * out.writeUTF(getId());
* </pre>
*
* @return the instance of {@code Ser}, not null
*/
! protected Object writeReplace() {
return new Ser(Ser.CHRONO_TYPE, this);
}
/**
* Defend against malicious streams.