42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.temporal; 63 64 import java.io.DataInput; 65 import java.io.DataOutput; 66 import java.io.IOException; 67 import java.io.InvalidObjectException; 68 import java.io.ObjectStreamException; 69 import java.time.Clock; 70 import java.time.DateTimeException; 71 import java.time.Instant; 72 import java.time.LocalDate; 73 import java.time.LocalTime; 74 import java.time.ZoneId; 75 import java.time.calendar.HijrahChrono; 76 import java.time.calendar.JapaneseChrono; 77 import java.time.calendar.MinguoChrono; 78 import java.time.calendar.ThaiBuddhistChrono; 79 import java.time.format.DateTimeFormatterBuilder; 80 import java.time.format.TextStyle; 81 import java.util.HashSet; 82 import java.util.List; 83 import java.util.Locale; 84 import java.util.Objects; 85 import java.util.ServiceLoader; 86 import java.util.Set; 87 import java.util.concurrent.ConcurrentHashMap; 88 89 /** 90 * A calendar system, used to organize and identify dates. 91 * <p> 92 * The main date and time API is built on the ISO calendar system. 93 * This class operates behind the scenes to represent the general concept of a calendar system. 94 * For example, the Japanese, Minguo, Thai Buddhist and others. 95 * <p> 96 * Most other calendar systems also operate on the shared concepts of year, month and day, 97 * linked to the cycles of the Earth around the Sun, and the Moon around the Earth. 98 * These shared concepts are defined by {@link ChronoField} and are availalbe 99 * for use by any {@code Chrono} implementation: 100 * <pre> 101 * LocalDate isoDate = ... 102 * ChronoLocalDate<ThaiBuddhistChrono> thaiDate = ... 103 * int isoYear = isoDate.get(ChronoField.YEAR); 104 * int thaiYear = thaiDate.get(ChronoField.YEAR); 105 * </pre> 106 * As shown, although the date objects are in different calendar systems, represented by different 107 * {@code Chrono} instances, both can be queried using the same constant on {@code ChronoField}. 108 * For a full discussion of the implications of this, see {@link ChronoLocalDate}. 109 * In general, the advice is to use the known ISO-based {@code LocalDate}, rather than 110 * {@code ChronoLocalDate}. 111 * <p> 112 * While a {@code Chrono} object typically uses {@code ChronoField} and is based on 113 * an era, year-of-era, month-of-year, day-of-month model of a date, this is not required. 114 * A {@code Chrono} instance may represent a totally different kind of calendar system, 115 * such as the Mayan. 116 * <p> 117 * In practical terms, the {@code Chrono} instance also acts as a factory. 118 * The {@link #of(String)} method allows an instance to be looked up by identifier, 119 * while the {@link #ofLocale(Locale)} method allows lookup by locale. 120 * <p> 121 * The {@code Chrono} instance provides a set of methods to create {@code ChronoLocalDate} instances. 122 * The date classes are used to manipulate specific dates. 123 * <p><ul> 124 * <li> {@link #dateNow() dateNow()} 125 * <li> {@link #dateNow(Clock) dateNow(clock)} 126 * <li> {@link #dateNow(ZoneId) dateNow(zone)} 127 * <li> {@link #date(int, int, int) date(yearProleptic, month, day)} 128 * <li> {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)} 129 * <li> {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)} 130 * <li> {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)} 131 * <li> {@link #date(TemporalAccessor) date(TemporalAccessor)} 132 * </ul><p> 133 * 134 * <p id="addcalendars">Adding New Calendars</p> 135 * The set of available chronologies can be extended by applications. 136 * Adding a new calendar system requires the writing of an implementation of 137 * {@code Chrono}, {@code ChronoLocalDate} and {@code Era}. 138 * The majority of the logic specific to the calendar system will be in 139 * {@code ChronoLocalDate}. The {@code Chrono} subclass acts as a factory. 140 * <p> 141 * To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader} 142 * is used. A file must be added to the {@code META-INF/services} directory with the 143 * name 'java.time.temporal.Chrono' listing the implementation classes. 144 * See the ServiceLoader for more details on service loading. 145 * For lookup by id or calendarType, the system provided calendars are found 146 * first followed by application provided calendars. 147 * <p> 148 * Each chronology must define a chronology ID that is unique within the system. 149 * If the chronology represents a calendar system defined by the 150 * <em>Unicode Locale Data Markup Language (LDML)</em> specification then that 151 * calendar type should also be specified. 152 * 153 * <h3>Specification for implementors</h3> 154 * This class must be implemented with care to ensure other classes operate correctly. 155 * All implementations that can be instantiated must be final, immutable and thread-safe. 156 * Subclasses should be Serializable wherever possible. 157 * 158 * @param <C> the type of the implementing subclass 159 * @since 1.8 160 */ 161 public abstract class Chrono<C extends Chrono<C>> implements Comparable<Chrono<?>> { 162 163 /** 164 * Map of available calendars by ID. 165 */ 166 private static final ConcurrentHashMap<String, Chrono<?>> CHRONOS_BY_ID = new ConcurrentHashMap<>(); 167 /** 168 * Map of available calendars by calendar type. 169 */ 170 private static final ConcurrentHashMap<String, Chrono<?>> CHRONOS_BY_TYPE = new ConcurrentHashMap<>(); 171 172 /** 173 * Register a Chrono by ID and type for lookup by {@link #of(java.lang.String)}. 174 * Chronos must not be registered until they are completely constructed. 175 * Specifically, not in the constructor of Chrono. 176 * @param chrono the chronology to register; not null 177 */ 178 private static void registerChrono(Chrono chrono) { 179 Chrono<?> prev = CHRONOS_BY_ID.putIfAbsent(chrono.getId(), chrono); 180 if (prev == null) { 181 String type = chrono.getCalendarType(); 182 if (type != null) { 183 CHRONOS_BY_TYPE.putIfAbsent(type, chrono); 184 } 185 } 186 } 187 188 /** 189 * Initialization of the maps from id and type to Chrono. 190 * The ServiceLoader is used to find and register any implementations 191 * of {@link javax.time.temporal.Chrono} found in the bootclass loader. 192 * The built-in chronologies are registered explicitly. 193 * Calendars configured via the Thread's context classloader are local 194 * to that thread and are ignored. 195 * <p> 196 * The initialization is done only once using the registration 197 * of the ISOChrono as the test and the final step. 198 * Multiple threads may perform the initialization concurrently. 199 * Only the first registration of each Chrono is retained by the 200 * ConcurrentHashMap. 201 * @return true if the cache was initialized 202 */ 203 private static boolean initCache() { 204 if (CHRONOS_BY_ID.get("ISO") == null) { 205 // Initialization is incomplete 206 @SuppressWarnings("rawtypes") 207 ServiceLoader<Chrono> loader = ServiceLoader.load(Chrono.class, null); 208 for (Chrono<?> chrono : loader) { 209 registerChrono(chrono); 210 } 211 212 // Register these calendars; the ServiceLoader configuration is not used 213 registerChrono(HijrahChrono.INSTANCE); 214 registerChrono(JapaneseChrono.INSTANCE); 215 registerChrono(MinguoChrono.INSTANCE); 216 registerChrono(ThaiBuddhistChrono.INSTANCE); 217 218 // finally, register ISOChrono to mark initialization is complete 219 registerChrono(ISOChrono.INSTANCE); 220 return true; 221 } 222 return false; 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Obtains an instance of {@code Chrono} from a temporal object. 228 * <p> 229 * A {@code TemporalAccessor} represents some form of date and time information. 230 * This factory converts the arbitrary temporal object to an instance of {@code Chrono}. 231 * If the specified temporal object does not have a chronology, {@link ISOChrono} is returned. 232 * <p> 233 * The conversion will obtain the chronology using {@link Queries#chrono()}. 234 * <p> 235 * This method matches the signature of the functional interface {@link TemporalQuery} 236 * allowing it to be used in queries via method reference, {@code Chrono::from}. 237 * 238 * @param temporal the temporal to convert, not null 239 * @return the chronology, not null 240 * @throws DateTimeException if unable to convert to an {@code Chrono} 241 */ 242 public static Chrono<?> from(TemporalAccessor temporal) { 243 Objects.requireNonNull(temporal, "temporal"); 244 Chrono<?> obj = temporal.query(Queries.chrono()); 245 return (obj != null ? obj : ISOChrono.INSTANCE); 246 } 247 248 //----------------------------------------------------------------------- 249 /** 250 * Obtains an instance of {@code Chrono} from a locale. 251 * <p> 252 * The locale can be used to identify a calendar. 253 * This uses {@link Locale#getUnicodeLocaleType(String)} to obtain the "ca" key 254 * to identify the calendar system. 255 * <p> 256 * If the locale does not contain calendar system information, the standard 257 * ISO calendar system is used. 258 * 259 * @param locale the locale to use to obtain the calendar system, not null 260 * @return the calendar system associated with the locale, not null 261 * @throws DateTimeException if the locale-specified calendar cannot be found 262 */ 263 public static Chrono<?> ofLocale(Locale locale) { 264 Objects.requireNonNull(locale, "locale"); 265 String type = locale.getUnicodeLocaleType("ca"); 266 if (type == null) { 267 return ISOChrono.INSTANCE; 268 } else if ("iso".equals(type) || "iso8601".equals(type)) { 269 return ISOChrono.INSTANCE; 270 } else { 271 Chrono<?> chrono = CHRONOS_BY_TYPE.get(type); 272 if (chrono == null) { 273 throw new DateTimeException("Unknown calendar system: " + type); 274 } 275 return chrono; 276 } 277 } 278 279 //----------------------------------------------------------------------- 280 /** 281 * Obtains an instance of {@code Chrono} from a chronology ID or 282 * calendar system type. 283 * <p> 284 * This returns a chronology based on either the ID or the type. 285 * The {@link #getId() chronology ID} uniquely identifies the chronology. 286 * The {@link #getCalendarType() calendar system type} is defined by the LDML specification. 287 * <p> 288 * The chronology may be a system chronology or a chronology 289 * provided by the application via ServiceLoader configuration. 290 * <p> 291 * Since some calendars can be customized, the ID or type typically refers 292 * to the default customization. For example, the Gregorian calendar can have multiple 293 * cutover dates from the Julian, but the lookup only provides the default cutover date. 294 * 295 * @param id the chronology ID or calendar system type, not null 296 * @return the chronology with the identifier requested, not null 297 * @throws DateTimeException if the chronology cannot be found 298 */ 299 public static Chrono<?> of(String id) { 300 Objects.requireNonNull(id, "id"); 301 do { 302 Chrono chrono = of0(id); 303 if (chrono != null) { 304 return chrono; 305 } 306 // If not found, do the initialization (once) and repeat the lookup 307 } while (initCache()); 308 309 // Look for a Chrono using ServiceLoader of the Thread's ContextClassLoader 310 // Application provided Chronologies must not be cached 311 @SuppressWarnings("rawtypes") 312 ServiceLoader<Chrono> loader = ServiceLoader.load(Chrono.class); 313 for (Chrono<?> chrono : loader) { 314 if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) { 315 return chrono; 316 } 317 } 318 throw new DateTimeException("Unknown chronology: " + id); 319 } 320 321 /** 322 * Obtains an instance of {@code Chrono} from a chronology ID or 323 * calendar system type. 324 * 325 * @param id the chronology ID or calendar system type, not null 326 * @return the chronology with the identifier requested, or {@code null} if not found 327 */ 328 private static Chrono<?> of0(String id) { 329 Chrono<?> chrono = CHRONOS_BY_ID.get(id); 330 if (chrono == null) { 331 chrono = CHRONOS_BY_TYPE.get(id); 332 } 333 return chrono; 334 } 335 336 /** 337 * Returns the available chronologies. 338 * <p> 339 * Each returned {@code Chrono} is available for use in the system. 340 * The set of chronologies includes the system chronologies and 341 * any chronologies provided by the application via ServiceLoader 342 * configuration. 343 * 344 * @return the independent, modifiable set of the available chronology IDs, not null 345 */ 346 public static Set<Chrono<?>> getAvailableChronologies() { 347 initCache(); // force initialization 348 HashSet<Chrono<?>> chronos = new HashSet<>(CHRONOS_BY_ID.values()); 349 350 /// Add in Chronologies from the ServiceLoader configuration 351 @SuppressWarnings("rawtypes") 352 ServiceLoader<Chrono> loader = ServiceLoader.load(Chrono.class); 353 for (Chrono<?> chrono : loader) { 354 chronos.add(chrono); 355 } 356 return chronos; 357 } 358 359 //----------------------------------------------------------------------- 360 /** 361 * Obtains a local date-time from the a date and time. 362 * <p> 363 * This combines a {@link ChronoLocalDate}, which provides the {@code Chrono}, 364 * with a {@link LocalTime} to produce a {@link ChronoLocalDateTime}. 365 * <p> 366 * This method is intended for chronology implementations. 367 * It uses a standard implementation that is shared for all chronologies. 368 * 369 * @param <R> the chronology of the date 370 * @param date the date, not null 371 * @param time the time, not null 372 * @return the local date-time combining the input date and time, not null 373 */ 374 public static <R extends Chrono<R>> ChronoLocalDateTime<R> dateTime(ChronoLocalDate<R> date, LocalTime time) { 375 return ChronoLocalDateTimeImpl.of(date, time); 376 } 377 378 //----------------------------------------------------------------------- 379 /** 380 * Creates an instance. 381 */ 382 protected Chrono() { 383 } 384 385 //----------------------------------------------------------------------- 386 /** 387 * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology. 388 * 389 * @param temporal a date-time to cast, not null 390 * @return the date-time checked and cast to {@code ChronoLocalDate}, not null 391 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate 392 * or the chronology is not equal this Chrono 393 */ 394 ChronoLocalDate<C> ensureChronoLocalDate(Temporal temporal) { 395 @SuppressWarnings("unchecked") 396 ChronoLocalDate<C> other = (ChronoLocalDate<C>) temporal; 397 if (this.equals(other.getChrono()) == false) { 398 throw new ClassCastException("Chrono mismatch, expected: " + getId() + ", actual: " + other.getChrono().getId()); 399 } 400 return other; 401 } 402 403 /** 404 * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology. 405 * 406 * @param temporal a date-time to cast, not null 407 * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null 408 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl 409 * or the chronology is not equal this Chrono 410 */ 411 ChronoLocalDateTimeImpl<C> ensureChronoLocalDateTime(Temporal temporal) { 412 @SuppressWarnings("unchecked") 413 ChronoLocalDateTimeImpl<C> other = (ChronoLocalDateTimeImpl<C>) temporal; 414 if (this.equals(other.getDate().getChrono()) == false) { 415 throw new ClassCastException("Chrono mismatch, required: " + getId() 416 + ", supplied: " + other.getDate().getChrono().getId()); 417 } 418 return other; 419 } 420 421 /** 422 * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology. 423 * 424 * @param temporal a date-time to cast, not null 425 * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null 426 * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl 427 * or the chronology is not equal this Chrono 428 */ 429 ChronoZonedDateTimeImpl<C> ensureChronoZonedDateTime(Temporal temporal) { 430 @SuppressWarnings("unchecked") 431 ChronoZonedDateTimeImpl<C> other = (ChronoZonedDateTimeImpl<C>) temporal; 432 if (this.equals(other.getDate().getChrono()) == false) { 433 throw new ClassCastException("Chrono mismatch, required: " + getId() 434 + ", supplied: " + other.getDate().getChrono().getId()); 435 } 436 return other; 437 } 438 439 //----------------------------------------------------------------------- 440 /** 441 * Gets the ID of the chronology. 442 * <p> 443 * The ID uniquely identifies the {@code Chrono}. 444 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 445 * 446 * @return the chronology ID, not null 447 * @see #getCalendarType() 448 */ 449 public abstract String getId(); 450 451 /** 452 * Gets the calendar type of the underlying calendar system. 453 * <p> 454 * The calendar type is an identifier defined by the 455 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 456 * It can be used to lookup the {@code Chrono} using {@link #of(String)}. 457 * It can also be used as part of a locale, accessible via 458 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 459 * 460 * @return the calendar system type, null if the calendar is not defined by LDML 461 * @see #getId() 462 */ 463 public abstract String getCalendarType(); 464 465 //----------------------------------------------------------------------- 466 /** 467 * Obtains a local date in this chronology from the era, year-of-era, 468 * month-of-year and day-of-month fields. 469 * 470 * @param era the era of the correct type for the chronology, not null 471 * @param yearOfEra the chronology year-of-era 472 * @param month the chronology month-of-year 473 * @param dayOfMonth the chronology day-of-month 474 * @return the local date in this chronology, not null 475 * @throws DateTimeException if unable to create the date 476 */ 477 public ChronoLocalDate<C> date(Era<C> era, int yearOfEra, int month, int dayOfMonth) { 478 return date(prolepticYear(era, yearOfEra), month, dayOfMonth); 479 } 480 481 /** 482 * Obtains a local date in this chronology from the proleptic-year, 483 * month-of-year and day-of-month fields. 484 * 485 * @param prolepticYear the chronology proleptic-year 486 * @param month the chronology month-of-year 487 * @param dayOfMonth the chronology day-of-month 488 * @return the local date in this chronology, not null 489 * @throws DateTimeException if unable to create the date 490 */ 491 public abstract ChronoLocalDate<C> date(int prolepticYear, int month, int dayOfMonth); 492 493 /** 494 * Obtains a local date in this chronology from the era, year-of-era and 495 * day-of-year fields. 496 * 497 * @param era the era of the correct type for the chronology, not null 498 * @param yearOfEra the chronology year-of-era 499 * @param dayOfYear the chronology day-of-year 500 * @return the local date in this chronology, not null 501 * @throws DateTimeException if unable to create the date 502 */ 503 public ChronoLocalDate<C> dateYearDay(Era<C> era, int yearOfEra, int dayOfYear) { 504 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); 505 } 506 507 /** 508 * Obtains a local date in this chronology from the proleptic-year and 509 * day-of-year fields. 510 * 511 * @param prolepticYear the chronology proleptic-year 512 * @param dayOfYear the chronology day-of-year 513 * @return the local date in this chronology, not null 514 * @throws DateTimeException if unable to create the date 515 */ 516 public abstract ChronoLocalDate<C> dateYearDay(int prolepticYear, int dayOfYear); 517 518 /** 519 * Obtains a local date in this chronology from another temporal object. 520 * <p> 521 * This creates a date in this chronology based on the specified {@code TemporalAccessor}. 522 * <p> 523 * The standard mechanism for conversion between date types is the 524 * {@link ChronoField#EPOCH_DAY local epoch-day} field. 525 * 526 * @param temporal the temporal object to convert, not null 527 * @return the local date in this chronology, not null 528 * @throws DateTimeException if unable to create the date 529 */ 530 public abstract ChronoLocalDate<C> date(TemporalAccessor temporal); 531 532 //----------------------------------------------------------------------- 533 /** 534 * Obtains the current local date in this chronology from the system clock in the default time-zone. 535 * <p> 536 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 537 * time-zone to obtain the current date. 538 * <p> 539 * Using this method will prevent the ability to use an alternate clock for testing 540 * because the clock is hard-coded. 541 * <p> 542 * This implementation uses {@link #dateNow(Clock)}. 543 * 544 * @return the current local date using the system clock and default time-zone, not null 545 * @throws DateTimeException if unable to create the date 546 */ 547 public ChronoLocalDate<C> dateNow() { 548 return dateNow(Clock.systemDefaultZone()); 549 } 550 551 /** 552 * Obtains the current local date in this chronology from the system clock in the specified time-zone. 553 * <p> 554 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. 555 * Specifying the time-zone avoids dependence on the default time-zone. 556 * <p> 557 * Using this method will prevent the ability to use an alternate clock for testing 558 * because the clock is hard-coded. 559 * 560 * @param zone the zone ID to use, not null 561 * @return the current local date using the system clock, not null 562 * @throws DateTimeException if unable to create the date 563 */ 564 public ChronoLocalDate<C> dateNow(ZoneId zone) { 565 return dateNow(Clock.system(zone)); 566 } 567 568 /** 569 * Obtains the current local date in this chronology from the specified clock. 570 * <p> 571 * This will query the specified clock to obtain the current date - today. 572 * Using this method allows the use of an alternate clock for testing. 573 * The alternate clock may be introduced using {@link Clock dependency injection}. 574 * 575 * @param clock the clock to use, not null 576 * @return the current local date, not null 577 * @throws DateTimeException if unable to create the date 578 */ 579 public ChronoLocalDate<C> dateNow(Clock clock) { 580 Objects.requireNonNull(clock, "clock"); 581 return date(LocalDate.now(clock)); 582 } 583 584 //----------------------------------------------------------------------- 585 /** 586 * Obtains a local date-time in this chronology from another temporal object. 587 * <p> 588 * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}. 589 * <p> 590 * The date of the date-time should be equivalent to that obtained by calling 591 * {@link #date(TemporalAccessor)}. 592 * The standard mechanism for conversion between time types is the 593 * {@link ChronoField#NANO_OF_DAY nano-of-day} field. 594 * 595 * @param temporal the temporal object to convert, not null 596 * @return the local date-time in this chronology, not null 597 * @throws DateTimeException if unable to create the date-time 598 */ 599 public ChronoLocalDateTime<C> localDateTime(TemporalAccessor temporal) { 600 try { 601 return date(temporal).atTime(LocalTime.from(temporal)); 602 } catch (DateTimeException ex) { 603 throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex); 604 } 605 } 606 607 /** 608 * Obtains a zoned date-time in this chronology from another temporal object. 609 * <p> 610 * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}. 611 * <p> 612 * This should obtain a {@code ZoneId} using {@link ZoneId#from(TemporalAccessor)}. 613 * The date-time should be obtained by obtaining an {@code Instant}. 614 * If that fails, the local date-time should be used. 615 * 616 * @param temporal the temporal object to convert, not null 617 * @return the zoned date-time in this chronology, not null 618 * @throws DateTimeException if unable to create the date-time 619 */ 620 public ChronoZonedDateTime<C> zonedDateTime(TemporalAccessor temporal) { 621 try { 622 ZoneId zone = ZoneId.from(temporal); 623 try { 624 Instant instant = Instant.from(temporal); 625 return zonedDateTime(instant, zone); 626 627 } catch (DateTimeException ex1) { 628 ChronoLocalDateTimeImpl<C> cldt = ensureChronoLocalDateTime(localDateTime(temporal)); 629 return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null); 630 } 631 } catch (DateTimeException ex) { 632 throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex); 633 } 634 } 635 636 /** 637 * Obtains a zoned date-time in this chronology from an {@code Instant}. 638 * <p> 639 * This creates a zoned date-time with the same instant as that specified. 640 * 641 * @param instant the instant to create the date-time from, not null 642 * @param zone the time-zone, not null 643 * @return the zoned date-time, not null 644 * @throws DateTimeException if the result exceeds the supported range 645 */ 646 public ChronoZonedDateTime<C> zonedDateTime(Instant instant, ZoneId zone) { 647 return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone); 648 } 649 650 //----------------------------------------------------------------------- 651 /** 652 * Checks if the specified year is a leap year. 653 * <p> 654 * A leap-year is a year of a longer length than normal. 655 * The exact meaning is determined by the chronology according to the following constraints. 656 * <p><ul> 657 * <li>a leap-year must imply a year-length longer than a non leap-year. 658 * <li>a chronology that does not support the concept of a year must return false. 659 * </ul><p> 660 * 661 * @param prolepticYear the proleptic-year to check, not validated for range 662 * @return true if the year is a leap year 663 */ 664 public abstract boolean isLeapYear(long prolepticYear); 665 666 /** 667 * Calculates the proleptic-year given the era and year-of-era. 668 * <p> 669 * This combines the era and year-of-era into the single proleptic-year field. 670 * 671 * @param era the era of the correct type for the chronology, not null 672 * @param yearOfEra the chronology year-of-era 673 * @return the proleptic-year 674 * @throws DateTimeException if unable to convert 675 */ 676 public abstract int prolepticYear(Era<C> era, int yearOfEra); 677 678 /** 679 * Creates the chronology era object from the numeric value. 680 * <p> 681 * The era is, conceptually, the largest division of the time-line. 682 * Most calendar systems have a single epoch dividing the time-line into two eras. 683 * However, some have multiple eras, such as one for the reign of each leader. 684 * The exact meaning is determined by the chronology according to the following constraints. 685 * <p> 686 * The era in use at 1970-01-01 must have the value 1. 687 * Later eras must have sequentially higher values. 688 * Earlier eras must have sequentially lower values. 689 * Each chronology must refer to an enum or similar singleton to provide the era values. 690 * <p> 691 * This method returns the singleton era of the correct type for the specified era value. 692 * 693 * @param eraValue the era value 694 * @return the calendar system era, not null 695 * @throws DateTimeException if unable to create the era 696 */ 697 public abstract Era<C> eraOf(int eraValue); 698 699 /** 700 * Gets the list of eras for the chronology. 701 * <p> 702 * Most calendar systems have an era, within which the year has meaning. 703 * If the calendar system does not support the concept of eras, an empty 704 * list must be returned. 705 * 706 * @return the list of eras for the chronology, may be immutable, not null 707 */ 708 public abstract List<Era<C>> eras(); 709 710 //----------------------------------------------------------------------- 711 /** 712 * Gets the range of valid values for the specified field. 713 * <p> 714 * All fields can be expressed as a {@code long} integer. 715 * This method returns an object that describes the valid range for that value. 716 * <p> 717 * Note that the result only describes the minimum and maximum valid values 718 * and it is important not to read too much into them. For example, there 719 * could be values within the range that are invalid for the field. 720 * <p> 721 * This method will return a result whether or not the chronology supports the field. 722 * 723 * @param field the field to get the range for, not null 724 * @return the range of valid values for the field, not null 725 * @throws DateTimeException if the range for the field cannot be obtained 726 */ 727 public abstract ValueRange range(ChronoField field); 728 729 //----------------------------------------------------------------------- 730 /** 731 * Gets the textual representation of this chronology. 732 * <p> 733 * This returns the textual name used to identify the chronology. 734 * The parameters control the style of the returned text and the locale. 735 * 736 * @param style the style of the text required, not null 737 * @param locale the locale to use, not null 738 * @return the text value of the chronology, not null 739 */ 740 public String getText(TextStyle style, Locale locale) { 741 return new DateTimeFormatterBuilder().appendChronoText(style).toFormatter(locale).print(new TemporalAccessor() { 742 @Override 743 public boolean isSupported(TemporalField field) { 744 return false; 745 } 746 @Override 747 public long getLong(TemporalField field) { 748 throw new DateTimeException("Unsupported field: " + field); 749 } 750 @SuppressWarnings("unchecked") 751 @Override 752 public <R> R query(TemporalQuery<R> query) { 753 if (query == Queries.chrono()) { 754 return (R) Chrono.this; 755 } 756 return TemporalAccessor.super.query(query); 757 } 758 }); 759 } 760 761 //----------------------------------------------------------------------- 762 /** 763 * Compares this chronology to another chronology. 764 * <p> 765 * The comparison order first by the chronology ID string, then by any 766 * additional information specific to the subclass. 767 * It is "consistent with equals", as defined by {@link Comparable}. 768 * <p> 769 * The default implementation compares the chronology ID. 770 * Subclasses must compare any additional state that they store. 771 * 772 * @param other the other chronology to compare to, not null 773 * @return the comparator value, negative if less, positive if greater 774 */ 775 @Override 776 public int compareTo(Chrono<?> other) { 777 return getId().compareTo(other.getId()); 778 } 779 780 /** 781 * Checks if this chronology is equal to another chronology. 782 * <p> 783 * The comparison is based on the entire state of the object. 784 * <p> 785 * The default implementation checks the type and calls {@link #compareTo(Chrono)}. 786 * 787 * @param obj the object to check, null returns false 788 * @return true if this is equal to the other chronology 789 */ 790 @Override 791 public boolean equals(Object obj) { 792 if (this == obj) { 793 return true; 794 } 795 if (obj instanceof Chrono) { 796 return compareTo((Chrono<?>) obj) == 0; 797 } 798 return false; 799 } 800 801 /** 802 * A hash code for this chronology. 803 * <p> 804 * The default implementation is based on the ID and class. 805 * Subclasses should add any additional state that they store. 806 * 807 * @return a suitable hash code 808 */ 809 @Override 810 public int hashCode() { 811 return getClass().hashCode() ^ getId().hashCode(); 812 } 813 814 //----------------------------------------------------------------------- 815 /** 816 * Outputs this chronology as a {@code String}, using the ID. 817 * 818 * @return a string representation of this chronology, not null 819 */ 820 @Override 821 public String toString() { 822 return getId(); 823 } 824 825 //----------------------------------------------------------------------- 826 /** 827 * Writes the object using a 828 * <a href="../../../serialized-form.html#java.time.temporal.Ser">dedicated serialized form</a>. 829 * <pre> 830 * out.writeByte(7); // identifies this as a Chrono 831 * out.writeUTF(chronoId); 832 * </pre> 833 * 834 * @return the instance of {@code Ser}, not null 835 */ 836 private Object writeReplace() { 837 return new Ser(Ser.CHRONO_TYPE, this); 838 } 839 840 /** 841 * Defend against malicious streams. 842 * @return never 843 * @throws InvalidObjectException always 844 */ 845 private Object readResolve() throws ObjectStreamException { 846 throw new InvalidObjectException("Deserialization via serialization delegate"); 847 } 848 849 void writeExternal(DataOutput out) throws IOException { 850 out.writeUTF(getId()); 851 } 852 853 static Chrono<?> readExternal(DataInput in) throws IOException { 854 String id = in.readUTF(); 855 return Chrono.of(id); 856 } 857 858 } | 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.chrono; 63 64 import java.io.DataInput; 65 import java.io.DataOutput; 66 import java.io.IOException; 67 import java.io.InvalidObjectException; 68 import java.io.ObjectStreamException; 69 import java.time.Clock; 70 import java.time.DateTimeException; 71 import java.time.Instant; 72 import java.time.LocalDate; 73 import java.time.LocalTime; 74 import java.time.ZoneId; 75 import java.time.chrono.HijrahChronology; 76 import java.time.chrono.JapaneseChronology; 77 import java.time.chrono.MinguoChronology; 78 import java.time.chrono.ThaiBuddhistChronology; 79 import java.time.format.DateTimeFormatterBuilder; 80 import java.time.format.TextStyle; 81 import java.time.temporal.ChronoField; 82 import java.time.temporal.Queries; 83 import java.time.temporal.Temporal; 84 import java.time.temporal.TemporalAccessor; 85 import java.time.temporal.TemporalField; 86 import java.time.temporal.TemporalQuery; 87 import java.time.temporal.ValueRange; 88 import java.util.HashSet; 89 import java.util.List; 90 import java.util.Locale; 91 import java.util.Objects; 92 import java.util.ServiceLoader; 93 import java.util.Set; 94 import java.util.concurrent.ConcurrentHashMap; 95 96 /** 97 * A calendar system, used to organize and identify dates. 98 * <p> 99 * The main date and time API is built on the ISO calendar system. 100 * This class operates behind the scenes to represent the general concept of a calendar system. 101 * For example, the Japanese, Minguo, Thai Buddhist and others. 102 * <p> 103 * Most other calendar systems also operate on the shared concepts of year, month and day, 104 * linked to the cycles of the Earth around the Sun, and the Moon around the Earth. 105 * These shared concepts are defined by {@link ChronoField} and are available 106 * for use by any {@code Chronology} implementation: 107 * <pre> 108 * LocalDate isoDate = ... 109 * ChronoLocalDate<ThaiBuddhistChronology> thaiDate = ... 110 * int isoYear = isoDate.get(ChronoField.YEAR); 111 * int thaiYear = thaiDate.get(ChronoField.YEAR); 112 * </pre> 113 * As shown, although the date objects are in different calendar systems, represented by different 114 * {@code Chronology} instances, both can be queried using the same constant on {@code ChronoField}. 115 * For a full discussion of the implications of this, see {@link ChronoLocalDate}. 116 * In general, the advice is to use the known ISO-based {@code LocalDate}, rather than 117 * {@code ChronoLocalDate}. 118 * <p> 119 * While a {@code Chronology} object typically uses {@code ChronoField} and is based on 120 * an era, year-of-era, month-of-year, day-of-month model of a date, this is not required. 121 * A {@code Chronology} instance may represent a totally different kind of calendar system, 122 * such as the Mayan. 123 * <p> 124 * In practical terms, the {@code Chronology} instance also acts as a factory. 125 * The {@link #of(String)} method allows an instance to be looked up by identifier, 126 * while the {@link #ofLocale(Locale)} method allows lookup by locale. 127 * <p> 128 * The {@code Chronology} instance provides a set of methods to create {@code ChronoLocalDate} instances. 129 * The date classes are used to manipulate specific dates. 130 * <p><ul> 131 * <li> {@link #dateNow() dateNow()} 132 * <li> {@link #dateNow(Clock) dateNow(clock)} 133 * <li> {@link #dateNow(ZoneId) dateNow(zone)} 134 * <li> {@link #date(int, int, int) date(yearProleptic, month, day)} 135 * <li> {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)} 136 * <li> {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)} 137 * <li> {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)} 138 * <li> {@link #date(TemporalAccessor) date(TemporalAccessor)} 139 * </ul><p> 140 * 141 * <h3 id="addcalendars">Adding New Calendars</h3> 142 * The set of available chronologies can be extended by applications. 143 * Adding a new calendar system requires the writing of an implementation of 144 * {@code Chronology}, {@code ChronoLocalDate} and {@code Era}. 145 * The majority of the logic specific to the calendar system will be in 146 * {@code ChronoLocalDate}. The {@code Chronology} subclass acts as a factory. 147 * <p> 148 * To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader} 149 * is used. A file must be added to the {@code META-INF/services} directory with the 150 * name 'java.time.chrono.Chronology' listing the implementation classes. 151 * See the ServiceLoader for more details on service loading. 152 * For lookup by id or calendarType, the system provided calendars are found 153 * first followed by application provided calendars. 154 * <p> 155 * Each chronology must define a chronology ID that is unique within the system. 156 * If the chronology represents a calendar system defined by the 157 * <em>Unicode Locale Data Markup Language (LDML)</em> specification then that 158 * calendar type should also be specified. 159 * 160 * <h3>Specification for implementors</h3> 161 * This class must be implemented with care to ensure other classes operate correctly. 162 * All implementations that can be instantiated must be final, immutable and thread-safe. 163 * Subclasses should be Serializable wherever possible. 164 * 165 * @since 1.8 166 */ 167 public abstract class Chronology implements Comparable<Chronology> { 168 169 /** 170 * Map of available calendars by ID. 171 */ 172 private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_ID = new ConcurrentHashMap<>(); 173 /** 174 * Map of available calendars by calendar type. 175 */ 176 private static final ConcurrentHashMap<String, Chronology> CHRONOS_BY_TYPE = new ConcurrentHashMap<>(); 177 178 /** 179 * Register a Chronology by ID and type for lookup by {@link #of(java.lang.String)}. 180 * Chronos must not be registered until they are completely constructed. 181 * Specifically, not in the constructor of Chronology. 182 * @param chrono the chronology to register; not null 183 */ 184 private static void registerChrono(Chronology chrono) { 185 Chronology prev = CHRONOS_BY_ID.putIfAbsent(chrono.getId(), chrono); 186 if (prev == null) { 187 String type = chrono.getCalendarType(); 188 if (type != null) { 189 CHRONOS_BY_TYPE.putIfAbsent(type, chrono); 190 } 191 } 192 } 193 194 /** 195 * Initialization of the maps from id and type to Chronology. 196 * The ServiceLoader is used to find and register any implementations 197 * of {@link java.time.chrono.Chronology} found in the bootclass loader. 198 * The built-in chronologies are registered explicitly. 199 * Calendars configured via the Thread's context classloader are local 200 * to that thread and are ignored. 201 * <p> 202 * The initialization is done only once using the registration 203 * of the IsoChronology as the test and the final step. 204 * Multiple threads may perform the initialization concurrently. 205 * Only the first registration of each Chronology is retained by the 206 * ConcurrentHashMap. 207 * @return true if the cache was initialized 208 */ 209 private static boolean initCache() { 210 if (CHRONOS_BY_ID.get("ISO") == null) { 211 // Initialization is incomplete 212 @SuppressWarnings("rawtypes") 213 ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class, null); 214 for (Chronology chrono : loader) { 215 registerChrono(chrono); 216 } 217 218 // Register these calendars; the ServiceLoader configuration is not used 219 registerChrono(HijrahChronology.INSTANCE); 220 registerChrono(JapaneseChronology.INSTANCE); 221 registerChrono(MinguoChronology.INSTANCE); 222 registerChrono(ThaiBuddhistChronology.INSTANCE); 223 224 // finally, register IsoChronology to mark initialization is complete 225 registerChrono(IsoChronology.INSTANCE); 226 return true; 227 } 228 return false; 229 } 230 231 //----------------------------------------------------------------------- 232 /** 233 * Obtains an instance of {@code Chronology} from a temporal object. 234 * <p> 235 * This obtains a chronology based on the specified temporal. 236 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 237 * which this factory converts to an instance of {@code Chronology}. 238 * <p> 239 * The conversion will obtain the chronology using {@link Queries#chronology()}. 240 * If the specified temporal object does not have a chronology, {@link IsoChronology} is returned. 241 * <p> 242 * This method matches the signature of the functional interface {@link TemporalQuery} 243 * allowing it to be used in queries via method reference, {@code Chronology::from}. 244 * 245 * @param temporal the temporal to convert, not null 246 * @return the chronology, not null 247 * @throws DateTimeException if unable to convert to an {@code Chronology} 248 */ 249 public static Chronology from(TemporalAccessor temporal) { 250 Objects.requireNonNull(temporal, "temporal"); 251 Chronology obj = temporal.query(Queries.chronology()); 252 return (obj != null ? obj : IsoChronology.INSTANCE); 253 } 254 255 //----------------------------------------------------------------------- 256 /** 257 * Obtains an instance of {@code Chronology} from a locale. 258 * <p> 259 * This returns a {@code Chronology} based on the specified locale, 260 * typically returning {@code IsoChronology}. Other calendar systems 261 * are only returned if they are explicitly selected within the locale. 262 * <p> 263 * The {@link Locale} class provide access to a range of information useful 264 * for localizing an application. This includes the language and region, 265 * such as "en-GB" for English as used in Great Britain. 266 * <p> 267 * The {@code Locale} class also supports an extension mechanism that 268 * can be used to identify a calendar system. The mechanism is a form 269 * of key-value pairs, where the calendar system has the key "ca". 270 * For example, the locale "en-JP-u-ca-japanese" represents the English 271 * language as used in Japan with the Japanese calendar system. 272 * <p> 273 * This method finds the desired calendar system by in a manner equivalent 274 * to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}. 275 * If the "ca" key is not present, then {@code IsoChronology} is returned. 276 * <p> 277 * Note that the behavior of this method differs from the older 278 * {@link java.util.Calendar#getInstance(Locale)} method. 279 * If that method receives a locale of "th_TH" it will return {@code BuddhistCalendar}. 280 * By contrast, this method will return {@code IsoChronology}. 281 * Passing the locale "th-TH-u-ca-buddhist" into either method will 282 * result in the Thai Buddhist calendar system and is therefore the 283 * recommended approach going forward for Thai calendar system localization. 284 * <p> 285 * A similar, but simpler, situation occurs for the Japanese calendar system. 286 * The locale "jp_JP_JP" has previously been used to access the calendar. 287 * However, unlike the Thai locale, "ja_JP_JP" is automatically converted by 288 * {@code Locale} to the modern and recommended form of "ja-JP-u-ca-japanese". 289 * Thus, there is no difference in behavior between this method and 290 * {@code Calendar#getInstance(Locale)}. 291 * 292 * @param locale the locale to use to obtain the calendar system, not null 293 * @return the calendar system associated with the locale, not null 294 * @throws DateTimeException if the locale-specified calendar cannot be found 295 */ 296 public static Chronology ofLocale(Locale locale) { 297 Objects.requireNonNull(locale, "locale"); 298 String type = locale.getUnicodeLocaleType("ca"); 299 if (type == null || "iso".equals(type) || "iso8601".equals(type)) { 300 return IsoChronology.INSTANCE; 301 } 302 // Not pre-defined; lookup by the type 303 do { 304 Chronology chrono = CHRONOS_BY_TYPE.get(type); 305 if (chrono != null) { 306 return chrono; 307 } 308 // If not found, do the initialization (once) and repeat the lookup 309 } while (initCache()); 310 throw new DateTimeException("Unknown calendar system: " + type); 311 } 312 313 //----------------------------------------------------------------------- 314 /** 315 * Obtains an instance of {@code Chronology} from a chronology ID or 316 * calendar system type. 317 * <p> 318 * This returns a chronology based on either the ID or the type. 319 * The {@link #getId() chronology ID} uniquely identifies the chronology. 320 * The {@link #getCalendarType() calendar system type} is defined by the LDML specification. 321 * <p> 322 * The chronology may be a system chronology or a chronology 323 * provided by the application via ServiceLoader configuration. 324 * <p> 325 * Since some calendars can be customized, the ID or type typically refers 326 * to the default customization. For example, the Gregorian calendar can have multiple 327 * cutover dates from the Julian, but the lookup only provides the default cutover date. 328 * 329 * @param id the chronology ID or calendar system type, not null 330 * @return the chronology with the identifier requested, not null 331 * @throws DateTimeException if the chronology cannot be found 332 */ 333 public static Chronology of(String id) { 334 Objects.requireNonNull(id, "id"); 335 do { 336 Chronology chrono = of0(id); 337 if (chrono != null) { 338 return chrono; 339 } 340 // If not found, do the initialization (once) and repeat the lookup 341 } while (initCache()); 342 343 // Look for a Chronology using ServiceLoader of the Thread's ContextClassLoader 344 // Application provided Chronologies must not be cached 345 @SuppressWarnings("rawtypes") 346 ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class); 347 for (Chronology chrono : loader) { 348 if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) { 349 return chrono; 350 } 351 } 352 throw new DateTimeException("Unknown chronology: " + id); 353 } 354 355 /** 356 * Obtains an instance of {@code Chronology} from a chronology ID or 357 * calendar system type. 358 * 359 * @param id the chronology ID or calendar system type, not null 360 * @return the chronology with the identifier requested, or {@code null} if not found 361 */ 362 private static Chronology of0(String id) { 363 Chronology chrono = CHRONOS_BY_ID.get(id); 364 if (chrono == null) { 365 chrono = CHRONOS_BY_TYPE.get(id); 366 } 367 return chrono; 368 } 369 370 /** 371 * Returns the available chronologies. 372 * <p> 373 * Each returned {@code Chronology} is available for use in the system. 374 * The set of chronologies includes the system chronologies and 375 * any chronologies provided by the application via ServiceLoader 376 * configuration. 377 * 378 * @return the independent, modifiable set of the available chronology IDs, not null 379 */ 380 public static Set<Chronology> getAvailableChronologies() { 381 initCache(); // force initialization 382 HashSet<Chronology> chronos = new HashSet(CHRONOS_BY_ID.values()); 383 384 /// Add in Chronologies from the ServiceLoader configuration 385 @SuppressWarnings("rawtypes") 386 ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class); 387 for (Chronology chrono : loader) { 388 chronos.add(chrono); 389 } 390 return chronos; 391 } 392 393 //----------------------------------------------------------------------- 394 /** 395 * Creates an instance. 396 */ 397 protected Chronology() { 398 } 399 400 //----------------------------------------------------------------------- 401 /** 402 * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology. 403 * 404 * @param temporal a date-time to cast, not null 405 * @return the date-time checked and cast to {@code ChronoLocalDate}, not null 406 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate 407 * or the chronology is not equal this Chronology 408 */ 409 ChronoLocalDate ensureChronoLocalDate(Temporal temporal) { 410 @SuppressWarnings("unchecked") 411 ChronoLocalDate other = (ChronoLocalDate) temporal; 412 if (this.equals(other.getChronology()) == false) { 413 throw new ClassCastException("Chronology mismatch, expected: " + getId() + ", actual: " + other.getChronology().getId()); 414 } 415 return other; 416 } 417 418 /** 419 * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology. 420 * 421 * @param temporal a date-time to cast, not null 422 * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null 423 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl 424 * or the chronology is not equal this Chronology 425 */ 426 ChronoLocalDateTimeImpl<?> ensureChronoLocalDateTime(Temporal temporal) { 427 @SuppressWarnings("unchecked") 428 ChronoLocalDateTimeImpl<?> other = (ChronoLocalDateTimeImpl<?>) temporal; 429 if (this.equals(other.toLocalDate().getChronology()) == false) { 430 throw new ClassCastException("Chronology mismatch, required: " + getId() 431 + ", supplied: " + other.toLocalDate().getChronology().getId()); 432 } 433 return other; 434 } 435 436 /** 437 * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology. 438 * 439 * @param temporal a date-time to cast, not null 440 * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null 441 * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl 442 * or the chronology is not equal this Chronology 443 */ 444 ChronoZonedDateTimeImpl<?> ensureChronoZonedDateTime(Temporal temporal) { 445 @SuppressWarnings("unchecked") 446 ChronoZonedDateTimeImpl<?> other = (ChronoZonedDateTimeImpl<?>) temporal; 447 if (this.equals(other.toLocalDate().getChronology()) == false) { 448 throw new ClassCastException("Chronology mismatch, required: " + getId() 449 + ", supplied: " + other.toLocalDate().getChronology().getId()); 450 } 451 return other; 452 } 453 454 //----------------------------------------------------------------------- 455 /** 456 * Gets the ID of the chronology. 457 * <p> 458 * The ID uniquely identifies the {@code Chronology}. 459 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 460 * 461 * @return the chronology ID, not null 462 * @see #getCalendarType() 463 */ 464 public abstract String getId(); 465 466 /** 467 * Gets the calendar type of the underlying calendar system. 468 * <p> 469 * The calendar type is an identifier defined by the 470 * <em>Unicode Locale Data Markup Language (LDML)</em> specification. 471 * It can be used to lookup the {@code Chronology} using {@link #of(String)}. 472 * It can also be used as part of a locale, accessible via 473 * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. 474 * 475 * @return the calendar system type, null if the calendar is not defined by LDML 476 * @see #getId() 477 */ 478 public abstract String getCalendarType(); 479 480 //----------------------------------------------------------------------- 481 /** 482 * Obtains a local date in this chronology from the era, year-of-era, 483 * month-of-year and day-of-month fields. 484 * 485 * @param era the era of the correct type for the chronology, not null 486 * @param yearOfEra the chronology year-of-era 487 * @param month the chronology month-of-year 488 * @param dayOfMonth the chronology day-of-month 489 * @return the local date in this chronology, not null 490 * @throws DateTimeException if unable to create the date 491 */ 492 public ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) { 493 return date(prolepticYear(era, yearOfEra), month, dayOfMonth); 494 } 495 496 /** 497 * Obtains a local date in this chronology from the proleptic-year, 498 * month-of-year and day-of-month fields. 499 * 500 * @param prolepticYear the chronology proleptic-year 501 * @param month the chronology month-of-year 502 * @param dayOfMonth the chronology day-of-month 503 * @return the local date in this chronology, not null 504 * @throws DateTimeException if unable to create the date 505 */ 506 public abstract ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth); 507 508 /** 509 * Obtains a local date in this chronology from the era, year-of-era and 510 * day-of-year fields. 511 * 512 * @param era the era of the correct type for the chronology, not null 513 * @param yearOfEra the chronology year-of-era 514 * @param dayOfYear the chronology day-of-year 515 * @return the local date in this chronology, not null 516 * @throws DateTimeException if unable to create the date 517 */ 518 public ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { 519 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); 520 } 521 522 /** 523 * Obtains a local date in this chronology from the proleptic-year and 524 * day-of-year fields. 525 * 526 * @param prolepticYear the chronology proleptic-year 527 * @param dayOfYear the chronology day-of-year 528 * @return the local date in this chronology, not null 529 * @throws DateTimeException if unable to create the date 530 */ 531 public abstract ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear); 532 533 //----------------------------------------------------------------------- 534 /** 535 * Obtains the current local date in this chronology from the system clock in the default time-zone. 536 * <p> 537 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 538 * time-zone to obtain the current date. 539 * <p> 540 * Using this method will prevent the ability to use an alternate clock for testing 541 * because the clock is hard-coded. 542 * <p> 543 * This implementation uses {@link #dateNow(Clock)}. 544 * 545 * @return the current local date using the system clock and default time-zone, not null 546 * @throws DateTimeException if unable to create the date 547 */ 548 public ChronoLocalDate dateNow() { 549 return dateNow(Clock.systemDefaultZone()); 550 } 551 552 /** 553 * Obtains the current local date in this chronology from the system clock in the specified time-zone. 554 * <p> 555 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. 556 * Specifying the time-zone avoids dependence on the default time-zone. 557 * <p> 558 * Using this method will prevent the ability to use an alternate clock for testing 559 * because the clock is hard-coded. 560 * 561 * @param zone the zone ID to use, not null 562 * @return the current local date using the system clock, not null 563 * @throws DateTimeException if unable to create the date 564 */ 565 public ChronoLocalDate dateNow(ZoneId zone) { 566 return dateNow(Clock.system(zone)); 567 } 568 569 /** 570 * Obtains the current local date in this chronology from the specified clock. 571 * <p> 572 * This will query the specified clock to obtain the current date - today. 573 * Using this method allows the use of an alternate clock for testing. 574 * The alternate clock may be introduced using {@link Clock dependency injection}. 575 * 576 * @param clock the clock to use, not null 577 * @return the current local date, not null 578 * @throws DateTimeException if unable to create the date 579 */ 580 public ChronoLocalDate dateNow(Clock clock) { 581 Objects.requireNonNull(clock, "clock"); 582 return date(LocalDate.now(clock)); 583 } 584 585 //----------------------------------------------------------------------- 586 /** 587 * Obtains a local date in this chronology from another temporal object. 588 * <p> 589 * This creates a date in this chronology based on the specified temporal. 590 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 591 * which this factory converts to an instance of {@code ChronoLocalDate}. 592 * <p> 593 * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} 594 * field, which is standardized across calendar systems. 595 * <p> 596 * This method matches the signature of the functional interface {@link TemporalQuery} 597 * allowing it to be used as a query via method reference, {@code aChronology::date}. 598 * 599 * @param temporal the temporal object to convert, not null 600 * @return the local date in this chronology, not null 601 * @throws DateTimeException if unable to create the date 602 */ 603 public abstract ChronoLocalDate date(TemporalAccessor temporal); 604 605 /** 606 * Obtains a local date-time in this chronology from another temporal object. 607 * <p> 608 * This creates a date-time in this chronology based on the specified temporal. 609 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 610 * which this factory converts to an instance of {@code ChronoLocalDateTime}. 611 * <p> 612 * The conversion extracts and combines the {@code ChronoLocalDate} and the 613 * {@code LocalTime} from the temporal object. 614 * Implementations are permitted to perform optimizations such as accessing 615 * those fields that are equivalent to the relevant objects. 616 * The result uses this chronology. 617 * <p> 618 * This method matches the signature of the functional interface {@link TemporalQuery} 619 * allowing it to be used as a query via method reference, {@code aChronology::localDateTime}. 620 * 621 * @param temporal the temporal object to convert, not null 622 * @return the local date-time in this chronology, not null 623 * @throws DateTimeException if unable to create the date-time 624 */ 625 public ChronoLocalDateTime<?> localDateTime(TemporalAccessor temporal) { 626 try { 627 return date(temporal).atTime(LocalTime.from(temporal)); 628 } catch (DateTimeException ex) { 629 throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex); 630 } 631 } 632 633 /** 634 * Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object. 635 * <p> 636 * This creates a zoned date-time in this chronology based on the specified temporal. 637 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 638 * which this factory converts to an instance of {@code ChronoZonedDateTime}. 639 * <p> 640 * The conversion will first obtain a {@code ZoneId} from the temporal object, 641 * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain 642 * an {@code Instant}, falling back to a {@code ChronoLocalDateTime} if necessary. 643 * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset} 644 * with {@code Instant} or {@code ChronoLocalDateTime}. 645 * Implementations are permitted to perform optimizations such as accessing 646 * those fields that are equivalent to the relevant objects. 647 * The result uses this chronology. 648 * <p> 649 * This method matches the signature of the functional interface {@link TemporalQuery} 650 * allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}. 651 * 652 * @param temporal the temporal object to convert, not null 653 * @return the zoned date-time in this chronology, not null 654 * @throws DateTimeException if unable to create the date-time 655 */ 656 public ChronoZonedDateTime<?> zonedDateTime(TemporalAccessor temporal) { 657 try { 658 ZoneId zone = ZoneId.from(temporal); 659 try { 660 Instant instant = Instant.from(temporal); 661 return zonedDateTime(instant, zone); 662 663 } catch (DateTimeException ex1) { 664 ChronoLocalDateTimeImpl cldt = ensureChronoLocalDateTime(localDateTime(temporal)); 665 return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null); 666 } 667 } catch (DateTimeException ex) { 668 throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex); 669 } 670 } 671 672 /** 673 * Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}. 674 * <p> 675 * This creates a zoned date-time with the same instant as that specified. 676 * 677 * @param instant the instant to create the date-time from, not null 678 * @param zone the time-zone, not null 679 * @return the zoned date-time, not null 680 * @throws DateTimeException if the result exceeds the supported range 681 */ 682 public ChronoZonedDateTime<?> zonedDateTime(Instant instant, ZoneId zone) { 683 return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone); 684 } 685 686 //----------------------------------------------------------------------- 687 /** 688 * Checks if the specified year is a leap year. 689 * <p> 690 * A leap-year is a year of a longer length than normal. 691 * The exact meaning is determined by the chronology according to the following constraints. 692 * <p><ul> 693 * <li>a leap-year must imply a year-length longer than a non leap-year. 694 * <li>a chronology that does not support the concept of a year must return false. 695 * </ul><p> 696 * 697 * @param prolepticYear the proleptic-year to check, not validated for range 698 * @return true if the year is a leap year 699 */ 700 public abstract boolean isLeapYear(long prolepticYear); 701 702 /** 703 * Calculates the proleptic-year given the era and year-of-era. 704 * <p> 705 * This combines the era and year-of-era into the single proleptic-year field. 706 * 707 * @param era the era of the correct type for the chronology, not null 708 * @param yearOfEra the chronology year-of-era 709 * @return the proleptic-year 710 * @throws DateTimeException if unable to convert 711 */ 712 public abstract int prolepticYear(Era era, int yearOfEra); 713 714 /** 715 * Creates the chronology era object from the numeric value. 716 * <p> 717 * The era is, conceptually, the largest division of the time-line. 718 * Most calendar systems have a single epoch dividing the time-line into two eras. 719 * However, some have multiple eras, such as one for the reign of each leader. 720 * The exact meaning is determined by the chronology according to the following constraints. 721 * <p> 722 * The era in use at 1970-01-01 must have the value 1. 723 * Later eras must have sequentially higher values. 724 * Earlier eras must have sequentially lower values. 725 * Each chronology must refer to an enum or similar singleton to provide the era values. 726 * <p> 727 * This method returns the singleton era of the correct type for the specified era value. 728 * 729 * @param eraValue the era value 730 * @return the calendar system era, not null 731 * @throws DateTimeException if unable to create the era 732 */ 733 public abstract Era eraOf(int eraValue); 734 735 /** 736 * Gets the list of eras for the chronology. 737 * <p> 738 * Most calendar systems have an era, within which the year has meaning. 739 * If the calendar system does not support the concept of eras, an empty 740 * list must be returned. 741 * 742 * @return the list of eras for the chronology, may be immutable, not null 743 */ 744 public abstract List<Era> eras(); 745 746 //----------------------------------------------------------------------- 747 /** 748 * Gets the range of valid values for the specified field. 749 * <p> 750 * All fields can be expressed as a {@code long} integer. 751 * This method returns an object that describes the valid range for that value. 752 * <p> 753 * Note that the result only describes the minimum and maximum valid values 754 * and it is important not to read too much into them. For example, there 755 * could be values within the range that are invalid for the field. 756 * <p> 757 * This method will return a result whether or not the chronology supports the field. 758 * 759 * @param field the field to get the range for, not null 760 * @return the range of valid values for the field, not null 761 * @throws DateTimeException if the range for the field cannot be obtained 762 */ 763 public abstract ValueRange range(ChronoField field); 764 765 //----------------------------------------------------------------------- 766 /** 767 * Gets the textual representation of this chronology. 768 * <p> 769 * This returns the textual name used to identify the chronology, 770 * suitable for presentation to the user. 771 * The parameters control the style of the returned text and the locale. 772 * 773 * @param style the style of the text required, not null 774 * @param locale the locale to use, not null 775 * @return the text value of the chronology, not null 776 */ 777 public String getDisplayName(TextStyle style, Locale locale) { 778 return new DateTimeFormatterBuilder().appendChronologyText(style).toFormatter(locale).format(new TemporalAccessor() { 779 @Override 780 public boolean isSupported(TemporalField field) { 781 return false; 782 } 783 @Override 784 public long getLong(TemporalField field) { 785 throw new DateTimeException("Unsupported field: " + field); 786 } 787 @SuppressWarnings("unchecked") 788 @Override 789 public <R> R query(TemporalQuery<R> query) { 790 if (query == Queries.chronology()) { 791 return (R) Chronology.this; 792 } 793 return TemporalAccessor.super.query(query); 794 } 795 }); 796 } 797 798 //----------------------------------------------------------------------- 799 /** 800 * Compares this chronology to another chronology. 801 * <p> 802 * The comparison order first by the chronology ID string, then by any 803 * additional information specific to the subclass. 804 * It is "consistent with equals", as defined by {@link Comparable}. 805 * <p> 806 * The default implementation compares the chronology ID. 807 * Subclasses must compare any additional state that they store. 808 * 809 * @param other the other chronology to compare to, not null 810 * @return the comparator value, negative if less, positive if greater 811 */ 812 @Override 813 public int compareTo(Chronology other) { 814 return getId().compareTo(other.getId()); 815 } 816 817 /** 818 * Checks if this chronology is equal to another chronology. 819 * <p> 820 * The comparison is based on the entire state of the object. 821 * <p> 822 * The default implementation checks the type and calls {@link #compareTo(Chronology)}. 823 * 824 * @param obj the object to check, null returns false 825 * @return true if this is equal to the other chronology 826 */ 827 @Override 828 public boolean equals(Object obj) { 829 if (this == obj) { 830 return true; 831 } 832 if (obj instanceof Chronology) { 833 return compareTo((Chronology) obj) == 0; 834 } 835 return false; 836 } 837 838 /** 839 * A hash code for this chronology. 840 * <p> 841 * The default implementation is based on the ID and class. 842 * Subclasses should add any additional state that they store. 843 * 844 * @return a suitable hash code 845 */ 846 @Override 847 public int hashCode() { 848 return getClass().hashCode() ^ getId().hashCode(); 849 } 850 851 //----------------------------------------------------------------------- 852 /** 853 * Outputs this chronology as a {@code String}, using the ID. 854 * 855 * @return a string representation of this chronology, not null 856 */ 857 @Override 858 public String toString() { 859 return getId(); 860 } 861 862 //----------------------------------------------------------------------- 863 /** 864 * Writes the object using a 865 * <a href="../../../serialized-form.html#java.time.temporal.Ser">dedicated serialized form</a>. 866 * <pre> 867 * out.writeByte(7); // identifies this as a Chronology 868 * out.writeUTF(chronoId); 869 * </pre> 870 * 871 * @return the instance of {@code Ser}, not null 872 */ 873 private Object writeReplace() { 874 return new Ser(Ser.CHRONO_TYPE, this); 875 } 876 877 /** 878 * Defend against malicious streams. 879 * @return never 880 * @throws InvalidObjectException always 881 */ 882 private Object readResolve() throws ObjectStreamException { 883 throw new InvalidObjectException("Deserialization via serialization delegate"); 884 } 885 886 void writeExternal(DataOutput out) throws IOException { 887 out.writeUTF(getId()); 888 } 889 890 static Chronology readExternal(DataInput in) throws IOException { 891 String id = in.readUTF(); 892 return Chronology.of(id); 893 } 894 895 } |