001/*
002 * Copyright (c) 2007-2013, Stephen Colebourne & Michael Nascimento Santos
003 *
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 *  * Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 *  * Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 *  * Neither the name of JSR-310 nor the names of its contributors
017 *    may be used to endorse or promote products derived from this software
018 *    without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
021 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
022 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
023 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
027 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
028 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
029 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032package org.threeten.bp.temporal;
033
034import java.io.DataInput;
035import java.io.DataOutput;
036import java.io.IOException;
037import java.io.InvalidObjectException;
038import java.io.ObjectStreamException;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Locale;
042import java.util.Objects;
043import java.util.ServiceLoader;
044import java.util.Set;
045import java.util.concurrent.ConcurrentHashMap;
046
047import org.threeten.bp.Clock;
048import org.threeten.bp.DateTimeException;
049import org.threeten.bp.Instant;
050import org.threeten.bp.LocalDate;
051import org.threeten.bp.LocalTime;
052import org.threeten.bp.ZoneId;
053import org.threeten.bp.format.DateTimeFormatterBuilder;
054import org.threeten.bp.format.TextStyle;
055import org.threeten.bp.jdk8.DefaultInterfaceTemporalAccessor;
056
057/**
058 * A calendar system, used to organize and identify dates.
059 * <p>
060 * The main date and time API is built on the ISO calendar system.
061 * This class operates behind the scenes to represent the general concept of a calendar system.
062 * For example, the Japanese, Minguo, Thai Buddhist and others.
063 * <p>
064 * Most other calendar systems also operate on the shared concepts of year, month and day,
065 * linked to the cycles of the Earth around the Sun, and the Moon around the Earth.
066 * These shared concepts are defined by {@link ChronoField} and are availalbe
067 * for use by any {@code Chrono} implementation:
068 * <pre>
069 *   LocalDate isoDate = ...
070 *   ChronoLocalDate&lt;ThaiBuddhistChrono&gt; minguoDate = ...
071 *   int isoYear = isoDate.get(ChronoField.YEAR);
072 *   int thaiYear = thaiDate.get(ChronoField.YEAR);
073 * </pre>
074 * As shown, although the date objects are in different calendar systems, represented by different
075 * {@code Chrono} instances, both can be queried using the same constant on {@code ChronoField}.
076 * For a full discussion of the implications of this, see {@link ChronoLocalDate}.
077 * In general, the advice is to use the known ISO-based {@code LocalDate}, rather than
078 * {@code ChronoLocalDate}.
079 * <p>
080 * While a {@code Chrono} object typically uses {@code ChronoField} and is based on
081 * an era, year-of-era, month-of-year, day-of-month model of a date, this is not required.
082 * A {@code Chrono} instance may represent a totally different kind of calendar system,
083 * such as the Mayan.
084 * <p>
085 * In practical terms, the {@code Chrono} instance also acts as a factory.
086 * The {@link #of(String)} method allows an instance to be looked up by identifier,
087 * while the {@link #ofLocale(Locale)} method allows lookup by locale.
088 * <p>
089 * The {@code Chrono} instance provides a set of methods to create {@code ChronoLocalDate} instances.
090 * The date classes are used to manipulate specific dates.
091 * <p><ul>
092 * <li> {@link #dateNow() dateNow()}
093 * <li> {@link #dateNow(Clock) dateNow(clock)}
094 * <li> {@link #dateNow(ZoneId) dateNow(zone)}
095 * <li> {@link #date(int, int, int) date(yearProleptic, month, day)}
096 * <li> {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)}
097 * <li> {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)}
098 * <li> {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)}
099 * <li> {@link #date(TemporalAccessor) date(TemporalAccessor)}
100 * </ul><p>
101 *
102 * <p id="addcalendars">Adding New Calendars</p>
103 * The set of available chronologies can be extended by applications.
104 * Adding a new calendar system requires the writing of an implementation of
105 * {@code Chrono}, {@code ChronoLocalDate} and {@code Era}.
106 * The majority of the logic specific to the calendar system will be in
107 * {@code ChronoLocalDate}. The {@code Chrono} subclass acts as a factory.
108 * <p>
109 * To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader}
110 * is used. A file must be added to the {@code META-INF/services} directory with the
111 * name 'org.threeten.bp.chrono.Chrono' listing the implementation classes.
112 * See the ServiceLoader for more details on service loading.
113 * For lookup by id or calendarType, the system provided calendars are found
114 * first followed by application provided calendars.
115 * <p>
116 * Each chronology must define a chronology ID that is unique within the system.
117 * If the chronology represents a calendar system defined by the
118 * <em>Unicode Locale Data Markup Language (LDML)</em> specification then that
119 * calendar type should also be specified.
120 *
121 * <h3>Specification for implementors</h3>
122 * This class must be implemented with care to ensure other classes operate correctly.
123 * All implementations that can be instantiated must be final, immutable and thread-safe.
124 * Subclasses should be Serializable wherever possible.
125 *
126 * @param <C> the type of the implementing subclass
127 */
128public abstract class Chrono<C extends Chrono<C>> implements Comparable<Chrono<?>> {
129
130    /**
131     * Map of available calendars by ID.
132     */
133    private static final ConcurrentHashMap<String, Chrono<?>> CHRONOS_BY_ID;
134    /**
135     * Map of available calendars by calendar type.
136     */
137    private static final ConcurrentHashMap<String, Chrono<?>> CHRONOS_BY_TYPE;
138    static {
139        // TODO: defer initialization?
140        ConcurrentHashMap<String, Chrono<?>> ids = new ConcurrentHashMap<>();
141        ConcurrentHashMap<String, Chrono<?>> types = new ConcurrentHashMap<>();
142        @SuppressWarnings("rawtypes")
143        ServiceLoader<Chrono> loader =  ServiceLoader.load(Chrono.class);
144        for (Chrono<?> chrono : loader) {
145            ids.putIfAbsent(chrono.getId(), chrono);
146            String type = chrono.getCalendarType();
147            if (type != null) {
148                types.putIfAbsent(type, chrono);
149            }
150        }
151        CHRONOS_BY_ID = ids;
152        CHRONOS_BY_TYPE = types;
153    }
154
155    //-----------------------------------------------------------------------
156    /**
157     * Obtains an instance of {@code Chrono} from a temporal object.
158     * <p>
159     * A {@code TemporalAccessor} represents some form of date and time information.
160     * This factory converts the arbitrary temporal object to an instance of {@code Chrono}.
161     * If the specified temporal object does not have a chronology, {@link ISOChrono} is returned.
162     * <p>
163     * The conversion will obtain the chronology using {@link TemporalQueries#chrono()}.
164     * <p>
165     * This method matches the signature of the functional interface {@link TemporalQuery}
166     * allowing it to be used in queries via method reference, {@code Chrono::from}.
167     *
168     * @param temporal  the temporal to convert, not null
169     * @return the chronology, not null
170     * @throws DateTimeException if unable to convert to an {@code Chrono}
171     */
172    public static Chrono<?> from(TemporalAccessor temporal) {
173        Objects.requireNonNull(temporal, "temporal");
174        Chrono<?> obj = temporal.query(TemporalQueries.CHRONO);
175        return (obj != null ? obj : ISOChrono.INSTANCE);
176    }
177
178    //-----------------------------------------------------------------------
179    /**
180     * Obtains an instance of {@code Chrono} from a locale.
181     * <p>
182     * The locale can be used to identify a calendar.
183     * This uses {@link Locale#getUnicodeLocaleType(String)} to obtain the "ca" key
184     * to identify the calendar system.
185     * <p>
186     * If the locale does not contain calendar system information, the standard
187     * ISO calendar system is used.
188     *
189     * @param locale  the locale to use to obtain the calendar system, not null
190     * @return the calendar system associated with the locale, not null
191     * @throws DateTimeException if the locale-specified calendar cannot be found
192     */
193    public static Chrono<?> ofLocale(Locale locale) {
194        Objects.requireNonNull(locale, "locale");
195        String type = locale.getUnicodeLocaleType("ca");
196        if (type == null) {
197            return ISOChrono.INSTANCE;
198        } else if ("iso".equals(type) || "iso8601".equals(type)) {
199            return ISOChrono.INSTANCE;
200        } else {
201            Chrono<?> chrono = CHRONOS_BY_TYPE.get(type);
202            if (chrono == null) {
203                throw new DateTimeException("Unknown calendar system: " + type);
204            }
205            return chrono;
206        }
207    }
208
209    //-----------------------------------------------------------------------
210    /**
211     * Obtains an instance of {@code Chrono} from a chronology ID or
212     * calendar system type.
213     * <p>
214     * This returns a chronology based on either the ID or the type.
215     * The {@link #getId() chronology ID} uniquely identifies the chronology.
216     * The {@link #getCalendarType() calendar system type} is defined by the LDML specification.
217     * <p>
218     * The chronology may be a system chronology or a chronology
219     * provided by the application via ServiceLoader configuration.
220     * <p>
221     * Since some calendars can be customized, the ID or type typically refers
222     * to the default customization. For example, the Gregorian calendar can have multiple
223     * cutover dates from the Julian, but the lookup only provides the default cutover date.
224     *
225     * @param id  the chronology ID or calendar system type, not null
226     * @return the chronology with the identifier requested, not null
227     * @throws DateTimeException if the chronology cannot be found
228     */
229    public static Chrono<?> of(String id) {
230        Chrono<?> chrono = CHRONOS_BY_ID.get(id);
231        if (chrono != null) {
232            return chrono;
233        }
234        chrono = CHRONOS_BY_TYPE.get(id);
235        if (chrono != null) {
236            return chrono;
237        }
238        throw new DateTimeException("Unknown chronology: " + id);
239    }
240
241    /**
242     * Returns the available chronologies.
243     * <p>
244     * Each returned {@code Chrono} is available for use in the system.
245     *
246     * @return the independent, modifiable set of the available chronology IDs, not null
247     */
248    public static Set<Chrono<?>> getAvailableChronologies() {
249        return new HashSet<>(CHRONOS_BY_ID.values());
250    }
251
252    //-----------------------------------------------------------------------
253    /**
254     * Obtains a local date-time from the a date and time.
255     * <p>
256     * This combines a {@link ChronoLocalDate}, which provides the {@code Chrono},
257     * with a {@link LocalTime} to produce a {@link ChronoLocalDateTime}.
258     * <p>
259     * This method is intended for chronology implementations.
260     * It uses a standard implementation that is shared for all chronologies.
261     *
262     * @param <R>  the chronology of the date
263     * @param date  the date, not null
264     * @param time  the time, not null
265     * @return the local date-time combining the input date and time, not null
266     */
267    public static <R extends Chrono<R>> ChronoLocalDateTime<R> dateTime(ChronoLocalDate<R> date, LocalTime time) {
268        return ChronoLocalDateTimeImpl.of(date, time);
269    }
270
271    //-----------------------------------------------------------------------
272    /**
273     * Creates an instance.
274     */
275    protected Chrono() {
276        // register the subclass
277        CHRONOS_BY_ID.putIfAbsent(this.getId(), this);
278        String type = this.getCalendarType();
279        if (type != null) {
280            CHRONOS_BY_TYPE.putIfAbsent(type, this);
281        }
282    }
283
284    //-----------------------------------------------------------------------
285    /**
286     * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology.
287     *
288     * @param temporal  a date-time to cast, not null
289     * @return the date-time checked and cast to {@code ChronoLocalDate}, not null
290     * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
291     *  or the chronology is not equal this Chrono
292     */
293    public /* should be package-scoped */ ChronoLocalDate<C> ensureChronoLocalDate(Temporal temporal) {
294        @SuppressWarnings("unchecked")
295        ChronoLocalDate<C> other = (ChronoLocalDate<C>) temporal;
296        if (this.equals(other.getChrono()) == false) {
297            throw new ClassCastException("Chrono mismatch, expected: " + getId() + ", actual: " + other.getChrono().getId());
298        }
299        return other;
300    }
301
302    /**
303     * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology.
304     *
305     * @param temporal   a date-time to cast, not null
306     * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
307     * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
308     *  or the chronology is not equal this Chrono
309     */
310    public /* should be package-scoped */ ChronoLocalDateTimeImpl<C> ensureChronoLocalDateTime(Temporal temporal) {
311        @SuppressWarnings("unchecked")
312        ChronoLocalDateTimeImpl<C> other = (ChronoLocalDateTimeImpl<C>) temporal;
313        if (this.equals(other.getDate().getChrono()) == false) {
314            throw new ClassCastException("Chrono mismatch, required: " + getId()
315                    + ", supplied: " + other.getDate().getChrono().getId());
316        }
317        return other;
318    }
319
320    /**
321     * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology.
322     *
323     * @param temporal  a date-time to cast, not null
324     * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
325     * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
326     *  or the chronology is not equal this Chrono
327     */
328    public /* should be package-scoped */ ChronoZonedDateTimeImpl<C> ensureChronoZonedDateTime(Temporal temporal) {
329        @SuppressWarnings("unchecked")
330        ChronoZonedDateTimeImpl<C> other = (ChronoZonedDateTimeImpl<C>) temporal;
331        if (this.equals(other.getDate().getChrono()) == false) {
332            throw new ClassCastException("Chrono mismatch, required: " + getId()
333                    + ", supplied: " + other.getDate().getChrono().getId());
334        }
335        return other;
336    }
337
338    //-----------------------------------------------------------------------
339    /**
340     * Gets the ID of the chronology.
341     * <p>
342     * The ID uniquely identifies the {@code Chrono}.
343     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
344     *
345     * @return the chronology ID, not null
346     * @see #getCalendarType()
347     */
348    public abstract String getId();
349
350    /**
351     * Gets the calendar type of the underlying calendar system.
352     * <p>
353     * The calendar type is an identifier defined by the
354     * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
355     * It can be used to lookup the {@code Chrono} using {@link #of(String)}.
356     * It can also be used as part of a locale, accessible via
357     * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
358     *
359     * @return the calendar system type, null if the calendar is not defined by LDML
360     * @see #getId()
361     */
362    public abstract String getCalendarType();
363
364    //-----------------------------------------------------------------------
365    /**
366     * Obtains a local date in this chronology from the era, year-of-era,
367     * month-of-year and day-of-month fields.
368     *
369     * @param era  the era of the correct type for the chronology, not null
370     * @param yearOfEra  the chronology year-of-era
371     * @param month  the chronology month-of-year
372     * @param dayOfMonth  the chronology day-of-month
373     * @return the local date in this chronology, not null
374     * @throws DateTimeException if unable to create the date
375     */
376    public ChronoLocalDate<C> date(Era<C> era, int yearOfEra, int month, int dayOfMonth) {
377        return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
378    }
379
380    /**
381     * Obtains a local date in this chronology from the proleptic-year,
382     * month-of-year and day-of-month fields.
383     *
384     * @param prolepticYear  the chronology proleptic-year
385     * @param month  the chronology month-of-year
386     * @param dayOfMonth  the chronology day-of-month
387     * @return the local date in this chronology, not null
388     * @throws DateTimeException if unable to create the date
389     */
390    public abstract ChronoLocalDate<C> date(int prolepticYear, int month, int dayOfMonth);
391
392    /**
393     * Obtains a local date in this chronology from the era, year-of-era and
394     * day-of-year fields.
395     *
396     * @param era  the era of the correct type for the chronology, not null
397     * @param yearOfEra  the chronology year-of-era
398     * @param dayOfYear  the chronology day-of-year
399     * @return the local date in this chronology, not null
400     * @throws DateTimeException if unable to create the date
401     */
402    public ChronoLocalDate<C> dateYearDay(Era<C> era, int yearOfEra, int dayOfYear) {
403        return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
404    }
405
406    /**
407     * Obtains a local date in this chronology from the proleptic-year and
408     * day-of-year fields.
409     *
410     * @param prolepticYear  the chronology proleptic-year
411     * @param dayOfYear  the chronology day-of-year
412     * @return the local date in this chronology, not null
413     * @throws DateTimeException if unable to create the date
414     */
415    public abstract ChronoLocalDate<C> dateYearDay(int prolepticYear, int dayOfYear);
416
417    /**
418     * Obtains a local date in this chronology from another temporal object.
419     * <p>
420     * This creates a date in this chronology based on the specified {@code TemporalAccessor}.
421     * <p>
422     * The standard mechanism for conversion between date types is the
423     * {@link ChronoField#EPOCH_DAY local epoch-day} field.
424     *
425     * @param temporal  the temporal object to convert, not null
426     * @return the local date in this chronology, not null
427     * @throws DateTimeException if unable to create the date
428     */
429    public abstract ChronoLocalDate<C> date(TemporalAccessor temporal);
430
431    //-----------------------------------------------------------------------
432    /**
433     * Obtains the current local date in this chronology from the system clock in the default time-zone.
434     * <p>
435     * This will query the {@link Clock#systemDefaultZone() system clock} in the default
436     * time-zone to obtain the current date.
437     * <p>
438     * Using this method will prevent the ability to use an alternate clock for testing
439     * because the clock is hard-coded.
440     * <p>
441     * This implementation uses {@link #dateNow(Clock)}.
442     *
443     * @return the current local date using the system clock and default time-zone, not null
444     * @throws DateTimeException if unable to create the date
445     */
446    public ChronoLocalDate<C> dateNow() {
447        return dateNow(Clock.systemDefaultZone());
448    }
449
450    /**
451     * Obtains the current local date in this chronology from the system clock in the specified time-zone.
452     * <p>
453     * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
454     * Specifying the time-zone avoids dependence on the default time-zone.
455     * <p>
456     * Using this method will prevent the ability to use an alternate clock for testing
457     * because the clock is hard-coded.
458     *
459     * @param zone  the zone ID to use, not null
460     * @return the current local date using the system clock, not null
461     * @throws DateTimeException if unable to create the date
462     */
463    public ChronoLocalDate<C> dateNow(ZoneId zone) {
464        return dateNow(Clock.system(zone));
465    }
466
467    /**
468     * Obtains the current local date in this chronology from the specified clock.
469     * <p>
470     * This will query the specified clock to obtain the current date - today.
471     * Using this method allows the use of an alternate clock for testing.
472     * The alternate clock may be introduced using {@link Clock dependency injection}.
473     *
474     * @param clock  the clock to use, not null
475     * @return the current local date, not null
476     * @throws DateTimeException if unable to create the date
477     */
478    public ChronoLocalDate<C> dateNow(Clock clock) {
479        Objects.requireNonNull(clock, "clock");
480        return date(LocalDate.now(clock));
481    }
482
483    //-----------------------------------------------------------------------
484    /**
485     * Obtains a local date-time in this chronology from another temporal object.
486     * <p>
487     * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}.
488     * <p>
489     * The date of the date-time should be equivalent to that obtained by calling
490     * {@link #date(TemporalAccessor)}.
491     * The standard mechanism for conversion between time types is the
492     * {@link ChronoField#NANO_OF_DAY nano-of-day} field.
493     *
494     * @param temporal  the temporal object to convert, not null
495     * @return the local date-time in this chronology, not null
496     * @throws DateTimeException if unable to create the date-time
497     */
498    public ChronoLocalDateTime<C> localDateTime(TemporalAccessor temporal) {
499        try {
500            return date(temporal).atTime(LocalTime.from(temporal));
501        } catch (DateTimeException ex) {
502            throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex);
503        }
504    }
505
506    /**
507     * Obtains a zoned date-time in this chronology from another temporal object.
508     * <p>
509     * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}.
510     * <p>
511     * This should obtain a {@code ZoneId} using {@link ZoneId#from(TemporalAccessor)}.
512     * The date-time should be obtained by obtaining an {@code Instant}.
513     * If that fails, the local date-time should be used.
514     *
515     * @param temporal  the temporal object to convert, not null
516     * @return the zoned date-time in this chronology, not null
517     * @throws DateTimeException if unable to create the date-time
518     */
519    public ChronoZonedDateTime<C> zonedDateTime(TemporalAccessor temporal) {
520        try {
521            ZoneId zone = ZoneId.from(temporal);
522            try {
523                Instant instant = Instant.from(temporal);
524                return zonedDateTime(instant, zone);
525
526            } catch (DateTimeException ex1) {
527                ChronoLocalDateTimeImpl<C> cldt = ensureChronoLocalDateTime(localDateTime(temporal));
528                return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
529            }
530        } catch (DateTimeException ex) {
531            throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
532        }
533    }
534
535    /**
536     * Obtains a zoned date-time in this chronology from an {@code Instant}.
537     * <p>
538     * This creates a zoned date-time with the same instant as that specified.
539     *
540     * @param instant  the instant to create the date-time from, not null
541     * @param zone  the time-zone, not null
542     * @return the zoned date-time, not null
543     * @throws DateTimeException if the result exceeds the supported range
544     */
545    public ChronoZonedDateTime<C> zonedDateTime(Instant instant, ZoneId zone) {
546        return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
547    }
548
549    //-----------------------------------------------------------------------
550    /**
551     * Checks if the specified year is a leap year.
552     * <p>
553     * A leap-year is a year of a longer length than normal.
554     * The exact meaning is determined by the chronology according to the following constraints.
555     * <p><ul>
556     * <li>a leap-year must imply a year-length longer than a non leap-year.
557     * <li>a chronology that does not support the concept of a year must return false.
558     * </ul><p>
559     *
560     * @param prolepticYear  the proleptic-year to check, not validated for range
561     * @return true if the year is a leap year
562     */
563    public abstract boolean isLeapYear(long prolepticYear);
564
565    /**
566     * Calculates the proleptic-year given the era and year-of-era.
567     * <p>
568     * This combines the era and year-of-era into the single proleptic-year field.
569     *
570     * @param era  the era of the correct type for the chronology, not null
571     * @param yearOfEra  the chronology year-of-era
572     * @return the proleptic-year
573     * @throws DateTimeException if unable to convert
574     */
575    public abstract int prolepticYear(Era<C> era, int yearOfEra);
576
577    /**
578     * Creates the chronology era object from the numeric value.
579     * <p>
580     * The era is, conceptually, the largest division of the time-line.
581     * Most calendar systems have a single epoch dividing the time-line into two eras.
582     * However, some have multiple eras, such as one for the reign of each leader.
583     * The exact meaning is determined by the chronology according to the following constraints.
584     * <p>
585     * The era in use at 1970-01-01 must have the value 1.
586     * Later eras must have sequentially higher values.
587     * Earlier eras must have sequentially lower values.
588     * Each chronology must refer to an enum or similar singleton to provide the era values.
589     * <p>
590     * This method returns the singleton era of the correct type for the specified era value.
591     *
592     * @param eraValue  the era value
593     * @return the calendar system era, not null
594     * @throws DateTimeException if unable to create the era
595     */
596    public abstract Era<C> eraOf(int eraValue);
597
598    /**
599     * Gets the list of eras for the chronology.
600     * <p>
601     * Most calendar systems have an era, within which the year has meaning.
602     * If the calendar system does not support the concept of eras, an empty
603     * list must be returned.
604     *
605     * @return the list of eras for the chronology, may be immutable, not null
606     */
607    public abstract List<Era<C>> eras();
608
609    //-----------------------------------------------------------------------
610    /**
611     * Gets the range of valid values for the specified field.
612     * <p>
613     * All fields can be expressed as a {@code long} integer.
614     * This method returns an object that describes the valid range for that value.
615     * <p>
616     * Note that the result only describes the minimum and maximum valid values
617     * and it is important not to read too much into them. For example, there
618     * could be values within the range that are invalid for the field.
619     * <p>
620     * This method will return a result whether or not the chronology supports the field.
621     *
622     * @param field  the field to get the range for, not null
623     * @return the range of valid values for the field, not null
624     * @throws DateTimeException if the range for the field cannot be obtained
625     */
626    public abstract ValueRange range(ChronoField field);
627
628    //-----------------------------------------------------------------------
629    /**
630     * Gets the textual representation of this chronology.
631     * <p>
632     * This returns the textual name used to identify the chronology.
633     * The parameters control the style of the returned text and the locale.
634     *
635     * @param style  the style of the text required, not null
636     * @param locale  the locale to use, not null
637     * @return the text value of the chronology, not null
638     */
639    public String getText(TextStyle style, Locale locale) {
640        return new DateTimeFormatterBuilder().appendChronoText(style).toFormatter(locale).print(new DefaultInterfaceTemporalAccessor() {
641            @Override
642            public boolean isSupported(TemporalField field) {
643                return false;
644            }
645            @Override
646            public long getLong(TemporalField field) {
647                throw new DateTimeException("Unsupported field: " + field);
648            }
649            @SuppressWarnings("unchecked")
650            @Override
651            public <R> R query(TemporalQuery<R> query) {
652                if (query == TemporalQueries.CHRONO) {
653                    return (R) Chrono.this;
654                }
655                return super.query(query);
656            }
657        });
658    }
659
660    //-----------------------------------------------------------------------
661    /**
662     * Compares this chronology to another chronology.
663     * <p>
664     * The comparison order first by the chronology ID string, then by any
665     * additional information specific to the subclass.
666     * It is "consistent with equals", as defined by {@link Comparable}.
667     * <p>
668     * The default implementation compares the chronology ID.
669     * Subclasses must compare any additional state that they store.
670     *
671     * @param other  the other chronology to compare to, not null
672     * @return the comparator value, negative if less, positive if greater
673     */
674    @Override
675    public int compareTo(Chrono<?> other) {
676        return getId().compareTo(other.getId());
677    }
678
679    /**
680     * Checks if this chronology is equal to another chronology.
681     * <p>
682     * The comparison is based on the entire state of the object.
683     * <p>
684     * The default implementation checks the type and calls {@link #compareTo(Chrono)}.
685     *
686     * @param obj  the object to check, null returns false
687     * @return true if this is equal to the other chronology
688     */
689    @Override
690    public boolean equals(Object obj) {
691        if (this == obj) {
692           return true;
693        }
694        if (obj instanceof Chrono) {
695            return compareTo((Chrono<?>) obj) == 0;
696        }
697        return false;
698    }
699
700    /**
701     * A hash code for this chronology.
702     * <p>
703     * The default implementation is based on the ID and class.
704     * Subclasses should add any additional state that they store.
705     *
706     * @return a suitable hash code
707     */
708    @Override
709    public int hashCode() {
710        return getClass().hashCode() ^ getId().hashCode();
711    }
712
713    //-----------------------------------------------------------------------
714    /**
715     * Outputs this chronology as a {@code String}, using the ID.
716     *
717     * @return a string representation of this chronology, not null
718     */
719    @Override
720    public String toString() {
721        return getId();
722    }
723
724    //-----------------------------------------------------------------------
725    private Object writeReplace() {
726        return new Ser(Ser.CHRONO_TYPE, this);
727    }
728
729    /**
730     * Defend against malicious streams.
731     * @return never
732     * @throws InvalidObjectException always
733     */
734    private Object readResolve() throws ObjectStreamException {
735        throw new InvalidObjectException("Deserialization via serialization delegate");
736    }
737
738    void writeExternal(DataOutput out) throws IOException {
739        out.writeUTF(getId());
740    }
741
742    static Chrono<?> readExternal(DataInput in) throws IOException {
743        String id = in.readUTF();
744        return Chrono.of(id);
745    }
746
747}