1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 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 }