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<ThaiBuddhistChrono> 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}