diff --git a/jdk/src/share/classes/java/time/chrono/AbstractChronology.java b/jdk/src/share/classes/java/time/chrono/AbstractChronology.java new file mode 100644 index 00000000000..89b728e88c9 --- /dev/null +++ b/jdk/src/share/classes/java/time/chrono/AbstractChronology.java @@ -0,0 +1,783 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * 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.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.WEEKS; +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.DateTimeException; +import java.time.DayOfWeek; +import java.time.format.ResolverStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +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; + +/** + * An abstract implementation of a calendar system, used to organize and identify dates. + *

+ * The main date and time API is built on the ISO calendar system. + * The chronology operates behind the scenes to represent the general concept of a calendar system. + *

+ * See {@link Chronology} for more details. + * + * @implSpec + * This class is separated from the {@code Chronology} interface so that the static methods + * are not inherited. While {@code Chronology} can be implemented directly, it is strongly + * recommended to extend this abstract class instead. + *

+ * 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. + * + * @since 1.8 + */ +public abstract class AbstractChronology implements Chronology { + + /** + * ChronoLocalDate order constant. + */ + static final Comparator DATE_ORDER = + (Comparator & Serializable) (date1, date2) -> { + return Long.compare(date1.toEpochDay(), date2.toEpochDay()); + }; + /** + * ChronoLocalDateTime order constant. + */ + static final Comparator> DATE_TIME_ORDER = + (Comparator> & 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> INSTANT_ORDER = + (Comparator> & 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 CHRONOS_BY_ID = new ConcurrentHashMap<>(); + /** + * Map of available calendars by calendar type. + */ + private static final ConcurrentHashMap CHRONOS_BY_TYPE = new ConcurrentHashMap<>(); + + /** + * Register a Chronology by its ID and type for lookup by {@link #of(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(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 + * of {@link java.time.chrono.AbstractChronology} found in the bootclass loader. + * The built-in chronologies are registered explicitly. + * Calendars configured via the Thread's context classloader are local + * to that thread and are ignored. + *

+ * The initialization is done only once using the registration + * of the IsoChronology as the test and the final step. + * Multiple threads may perform the initialization concurrently. + * Only the first registration of each Chronology is retained by the + * ConcurrentHashMap. + * @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 loader = ServiceLoader.load(AbstractChronology.class, null); + for (AbstractChronology 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; + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Chronology} from a locale. + *

+ * See {@link Chronology#ofLocale(Locale)}. + * + * @param locale the locale to use to obtain the calendar system, not null + * @return the calendar system associated with the locale, not null + * @throws java.time.DateTimeException if the locale-specified calendar cannot be found + */ + static Chronology ofLocale(Locale locale) { + 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()); + + // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader + // Application provided Chronologies must not be cached + @SuppressWarnings("rawtypes") + ServiceLoader 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. + *

+ * See {@link Chronology#of(String)}. + * + * @param id the chronology ID or calendar system type, not null + * @return the chronology with the identifier requested, not null + * @throws java.time.DateTimeException if the chronology cannot be found + */ + static Chronology of(String id) { + Objects.requireNonNull(id, "id"); + do { + Chronology chrono = of0(id); + 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 loader = ServiceLoader.load(Chronology.class); + for (Chronology chrono : loader) { + if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) { + return chrono; + } + } + throw new DateTimeException("Unknown chronology: " + id); + } + + /** + * Obtains an instance of {@code Chronology} from a chronology ID or + * calendar system type. + * + * @param id the chronology ID or calendar system type, not null + * @return the chronology with the identifier requested, or {@code null} if not found + */ + private static Chronology of0(String id) { + Chronology chrono = CHRONOS_BY_ID.get(id); + if (chrono == null) { + chrono = CHRONOS_BY_TYPE.get(id); + } + return chrono; + } + + /** + * Returns the available chronologies. + *

+ * Each returned {@code Chronology} is available for use in the system. + * The set of chronologies includes the system chronologies and + * any chronologies provided by the application via ServiceLoader + * configuration. + * + * @return the independent, modifiable set of the available chronology IDs, not null + */ + static Set getAvailableChronologies() { + initCache(); // force initialization + HashSet chronos = new HashSet<>(CHRONOS_BY_ID.values()); + + /// Add in Chronologies from the ServiceLoader configuration + @SuppressWarnings("rawtypes") + ServiceLoader loader = ServiceLoader.load(Chronology.class); + for (Chronology chrono : loader) { + chronos.add(chrono); + } + return chronos; + } + + //----------------------------------------------------------------------- + /** + * Creates an instance. + */ + protected AbstractChronology() { + } + + //----------------------------------------------------------------------- + /** + * Resolves parsed {@code ChronoField} values into a date during parsing. + *

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

+ * {@code ChronoField} instances are resolved by this method, which may + * be overridden in subclasses. + *

+ *

+ * The default implementation is suitable for most calendar systems. + * If {@link java.time.temporal.ChronoField#YEAR_OF_ERA} is found without an {@link java.time.temporal.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, that first day-of-year has the value 1, and that the + * first of the month and year always exists. + * + * @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 java.time.DateTimeException if the date cannot be resolved, typically + * because of a conflict in the input data + */ + @Override + public ChronoLocalDate resolveDate(Map 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 + resolveProlepticMonth(fieldValues, resolverStyle); + + // invent era if necessary to resolve year-of-era + ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle); + if (resolved != null) { + return resolved; + } + + // build date + if (fieldValues.containsKey(YEAR)) { + if (fieldValues.containsKey(MONTH_OF_YEAR)) { + if (fieldValues.containsKey(DAY_OF_MONTH)) { + return resolveYMD(fieldValues, resolverStyle); + } + if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) { + if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { + return resolveYMAA(fieldValues, resolverStyle); + } + if (fieldValues.containsKey(DAY_OF_WEEK)) { + return resolveYMAD(fieldValues, resolverStyle); + } + } + } + if (fieldValues.containsKey(DAY_OF_YEAR)) { + return resolveYD(fieldValues, resolverStyle); + } + if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) { + if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { + return resolveYAA(fieldValues, resolverStyle); + } + if (fieldValues.containsKey(DAY_OF_WEEK)) { + return resolveYAD(fieldValues, resolverStyle); + } + } + } + return null; + } + + void resolveProlepticMonth(Map fieldValues, ResolverStyle resolverStyle) { + Long pMonth = fieldValues.remove(PROLEPTIC_MONTH); + if (pMonth != null) { + if (resolverStyle != ResolverStyle.LENIENT) { + PROLEPTIC_MONTH.checkValidValue(pMonth); + } + // 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)); + } + } + + ChronoLocalDate resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) { + Long yoeLong = fieldValues.remove(YEAR_OF_ERA); + if (yoeLong != null) { + Long eraLong = fieldValues.remove(ERA); + int yoe; + if (resolverStyle != ResolverStyle.LENIENT) { + yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); + } else { + yoe = Math.toIntExact(yoeLong); + } + if (eraLong != null) { + Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); + 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 if (resolverStyle == ResolverStyle.STRICT) { + // do not invent era if strict + // reinstate the field removed earlier, no cross-check issues + fieldValues.put(YEAR_OF_ERA, yoeLong); + } else { + List eras = eras(); + if (eras.isEmpty()) { + addFieldValue(fieldValues, YEAR, yoe); + } else { + Era eraObj = eras.get(eras.size() - 1); + addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe)); + } + } + } + } else if (fieldValues.containsKey(ERA)) { + range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated + } + return null; + } + + ChronoLocalDate resolveYMD(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); + long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1); + return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS); + } + int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); + ValueRange domRange = range(DAY_OF_MONTH); + int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); + if (resolverStyle == ResolverStyle.SMART) { // previous valid + try { + return date(y, moy, dom); + } catch (DateTimeException ex) { + return date(y, moy, 1).with(TemporalAdjuster.lastDayOfMonth()); + } + } + return date(y, moy, dom); + } + + ChronoLocalDate resolveYD(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1); + return dateYearDay(y, 1).plus(days, DAYS); + } + int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR); + return dateYearDay(y, doy); // smart is same as strict + } + + ChronoLocalDate resolveYMAA(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); + long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); + long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1); + return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); + } + 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 date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); + if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { + throw new DateTimeException("Strict mode rejected resolved date as it is in a different month"); + } + return date; + } + + ChronoLocalDate resolveYMAD(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); + long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); + long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1); + return resolveAligned(date(y, 1, 1), months, weeks, dow); + } + 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 date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow))); + if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { + throw new DateTimeException("Strict mode rejected resolved date as it is in a different month"); + } + return date; + } + + ChronoLocalDate resolveYAA(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); + long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1); + return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS); + } + 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 date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); + if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { + throw new DateTimeException("Strict mode rejected resolved date as it is in a different year"); + } + return date; + } + + ChronoLocalDate resolveYAD(Map fieldValues, ResolverStyle resolverStyle) { + int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); + if (resolverStyle == ResolverStyle.LENIENT) { + long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); + long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1); + return resolveAligned(dateYearDay(y, 1), 0, weeks, dow); + } + 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 date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow))); + if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { + throw new DateTimeException("Strict mode rejected resolved date as it is in a different year"); + } + return date; + } + + ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) { + ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS); + if (dow > 7) { + date = date.plus((dow - 1) / 7, WEEKS); + dow = ((dow - 1) % 7) + 1; + } else if (dow < 1) { + date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS); + dow = ((dow + 6) % 7) + 1; + } + return date.with(nextOrSame(DayOfWeek.of((int) dow))); + } + + /** + * Adds a field-value pair to the map, checking for conflicts. + *

+ * 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 java.time.DateTimeException if the field is already present with a different value + */ + void addFieldValue(Map 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. + *

+ * The comparison order first by the chronology ID string, then by any + * additional information specific to the subclass. + * It is "consistent with equals", as defined by {@link Comparable}. + * + * @implSpec + * This implementation compares the chronology ID. + * Subclasses must compare any additional state that they store. + * + * @param other the other chronology to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(Chronology other) { + return getId().compareTo(other.getId()); + } + + /** + * Checks if this chronology is equal to another chronology. + *

+ * The comparison is based on the entire state of the object. + * + * @implSpec + * This implementation checks the type and calls + * {@link #compareTo(java.time.chrono.Chronology)}. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other chronology + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AbstractChronology) { + return compareTo((AbstractChronology) obj) == 0; + } + return false; + } + + /** + * A hash code for this chronology. + *

+ * The hash code should be based on the entire state of the object. + * + * @implSpec + * This implementation is based on the chronology ID and class. + * Subclasses should add any additional state that they store. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return getClass().hashCode() ^ getId().hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Outputs this chronology as a {@code String}, using the chronology ID. + * + * @return a string representation of this chronology, not null + */ + @Override + public String toString() { + return getId(); + } + + //----------------------------------------------------------------------- + /** + * Writes the Chronology using a + * dedicated serialized form. + *

+     *  out.writeByte(1);  // identifies this as a Chronology
+     *  out.writeUTF(getId());
+     * 
+ * + * @return the instance of {@code Ser}, not null + */ + Object writeReplace() { + return new Ser(Ser.CHRONO_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws java.io.InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeUTF(getId()); + } + + static Chronology readExternal(DataInput in) throws IOException { + String id = in.readUTF(); + return Chronology.of(id); + } + +} diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java index 58e4f5d5250..da5f0e719ce 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java @@ -262,7 +262,7 @@ public interface ChronoLocalDate * @see #isEqual */ static Comparator timeLineOrder() { - return Chronology.DATE_ORDER; + return AbstractChronology.DATE_ORDER; } //----------------------------------------------------------------------- diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java index 4c2ddfd31a7..353673c9dcd 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java @@ -136,7 +136,7 @@ public interface ChronoLocalDateTime * @see #isEqual */ static Comparator> timeLineOrder() { - return Chronology.DATE_TIME_ORDER; + return AbstractChronology.DATE_TIME_ORDER; } //----------------------------------------------------------------------- diff --git a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java index 033ab997fdd..caec8558e6e 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java @@ -137,7 +137,7 @@ public interface ChronoZonedDateTime * @see #isEqual */ static Comparator> timeLineOrder() { - return Chronology.INSTANT_ORDER; + return AbstractChronology.INSTANT_ORDER; } //----------------------------------------------------------------------- diff --git a/jdk/src/share/classes/java/time/chrono/Chronology.java b/jdk/src/share/classes/java/time/chrono/Chronology.java index ed40f4138f0..22a7e69ae25 100644 --- a/jdk/src/share/classes/java/time/chrono/Chronology.java +++ b/jdk/src/share/classes/java/time/chrono/Chronology.java @@ -61,33 +61,8 @@ */ 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.ChronoUnit.DAYS; -import static java.time.temporal.ChronoUnit.MONTHS; -import static java.time.temporal.ChronoUnit.WEEKS; -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; @@ -97,28 +72,21 @@ import java.time.format.ResolverStyle; import java.time.format.TextStyle; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; -import java.time.temporal.TemporalAdjuster; 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. *

* 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. + * The chronology operates behind the scenes to represent the general concept of a calendar system. * For example, the Japanese, Minguo, Thai Buddhist and others. *

* Most other calendar systems also operate on the shared concepts of year, month and day, @@ -179,130 +147,14 @@ import sun.util.logging.PlatformLogger; * CLDR type and, if applicable, the CLDR variant, * * @implSpec - * This class must be implemented with care to ensure other classes operate correctly. + * This interface 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. * * @since 1.8 */ -public abstract class Chronology implements Comparable { +public interface Chronology extends Comparable { - /** - * ChronoLocalDate order constant. - */ - static final Comparator DATE_ORDER = - (Comparator & Serializable) (date1, date2) -> { - return Long.compare(date1.toEpochDay(), date2.toEpochDay()); - }; - /** - * ChronoLocalDateTime order constant. - */ - static final Comparator> DATE_TIME_ORDER = - (Comparator> & 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> INSTANT_ORDER = - (Comparator> & 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 CHRONOS_BY_ID = new ConcurrentHashMap<>(); - /** - * Map of available calendars by calendar type. - */ - private static final ConcurrentHashMap 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 - * of {@link java.time.chrono.Chronology} found in the bootclass loader. - * The built-in chronologies are registered explicitly. - * Calendars configured via the Thread's context classloader are local - * to that thread and are ignored. - *

- * The initialization is done only once using the registration - * of the IsoChronology as the test and the final step. - * Multiple threads may perform the initialization concurrently. - * Only the first registration of each Chronology is retained by the - * ConcurrentHashMap. - * @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 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; - } - - //----------------------------------------------------------------------- /** * Obtains an instance of {@code Chronology} from a temporal object. *

@@ -320,7 +172,7 @@ public abstract class Chronology implements Comparable { * @return the chronology, not null * @throws DateTimeException if unable to convert to an {@code Chronology} */ - public static Chronology from(TemporalAccessor temporal) { + static Chronology from(TemporalAccessor temporal) { Objects.requireNonNull(temporal, "temporal"); Chronology obj = temporal.query(TemporalQuery.chronology()); return (obj != null ? obj : IsoChronology.INSTANCE); @@ -367,31 +219,8 @@ public abstract class Chronology implements Comparable { * @return the calendar system associated with the locale, not null * @throws DateTimeException if the locale-specified calendar cannot be found */ - public static Chronology ofLocale(Locale locale) { - 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()); - - // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader - // Application provided Chronologies must not be cached - @SuppressWarnings("rawtypes") - ServiceLoader loader = ServiceLoader.load(Chronology.class); - for (Chronology chrono : loader) { - if (type.equals(chrono.getCalendarType())) { - return chrono; - } - } - throw new DateTimeException("Unknown calendar system: " + type); + static Chronology ofLocale(Locale locale) { + return AbstractChronology.ofLocale(locale); } //----------------------------------------------------------------------- @@ -415,41 +244,8 @@ public abstract class Chronology implements Comparable { * @return the chronology with the identifier requested, not null * @throws DateTimeException if the chronology cannot be found */ - public static Chronology of(String id) { - Objects.requireNonNull(id, "id"); - do { - Chronology chrono = of0(id); - 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 loader = ServiceLoader.load(Chronology.class); - for (Chronology chrono : loader) { - if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) { - return chrono; - } - } - throw new DateTimeException("Unknown chronology: " + id); - } - - /** - * Obtains an instance of {@code Chronology} from a chronology ID or - * calendar system type. - * - * @param id the chronology ID or calendar system type, not null - * @return the chronology with the identifier requested, or {@code null} if not found - */ - private static Chronology of0(String id) { - Chronology chrono = CHRONOS_BY_ID.get(id); - if (chrono == null) { - chrono = CHRONOS_BY_TYPE.get(id); - } - return chrono; + static Chronology of(String id) { + return AbstractChronology.of(id); } /** @@ -462,24 +258,8 @@ public abstract class Chronology implements Comparable { * * @return the independent, modifiable set of the available chronology IDs, not null */ - public static Set getAvailableChronologies() { - initCache(); // force initialization - HashSet chronos = new HashSet<>(CHRONOS_BY_ID.values()); - - /// Add in Chronologies from the ServiceLoader configuration - @SuppressWarnings("rawtypes") - ServiceLoader loader = ServiceLoader.load(Chronology.class); - for (Chronology chrono : loader) { - chronos.add(chrono); - } - return chronos; - } - - //----------------------------------------------------------------------- - /** - * Creates an instance. - */ - protected Chronology() { + static Set getAvailableChronologies() { + return AbstractChronology.getAvailableChronologies(); } //----------------------------------------------------------------------- @@ -492,7 +272,7 @@ public abstract class Chronology implements Comparable { * @return the chronology ID, not null * @see #getCalendarType() */ - public abstract String getId(); + String getId(); /** * Gets the calendar type of the calendar system. @@ -507,13 +287,17 @@ public abstract class Chronology implements Comparable { * @return the calendar system type, null if the calendar is not defined by CLDR/LDML * @see #getId() */ - public abstract String getCalendarType(); + String getCalendarType(); //----------------------------------------------------------------------- /** * Obtains a local date in this chronology from the era, year-of-era, * month-of-year and day-of-month fields. * + * @implSpec + * The default implementation combines the era and year-of-era into a proleptic + * year before calling {@link #date(int, int, int)}. + * * @param era the era of the correct type for the chronology, not null * @param yearOfEra the chronology year-of-era * @param month the chronology month-of-year @@ -522,7 +306,7 @@ public abstract class Chronology implements Comparable { * @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) { + default ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) { return date(prolepticYear(era, yearOfEra), month, dayOfMonth); } @@ -536,12 +320,16 @@ public abstract class Chronology implements Comparable { * @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); + 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. * + * @implSpec + * The default implementation combines the era and year-of-era into a proleptic + * year before calling {@link #dateYearDay(int, int)}. + * * @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 @@ -549,7 +337,7 @@ public abstract class Chronology implements Comparable { * @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) { + default ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); } @@ -562,7 +350,7 @@ public abstract class Chronology implements Comparable { * @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); + ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear); /** * Obtains a local date in this chronology from the epoch-day. @@ -574,7 +362,7 @@ public abstract class Chronology implements Comparable { * @return the local date in this chronology, not null * @throws DateTimeException if unable to create the date */ - public abstract ChronoLocalDate dateEpochDay(long epochDay); + ChronoLocalDate dateEpochDay(long epochDay); //----------------------------------------------------------------------- /** @@ -585,13 +373,14 @@ public abstract class Chronology implements Comparable { *

* Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. - *

- * This implementation uses {@link #dateNow(Clock)}. + * + * @implSpec + * The default implementation invokes {@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() { + default ChronoLocalDate dateNow() { return dateNow(Clock.systemDefaultZone()); } @@ -604,11 +393,14 @@ public abstract class Chronology implements Comparable { * Using this method will prevent the ability to use an alternate clock for testing * because the clock is hard-coded. * + * @implSpec + * The default implementation invokes {@link #dateNow(Clock)}. + * * @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) { + default ChronoLocalDate dateNow(ZoneId zone) { return dateNow(Clock.system(zone)); } @@ -619,11 +411,14 @@ public abstract class Chronology implements Comparable { * Using this method allows the use of an alternate clock for testing. * The alternate clock may be introduced using {@link Clock dependency injection}. * + * @implSpec + * The default implementation invokes {@link #date(TemporalAccessor)} )}. + * * @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) { + default ChronoLocalDate dateNow(Clock clock) { Objects.requireNonNull(clock, "clock"); return date(LocalDate.now(clock)); } @@ -647,7 +442,7 @@ public abstract class Chronology implements Comparable { * @throws DateTimeException if unable to create the date * @see ChronoLocalDate#from(TemporalAccessor) */ - public abstract ChronoLocalDate date(TemporalAccessor temporal); + ChronoLocalDate date(TemporalAccessor temporal); /** * Obtains a local date-time in this chronology from another temporal object. @@ -670,7 +465,7 @@ public abstract class Chronology implements Comparable { * @throws DateTimeException if unable to create the date-time * @see ChronoLocalDateTime#from(TemporalAccessor) */ - public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { + default ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { try { return date(temporal).atTime(LocalTime.from(temporal)); } catch (DateTimeException ex) { @@ -702,7 +497,7 @@ public abstract class Chronology implements Comparable { * @throws DateTimeException if unable to create the date-time * @see ChronoZonedDateTime#from(TemporalAccessor) */ - public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { + default ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { try { ZoneId zone = ZoneId.from(temporal); try { @@ -728,7 +523,7 @@ public abstract class Chronology implements Comparable { * @return the zoned date-time, not null * @throws DateTimeException if the result exceeds the supported range */ - public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { + default ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone); } @@ -746,7 +541,7 @@ public abstract class Chronology implements Comparable { * @param prolepticYear the proleptic-year to check, not validated for range * @return true if the year is a leap year */ - public abstract boolean isLeapYear(long prolepticYear); + boolean isLeapYear(long prolepticYear); /** * Calculates the proleptic-year given the era and year-of-era. @@ -764,7 +559,7 @@ public abstract class Chronology implements Comparable { * 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); + int prolepticYear(Era era, int yearOfEra); /** * Creates the chronology era object from the numeric value. @@ -785,7 +580,7 @@ public abstract class Chronology implements Comparable { * @return the calendar system era, not null * @throws DateTimeException if unable to create the era */ - public abstract Era eraOf(int eraValue); + Era eraOf(int eraValue); /** * Gets the list of eras for the chronology. @@ -796,7 +591,7 @@ public abstract class Chronology implements Comparable { * * @return the list of eras for the chronology, may be immutable, not null */ - public abstract List eras(); + List eras(); //----------------------------------------------------------------------- /** @@ -815,7 +610,7 @@ public abstract class Chronology implements Comparable { * @return the range of valid values for the field, not null * @throws DateTimeException if the range for the field cannot be obtained */ - public abstract ValueRange range(ChronoField field); + ValueRange range(ChronoField field); //----------------------------------------------------------------------- /** @@ -825,28 +620,16 @@ public abstract class Chronology implements Comparable { * suitable for presentation to the user. * The parameters control the style of the returned text and the locale. * + * @implSpec + * The default implementation behaves as the the formatter was used to + * format the chronology textual name. + * * @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}. - *

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

- * 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() { + default String getDisplayName(TextStyle style, Locale locale) { + TemporalAccessor temporal = new TemporalAccessor() { @Override public boolean isSupported(TemporalField field) { return false; @@ -864,6 +647,7 @@ public abstract class Chronology implements Comparable { return TemporalAccessor.super.query(query); } }; + return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(temporal); } //----------------------------------------------------------------------- @@ -876,82 +660,8 @@ public abstract class Chronology implements Comparable { * As such, {@code ChronoField} date fields are resolved here in the * context of a specific chronology. *

- * {@code ChronoField} instances are resolved by this method, which may - * be overridden in subclasses. - *

    - *
  • {@code EPOCH_DAY} - If present, this is converted to a date and - * all other date fields are then cross-checked against the date. - *
  • {@code PROLEPTIC_MONTH} - If present, then it is split into the - * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart - * then the field is validated. - *
  • {@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they - * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA} - * range is not validated, in smart and strict mode it is. The {@code ERA} is - * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is - * present, and the mode is smart or lenient, then the last available era - * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is - * left untouched. If only the {@code ERA} is present, then it is left untouched. - *
  • {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} - - * If all three are present, then they are combined to form a date. - * In all three modes, the {@code YEAR} is validated. - * If the mode is smart or strict, then the month and day are validated. - * If the mode is lenient, then the date is combined in a manner equivalent to - * creating a date on the first day of the first month in the requested year, - * then adding the difference in months, then the difference in days. - * If the mode is smart, and the day-of-month is greater than the maximum for - * the year-month, then the day-of-month is adjusted to the last day-of-month. - * If the mode is strict, then the three fields must form a valid date. - *
  • {@code YEAR} and {@code DAY_OF_YEAR} - - * If both are present, then they are combined to form a date. - * In all three modes, the {@code YEAR} is validated. - * If the mode is lenient, then the date is combined in a manner equivalent to - * creating a date on the first day of the requested year, then adding - * the difference in days. - * If the mode is smart or strict, then the two fields must form a valid date. - *
  • {@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and - * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} - - * If all four are present, then they are combined to form a date. - * In all three modes, the {@code YEAR} is validated. - * If the mode is lenient, then the date is combined in a manner equivalent to - * creating a date on the first day of the first month in the requested year, then adding - * the difference in months, then the difference in weeks, then in days. - * If the mode is smart or strict, then the all four fields are validated to - * their outer ranges. The date is then combined in a manner equivalent to - * creating a date on the first day of the requested year and month, then adding - * the amount in weeks and days to reach their values. If the mode is strict, - * the date is additionally validated to check that the day and week adjustment - * did not change the month. - *
  • {@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and - * {@code DAY_OF_WEEK} - If all four are present, then they are combined to - * form a date. The approach is the same as described above for - * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}. - * The day-of-week is adjusted as the next or same matching day-of-week once - * the years, months and weeks have been handled. - *
  • {@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} - - * If all three are present, then they are combined to form a date. - * In all three modes, the {@code YEAR} is validated. - * If the mode is lenient, then the date is combined in a manner equivalent to - * creating a date on the first day of the requested year, then adding - * the difference in weeks, then in days. - * If the mode is smart or strict, then the all three fields are validated to - * their outer ranges. The date is then combined in a manner equivalent to - * creating a date on the first day of the requested year, then adding - * the amount in weeks and days to reach their values. If the mode is strict, - * the date is additionally validated to check that the day and week adjustment - * did not change the year. - *
  • {@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} - - * If all three are present, then they are combined to form a date. - * The approach is the same as described above for years and weeks in - * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the - * next or same matching day-of-week once the years and weeks have been handled. - *
- *

- * 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, that first day-of-year has the value 1, and that the - * first of the month and year always exists. + * The default implementation, which explains typical resolve behaviour, + * is provided in {@link AbstractChronology}. * * @param fieldValues the map of fields to values, which can be updated, not null * @param resolverStyle the requested type of resolve, not null @@ -959,233 +669,7 @@ public abstract class Chronology implements Comparable { * @throws DateTimeException if the date cannot be resolved, typically * because of a conflict in the input data */ - public ChronoLocalDate resolveDate(Map 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 - resolveProlepticMonth(fieldValues, resolverStyle); - - // invent era if necessary to resolve year-of-era - ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle); - if (resolved != null) { - return resolved; - } - - // build date - if (fieldValues.containsKey(YEAR)) { - if (fieldValues.containsKey(MONTH_OF_YEAR)) { - if (fieldValues.containsKey(DAY_OF_MONTH)) { - return resolveYMD(fieldValues, resolverStyle); - } - if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) { - if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) { - return resolveYMAA(fieldValues, resolverStyle); - } - if (fieldValues.containsKey(DAY_OF_WEEK)) { - return resolveYMAD(fieldValues, resolverStyle); - } - } - } - if (fieldValues.containsKey(DAY_OF_YEAR)) { - return resolveYD(fieldValues, resolverStyle); - } - if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) { - if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) { - return resolveYAA(fieldValues, resolverStyle); - } - if (fieldValues.containsKey(DAY_OF_WEEK)) { - return resolveYAD(fieldValues, resolverStyle); - } - } - } - return null; - } - - void resolveProlepticMonth(Map fieldValues, ResolverStyle resolverStyle) { - Long pMonth = fieldValues.remove(PROLEPTIC_MONTH); - if (pMonth != null) { - if (resolverStyle != ResolverStyle.LENIENT) { - PROLEPTIC_MONTH.checkValidValue(pMonth); - } - // 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)); - } - } - - ChronoLocalDate resolveYearOfEra(Map fieldValues, ResolverStyle resolverStyle) { - Long yoeLong = fieldValues.remove(YEAR_OF_ERA); - if (yoeLong != null) { - Long eraLong = fieldValues.remove(ERA); - int yoe; - if (resolverStyle != ResolverStyle.LENIENT) { - yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA); - } else { - yoe = Math.toIntExact(yoeLong); - } - if (eraLong != null) { - Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA)); - 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 if (resolverStyle == ResolverStyle.STRICT) { - // do not invent era if strict - // reinstate the field removed earlier, no cross-check issues - fieldValues.put(YEAR_OF_ERA, yoeLong); - } else { - List eras = eras(); - if (eras.isEmpty()) { - addFieldValue(fieldValues, YEAR, yoe); - } else { - Era eraObj = eras.get(eras.size() - 1); - addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe)); - } - } - } - } else if (fieldValues.containsKey(ERA)) { - range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated - } - return null; - } - - ChronoLocalDate resolveYMD(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); - long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1); - return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS); - } - int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR); - ValueRange domRange = range(DAY_OF_MONTH); - int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH); - if (resolverStyle == ResolverStyle.SMART) { // previous valid - try { - return date(y, moy, dom); - } catch (DateTimeException ex) { - return date(y, moy, 1).with(TemporalAdjuster.lastDayOfMonth()); - } - } - return date(y, moy, dom); - } - - ChronoLocalDate resolveYD(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1); - return dateYearDay(y, 1).plus(days, DAYS); - } - int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR); - return dateYearDay(y, doy); // smart is same as strict - } - - ChronoLocalDate resolveYMAA(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); - long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); - long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1); - return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS); - } - 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 date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); - if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { - throw new DateTimeException("Strict mode rejected resolved date as it is in a different month"); - } - return date; - } - - ChronoLocalDate resolveYMAD(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1); - long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1); - long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1); - return resolveAligned(date(y, 1, 1), months, weeks, dow); - } - 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 date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow))); - if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) { - throw new DateTimeException("Strict mode rejected resolved date as it is in a different month"); - } - return date; - } - - ChronoLocalDate resolveYAA(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); - long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1); - return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS); - } - 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 date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS); - if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { - throw new DateTimeException("Strict mode rejected resolved date as it is in a different year"); - } - return date; - } - - ChronoLocalDate resolveYAD(Map fieldValues, ResolverStyle resolverStyle) { - int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR); - if (resolverStyle == ResolverStyle.LENIENT) { - long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1); - long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1); - return resolveAligned(dateYearDay(y, 1), 0, weeks, dow); - } - 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 date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow))); - if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) { - throw new DateTimeException("Strict mode rejected resolved date as it is in a different year"); - } - return date; - } - - ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) { - ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS); - if (dow > 7) { - date = date.plus((dow - 1) / 7, WEEKS); - dow = ((dow - 1) % 7) + 1; - } else if (dow < 1) { - date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS); - dow = ((dow + 6) % 7) + 1; - } - return date.with(nextOrSame(DayOfWeek.of((int) dow))); - } - - /** - * Adds a field-value pair to the map, checking for conflicts. - *

- * 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 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); - } + ChronoLocalDate resolveDate(Map fieldValues, ResolverStyle resolverStyle); //----------------------------------------------------------------------- /** @@ -1215,7 +699,7 @@ public abstract class Chronology implements Comparable { * @param days the number of years, may be negative * @return the period in terms of this chronology, not null */ - public ChronoPeriod period(int years, int months, int days) { + default ChronoPeriod period(int years, int months, int days) { return new ChronoPeriodImpl(this, years, months, days); } @@ -1226,107 +710,43 @@ public abstract class Chronology implements Comparable { * The comparison order first by the chronology ID string, then by any * additional information specific to the subclass. * It is "consistent with equals", as defined by {@link Comparable}. - *

- * The default implementation compares the chronology ID. - * Subclasses must compare any additional state that they store. * * @param other the other chronology to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override - public int compareTo(Chronology other) { - return getId().compareTo(other.getId()); - } + int compareTo(Chronology other); /** * Checks if this chronology is equal to another chronology. *

* The comparison is based on the entire state of the object. - *

- * The default implementation checks the type and calls {@link #compareTo(Chronology)}. * * @param obj the object to check, null returns false * @return true if this is equal to the other chronology */ @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof Chronology) { - return compareTo((Chronology) obj) == 0; - } - return false; - } + boolean equals(Object obj); /** * A hash code for this chronology. *

- * The default implementation is based on the ID and class. - * Subclasses should add any additional state that they store. + * The hash code should be based on the entire state of the object. * * @return a suitable hash code */ @Override - public int hashCode() { - return getClass().hashCode() ^ getId().hashCode(); - } + int hashCode(); //----------------------------------------------------------------------- /** - * Outputs this chronology as a {@code String}, using the ID. + * Outputs this chronology as a {@code String}. + *

+ * The format should include the entire state of the object. * * @return a string representation of this chronology, not null */ @Override - public String toString() { - return getId(); - } - - //----------------------------------------------------------------------- - /** - * Writes the Chronology using a - * dedicated serialized form. - * @serialData - *

-     *  out.writeByte(1);  // identifies a Chronology
-     *  out.writeUTF(getId());
-     * 
- * - * @return the instance of {@code Ser}, not null - */ - Object writeReplace() { - return new Ser(Ser.CHRONO_TYPE, this); - } - - /** - * Defend against malicious streams. - * @return never - * @throws InvalidObjectException always - */ - private Object readResolve() throws InvalidObjectException { - throw new InvalidObjectException("Deserialization via serialization delegate"); - } - - /** - * Write the Chronology id to the stream. - * @param out the output stream - * @throws IOException on any error during the write - */ - void writeExternal(DataOutput out) throws IOException { - out.writeUTF(getId()); - } - - /** - * Reads the Chronology id and creates the Chronology. - * @param in the input stream - * @return the Chronology - * @throws IOException on errors during the read - * @throws DateTimeException if the Chronology cannot be returned - */ - static Chronology readExternal(DataInput in) throws IOException { - String id = in.readUTF(); - return Chronology.of(id); - } + String toString(); } diff --git a/jdk/src/share/classes/java/time/chrono/HijrahChronology.java b/jdk/src/share/classes/java/time/chrono/HijrahChronology.java index 7d4485809c9..277b1010fe4 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahChronology.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahChronology.java @@ -214,7 +214,7 @@ import sun.util.logging.PlatformLogger; * * @since 1.8 */ -public final class HijrahChronology extends Chronology implements Serializable { +public final class HijrahChronology extends AbstractChronology implements Serializable { /** * The Hijrah Calendar id. @@ -308,8 +308,8 @@ public final class HijrahChronology extends Chronology implements Serializable { try { INSTANCE = new HijrahChronology("Hijrah-umalqura"); // Register it by its aliases - Chronology.registerChrono(INSTANCE, "Hijrah"); - Chronology.registerChrono(INSTANCE, "islamic"); + AbstractChronology.registerChrono(INSTANCE, "Hijrah"); + AbstractChronology.registerChrono(INSTANCE, "islamic"); } catch (DateTimeException ex) { // Absence of Hijrah calendar is fatal to initializing this class. PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono"); @@ -336,7 +336,7 @@ public final class HijrahChronology extends Chronology implements Serializable { try { // Create and register the variant HijrahChronology chrono = new HijrahChronology(id); - Chronology.registerChrono(chrono); + AbstractChronology.registerChrono(chrono); } catch (DateTimeException ex) { // Log error and continue PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono"); diff --git a/jdk/src/share/classes/java/time/chrono/IsoChronology.java b/jdk/src/share/classes/java/time/chrono/IsoChronology.java index 9638f6eea4f..eb71a1fe65c 100644 --- a/jdk/src/share/classes/java/time/chrono/IsoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/IsoChronology.java @@ -120,7 +120,7 @@ import java.util.Objects; * * @since 1.8 */ -public final class IsoChronology extends Chronology implements Serializable { +public final class IsoChronology extends AbstractChronology implements Serializable { /** * Singleton instance of the ISO chronology. diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java index b01707e63ab..09e5b692ef2 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java @@ -120,7 +120,7 @@ import sun.util.calendar.LocalGregorianCalendar; * * @since 1.8 */ -public final class JapaneseChronology extends Chronology implements Serializable { +public final class JapaneseChronology extends AbstractChronology implements Serializable { static final LocalGregorianCalendar JCAL = (LocalGregorianCalendar) CalendarSystem.forName("japanese"); diff --git a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java index db75a8645d6..af0be986c93 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java @@ -105,7 +105,7 @@ import java.util.Map; * * @since 1.8 */ -public final class MinguoChronology extends Chronology implements Serializable { +public final class MinguoChronology extends AbstractChronology implements Serializable { /** * Singleton instance for the Minguo chronology. diff --git a/jdk/src/share/classes/java/time/chrono/Ser.java b/jdk/src/share/classes/java/time/chrono/Ser.java index 5a4e3c12623..317a7696497 100644 --- a/jdk/src/share/classes/java/time/chrono/Ser.java +++ b/jdk/src/share/classes/java/time/chrono/Ser.java @@ -161,7 +161,7 @@ final class Ser implements Externalizable { out.writeByte(type); switch (type) { case CHRONO_TYPE: - ((Chronology) object).writeExternal(out); + ((AbstractChronology) object).writeExternal(out); break; case CHRONO_LOCAL_DATE_TIME_TYPE: ((ChronoLocalDateTimeImpl) object).writeExternal(out); @@ -231,7 +231,7 @@ final class Ser implements Externalizable { private static Object readInternal(byte type, ObjectInput in) throws IOException, ClassNotFoundException { switch (type) { - case CHRONO_TYPE: return Chronology.readExternal(in); + case CHRONO_TYPE: return AbstractChronology.readExternal(in); case CHRONO_LOCAL_DATE_TIME_TYPE: return ChronoLocalDateTimeImpl.readExternal(in); case CHRONO_ZONE_DATE_TIME_TYPE: return ChronoZonedDateTimeImpl.readExternal(in); case JAPANESE_DATE_TYPE: return JapaneseDate.readExternal(in); diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java index 4ffd15bfcc6..c41aa34c0f1 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java @@ -106,7 +106,7 @@ import java.util.Map; * * @since 1.8 */ -public final class ThaiBuddhistChronology extends Chronology implements Serializable { +public final class ThaiBuddhistChronology extends AbstractChronology implements Serializable { /** * Singleton instance of the Buddhist chronology. diff --git a/jdk/test/java/time/tck/java/time/chrono/CopticChronology.java b/jdk/test/java/time/tck/java/time/chrono/CopticChronology.java index c111eed3658..c72b7370de8 100644 --- a/jdk/test/java/time/tck/java/time/chrono/CopticChronology.java +++ b/jdk/test/java/time/tck/java/time/chrono/CopticChronology.java @@ -59,13 +59,11 @@ package tck.java.time.chrono; import static java.time.temporal.ChronoField.EPOCH_DAY; import java.io.Serializable; - +import java.time.chrono.AbstractChronology; +import java.time.chrono.Era; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; import java.time.temporal.ValueRange; -import java.time.chrono.Chronology; -import java.time.chrono.Era; - import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -95,7 +93,7 @@ import java.util.Locale; *

Implementation notes

* This class is immutable and thread-safe. */ -public final class CopticChronology extends Chronology implements Serializable { +public final class CopticChronology extends AbstractChronology implements Serializable { /** * Singleton instance of the Coptic chronology.