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 static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
65 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
66 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
67 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
68 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
69 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
70 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
71 import static java.time.temporal.ChronoField.EPOCH_DAY;
72 import static java.time.temporal.ChronoField.ERA;
73 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
74 import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
75 import static java.time.temporal.ChronoField.YEAR;
76 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
77 import static java.time.temporal.TemporalAdjuster.nextOrSame;
78
79 import java.io.DataInput;
80 import java.io.DataOutput;
81 import java.io.IOException;
82 import java.io.InvalidObjectException;
83 import java.io.ObjectStreamException;
84 import java.io.Serializable;
85 import java.time.Clock;
86 import java.time.DateTimeException;
87 import java.time.DayOfWeek;
88 import java.time.Instant;
89 import java.time.LocalDate;
90 import java.time.LocalTime;
91 import java.time.ZoneId;
92 import java.time.format.DateTimeFormatterBuilder;
93 import java.time.format.ResolverStyle;
94 import java.time.format.TextStyle;
95 import java.time.temporal.ChronoField;
96 import java.time.temporal.ChronoUnit;
97 import java.time.temporal.Temporal;
98 import java.time.temporal.TemporalAccessor;
99 import java.time.temporal.TemporalField;
100 import java.time.temporal.TemporalQuery;
101 import java.time.temporal.UnsupportedTemporalTypeException;
102 import java.time.temporal.ValueRange;
103 import java.util.Comparator;
104 import java.util.HashSet;
105 import java.util.List;
106 import java.util.Locale;
107 import java.util.Map;
108 import java.util.Objects;
109 import java.util.ServiceLoader;
110 import java.util.Set;
111 import java.util.concurrent.ConcurrentHashMap;
112
113 import sun.util.logging.PlatformLogger;
114
115 /**
116 * A calendar system, used to organize and identify dates.
117 * <p>
118 * The main date and time API is built on the ISO calendar system.
171 * For lookup by id or calendarType, the system provided calendars are found
172 * first followed by application provided calendars.
173 * <p>
174 * Each chronology must define a chronology ID that is unique within the system.
175 * If the chronology represents a calendar system defined by the
176 * CLDR specification then the calendar type is the concatenation of the
177 * CLDR type and, if applicable, the CLDR variant,
178 *
179 * @implSpec
180 * This class must be implemented with care to ensure other classes operate correctly.
181 * All implementations that can be instantiated must be final, immutable and thread-safe.
182 * Subclasses should be Serializable wherever possible.
183 *
184 * @since 1.8
185 */
186 public abstract class Chronology implements Comparable<Chronology> {
187
188 /**
189 * ChronoLocalDate order constant.
190 */
191 static final Comparator<ChronoLocalDate<?>> DATE_ORDER =
192 (Comparator<ChronoLocalDate<?>> & Serializable) (date1, date2) -> {
193 return Long.compare(date1.toEpochDay(), date2.toEpochDay());
194 };
195 /**
196 * ChronoLocalDateTime order constant.
197 */
198 static final Comparator<ChronoLocalDateTime<?>> DATE_TIME_ORDER =
199 (Comparator<ChronoLocalDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
200 int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
201 if (cmp == 0) {
202 cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
203 }
204 return cmp;
205 };
206 /**
207 * ChronoZonedDateTime order constant.
208 */
209 static final Comparator<ChronoZonedDateTime<?>> INSTANT_ORDER =
210 (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
211 int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
212 if (cmp == 0) {
465 HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
466
467 /// Add in Chronologies from the ServiceLoader configuration
468 @SuppressWarnings("rawtypes")
469 ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
470 for (Chronology chrono : loader) {
471 chronos.add(chrono);
472 }
473 return chronos;
474 }
475
476 //-----------------------------------------------------------------------
477 /**
478 * Creates an instance.
479 */
480 protected Chronology() {
481 }
482
483 //-----------------------------------------------------------------------
484 /**
485 * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology.
486 *
487 * @param temporal a date-time to cast, not null
488 * @return the date-time checked and cast to {@code ChronoLocalDate}, not null
489 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate
490 * or the chronology is not equal this Chronology
491 */
492 ChronoLocalDate<?> ensureChronoLocalDate(Temporal temporal) {
493 @SuppressWarnings("unchecked")
494 ChronoLocalDate<?> other = (ChronoLocalDate<?>) temporal;
495 if (this.equals(other.getChronology()) == false) {
496 throw new ClassCastException("Chronology mismatch, expected: " + getId() + ", actual: " + other.getChronology().getId());
497 }
498 return other;
499 }
500
501 /**
502 * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology.
503 *
504 * @param temporal a date-time to cast, not null
505 * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null
506 * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl
507 * or the chronology is not equal this Chronology
508 */
509 ChronoLocalDateTimeImpl<?> ensureChronoLocalDateTime(Temporal temporal) {
510 @SuppressWarnings("unchecked")
511 ChronoLocalDateTimeImpl<?> other = (ChronoLocalDateTimeImpl<?>) temporal;
512 if (this.equals(other.toLocalDate().getChronology()) == false) {
513 throw new ClassCastException("Chronology mismatch, required: " + getId()
514 + ", supplied: " + other.toLocalDate().getChronology().getId());
515 }
516 return other;
517 }
518
519 /**
520 * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology.
521 *
522 * @param temporal a date-time to cast, not null
523 * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null
524 * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl
525 * or the chronology is not equal this Chronology
526 */
527 ChronoZonedDateTimeImpl<?> ensureChronoZonedDateTime(Temporal temporal) {
528 @SuppressWarnings("unchecked")
529 ChronoZonedDateTimeImpl<?> other = (ChronoZonedDateTimeImpl<?>) temporal;
530 if (this.equals(other.toLocalDate().getChronology()) == false) {
531 throw new ClassCastException("Chronology mismatch, required: " + getId()
532 + ", supplied: " + other.toLocalDate().getChronology().getId());
533 }
534 return other;
535 }
536
537 //-----------------------------------------------------------------------
538 /**
539 * Gets the ID of the chronology.
540 * <p>
541 * The ID uniquely identifies the {@code Chronology}.
542 * It can be used to lookup the {@code Chronology} using {@link #of(String)}.
543 *
544 * @return the chronology ID, not null
545 * @see #getCalendarType()
546 */
547 public abstract String getId();
548
549 /**
550 * Gets the calendar type of the calendar system.
551 * <p>
552 * The calendar type is an identifier defined by the CLDR and
553 * <em>Unicode Locale Data Markup Language (LDML)</em> specifications
554 * to uniquely identification a calendar.
555 * The {@code getCalendarType} is the concatenation of the CLDR calendar type
556 * and the variant, if applicable, is appended separated by "-".
557 * The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
558 *
559 * @return the calendar system type, null if the calendar is not defined by CLDR/LDML
560 * @see #getId()
561 */
562 public abstract String getCalendarType();
563
564 //-----------------------------------------------------------------------
565 /**
566 * Obtains a local date in this chronology from the era, year-of-era,
567 * month-of-year and day-of-month fields.
568 *
569 * @param era the era of the correct type for the chronology, not null
570 * @param yearOfEra the chronology year-of-era
571 * @param month the chronology month-of-year
572 * @param dayOfMonth the chronology day-of-month
573 * @return the local date in this chronology, not null
574 * @throws DateTimeException if unable to create the date
575 * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
576 */
577 public ChronoLocalDate<?> date(Era era, int yearOfEra, int month, int dayOfMonth) {
578 return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
579 }
580
581 /**
582 * Obtains a local date in this chronology from the proleptic-year,
583 * month-of-year and day-of-month fields.
584 *
585 * @param prolepticYear the chronology proleptic-year
586 * @param month the chronology month-of-year
587 * @param dayOfMonth the chronology day-of-month
588 * @return the local date in this chronology, not null
589 * @throws DateTimeException if unable to create the date
590 */
591 public abstract ChronoLocalDate<?> date(int prolepticYear, int month, int dayOfMonth);
592
593 /**
594 * Obtains a local date in this chronology from the era, year-of-era and
595 * day-of-year fields.
596 *
597 * @param era the era of the correct type for the chronology, not null
598 * @param yearOfEra the chronology year-of-era
599 * @param dayOfYear the chronology day-of-year
600 * @return the local date in this chronology, not null
601 * @throws DateTimeException if unable to create the date
602 * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
603 */
604 public ChronoLocalDate<?> dateYearDay(Era era, int yearOfEra, int dayOfYear) {
605 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
606 }
607
608 /**
609 * Obtains a local date in this chronology from the proleptic-year and
610 * day-of-year fields.
611 *
612 * @param prolepticYear the chronology proleptic-year
613 * @param dayOfYear the chronology day-of-year
614 * @return the local date in this chronology, not null
615 * @throws DateTimeException if unable to create the date
616 */
617 public abstract ChronoLocalDate<?> dateYearDay(int prolepticYear, int dayOfYear);
618
619 /**
620 * Obtains a local date in this chronology from the epoch-day.
621 * <p>
622 * The definition of {@link ChronoField#EPOCH_DAY EPOCH_DAY} is the same
623 * for all calendar systems, thus it can be used for conversion.
624 *
625 * @param epochDay the epoch day
626 * @return the local date in this chronology, not null
627 * @throws DateTimeException if unable to create the date
628 */
629 public abstract ChronoLocalDate<?> dateEpochDay(long epochDay);
630
631 //-----------------------------------------------------------------------
632 /**
633 * Obtains the current local date in this chronology from the system clock in the default time-zone.
634 * <p>
635 * This will query the {@link Clock#systemDefaultZone() system clock} in the default
636 * time-zone to obtain the current date.
637 * <p>
638 * Using this method will prevent the ability to use an alternate clock for testing
639 * because the clock is hard-coded.
640 * <p>
641 * This implementation uses {@link #dateNow(Clock)}.
642 *
643 * @return the current local date using the system clock and default time-zone, not null
644 * @throws DateTimeException if unable to create the date
645 */
646 public ChronoLocalDate<?> dateNow() {
647 return dateNow(Clock.systemDefaultZone());
648 }
649
650 /**
651 * Obtains the current local date in this chronology from the system clock in the specified time-zone.
652 * <p>
653 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
654 * Specifying the time-zone avoids dependence on the default time-zone.
655 * <p>
656 * Using this method will prevent the ability to use an alternate clock for testing
657 * because the clock is hard-coded.
658 *
659 * @param zone the zone ID to use, not null
660 * @return the current local date using the system clock, not null
661 * @throws DateTimeException if unable to create the date
662 */
663 public ChronoLocalDate<?> dateNow(ZoneId zone) {
664 return dateNow(Clock.system(zone));
665 }
666
667 /**
668 * Obtains the current local date in this chronology from the specified clock.
669 * <p>
670 * This will query the specified clock to obtain the current date - today.
671 * Using this method allows the use of an alternate clock for testing.
672 * The alternate clock may be introduced using {@link Clock dependency injection}.
673 *
674 * @param clock the clock to use, not null
675 * @return the current local date, not null
676 * @throws DateTimeException if unable to create the date
677 */
678 public ChronoLocalDate<?> dateNow(Clock clock) {
679 Objects.requireNonNull(clock, "clock");
680 return date(LocalDate.now(clock));
681 }
682
683 //-----------------------------------------------------------------------
684 /**
685 * Obtains a local date in this chronology from another temporal object.
686 * <p>
687 * This obtains a date in this chronology based on the specified temporal.
688 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
689 * which this factory converts to an instance of {@code ChronoLocalDate}.
690 * <p>
691 * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
692 * field, which is standardized across calendar systems.
693 * <p>
694 * This method matches the signature of the functional interface {@link TemporalQuery}
695 * allowing it to be used as a query via method reference, {@code aChronology::date}.
696 *
697 * @param temporal the temporal object to convert, not null
698 * @return the local date in this chronology, not null
699 * @throws DateTimeException if unable to create the date
700 * @see ChronoLocalDate#from(TemporalAccessor)
701 */
702 public abstract ChronoLocalDate<?> date(TemporalAccessor temporal);
703
704 /**
705 * Obtains a local date-time in this chronology from another temporal object.
706 * <p>
707 * This obtains a date-time in this chronology based on the specified temporal.
708 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
709 * which this factory converts to an instance of {@code ChronoLocalDateTime}.
710 * <p>
711 * The conversion extracts and combines the {@code ChronoLocalDate} and the
712 * {@code LocalTime} from the temporal object.
713 * Implementations are permitted to perform optimizations such as accessing
714 * those fields that are equivalent to the relevant objects.
715 * The result uses this chronology.
716 * <p>
717 * This method matches the signature of the functional interface {@link TemporalQuery}
718 * allowing it to be used as a query via method reference, {@code aChronology::localDateTime}.
719 *
720 * @param temporal the temporal object to convert, not null
721 * @return the local date-time in this chronology, not null
722 * @throws DateTimeException if unable to create the date-time
723 * @see ChronoLocalDateTime#from(TemporalAccessor)
724 */
725 public ChronoLocalDateTime<?> localDateTime(TemporalAccessor temporal) {
726 try {
727 return date(temporal).atTime(LocalTime.from(temporal));
728 } catch (DateTimeException ex) {
729 throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex);
730 }
731 }
732
733 /**
734 * Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
735 * <p>
736 * This obtains a zoned date-time in this chronology based on the specified temporal.
737 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
738 * which this factory converts to an instance of {@code ChronoZonedDateTime}.
739 * <p>
740 * The conversion will first obtain a {@code ZoneId} from the temporal object,
741 * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
742 * an {@code Instant}, falling back to a {@code ChronoLocalDateTime} if necessary.
743 * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
744 * with {@code Instant} or {@code ChronoLocalDateTime}.
745 * Implementations are permitted to perform optimizations such as accessing
746 * those fields that are equivalent to the relevant objects.
747 * The result uses this chronology.
748 * <p>
749 * This method matches the signature of the functional interface {@link TemporalQuery}
750 * allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}.
751 *
752 * @param temporal the temporal object to convert, not null
753 * @return the zoned date-time in this chronology, not null
754 * @throws DateTimeException if unable to create the date-time
755 * @see ChronoZonedDateTime#from(TemporalAccessor)
756 */
757 public ChronoZonedDateTime<?> zonedDateTime(TemporalAccessor temporal) {
758 try {
759 ZoneId zone = ZoneId.from(temporal);
760 try {
761 Instant instant = Instant.from(temporal);
762 return zonedDateTime(instant, zone);
763
764 } catch (DateTimeException ex1) {
765 @SuppressWarnings("rawtypes")
766 ChronoLocalDateTimeImpl cldt = ensureChronoLocalDateTime(localDateTime(temporal));
767 return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
768 }
769 } catch (DateTimeException ex) {
770 throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
771 }
772 }
773
774 /**
775 * Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
776 * <p>
777 * This obtains a zoned date-time with the same instant as that specified.
778 *
779 * @param instant the instant to create the date-time from, not null
780 * @param zone the time-zone, not null
781 * @return the zoned date-time, not null
782 * @throws DateTimeException if the result exceeds the supported range
783 */
784 public ChronoZonedDateTime<?> zonedDateTime(Instant instant, ZoneId zone) {
785 return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
786 }
787
788 //-----------------------------------------------------------------------
789 /**
790 * Checks if the specified year is a leap year.
791 * <p>
792 * A leap-year is a year of a longer length than normal.
793 * The exact meaning is determined by the chronology according to the following constraints.
794 * <p><ul>
795 * <li>a leap-year must imply a year-length longer than a non leap-year.
796 * <li>a chronology that does not support the concept of a year must return false.
797 * </ul><p>
798 *
799 * @param prolepticYear the proleptic-year to check, not validated for range
800 * @return true if the year is a leap year
801 */
802 public abstract boolean isLeapYear(long prolepticYear);
803
804 /**
912 @Override
913 public <R> R query(TemporalQuery<R> query) {
914 if (query == TemporalQuery.chronology()) {
915 return (R) Chronology.this;
916 }
917 return TemporalAccessor.super.query(query);
918 }
919 };
920 }
921
922 //-----------------------------------------------------------------------
923 /**
924 * Resolves parsed {@code ChronoField} values into a date during parsing.
925 * <p>
926 * Most {@code TemporalField} implementations are resolved using the
927 * resolve method on the field. By contrast, the {@code ChronoField} class
928 * defines fields that only have meaning relative to the chronology.
929 * As such, {@code ChronoField} date fields are resolved here in the
930 * context of a specific chronology.
931 * <p>
932 * The default implementation is suitable for most calendar systems.
933 * If {@link ChronoField#YEAR_OF_ERA} is found without an {@link ChronoField#ERA}
934 * then the last era in {@link #eras()} is used.
935 * The implementation assumes a 7 day week, that the first day-of-month
936 * has the value 1, and that first day-of-year has the value 1.
937 *
938 * @param fieldValues the map of fields to values, which can be updated, not null
939 * @param resolverStyle the requested type of resolve, not null
940 * @return the resolved date, null if insufficient information to create a date
941 * @throws DateTimeException if the date cannot be resolved, typically
942 * because of a conflict in the input data
943 */
944 public ChronoLocalDate<?> resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
945 // check epoch-day before inventing era
946 if (fieldValues.containsKey(EPOCH_DAY)) {
947 return dateEpochDay(fieldValues.remove(EPOCH_DAY));
948 }
949
950 // fix proleptic month before inventing era
951 Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
952 if (pMonth != null) {
953 // first day-of-month is likely to be safest for setting proleptic-month
954 // cannot add to year zero, as not all chronologies have a year zero
955 ChronoLocalDate<?> chronoDate = dateNow()
956 .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
957 addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
958 addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
959 }
960
961 // invent era if necessary to resolve year-of-era
962 Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
963 if (yoeLong != null) {
964 Long eraLong = fieldValues.remove(ERA);
965 int yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
966 if (eraLong != null) {
967 Era eraObj = eraOf(Math.toIntExact(eraLong));
968 addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
969 } else if (fieldValues.containsKey(YEAR)) {
970 int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
971 ChronoLocalDate<?> chronoDate = dateYearDay(year, 1);
972 addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
973 } else {
974 List<Era> eras = eras();
975 if (eras.isEmpty()) {
976 addFieldValue(fieldValues, YEAR, yoe);
977 } else {
978 Era eraObj = eras.get(eras.size() - 1);
979 addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
980 }
981 }
982 }
983
984 // build date
985 if (fieldValues.containsKey(YEAR)) {
986 if (fieldValues.containsKey(MONTH_OF_YEAR)) {
987 if (fieldValues.containsKey(DAY_OF_MONTH)) {
988 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
989 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
990 int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
991 return date(y, moy, dom);
992 }
993 if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
994 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
995 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
996 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
997 int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
998 int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
999 ChronoLocalDate<?> chronoDate = date(y, moy, 1);
1000 return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
1001 }
1002 if (fieldValues.containsKey(DAY_OF_WEEK)) {
1003 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1004 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
1005 int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
1006 int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
1007 ChronoLocalDate<?> chronoDate = date(y, moy, 1);
1008 return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
1009 }
1010 }
1011 }
1012 if (fieldValues.containsKey(DAY_OF_YEAR)) {
1013 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1014 int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
1015 return dateYearDay(y, doy);
1016 }
1017 if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
1018 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
1019 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1020 int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
1021 int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
1022 ChronoLocalDate<?> chronoDate = dateYearDay(y, 1);
1023 return chronoDate.plus((aw - 1) * 7 + (ad - 1), ChronoUnit.DAYS);
1024 }
1025 if (fieldValues.containsKey(DAY_OF_WEEK)) {
1026 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1027 int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
1028 int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
1029 ChronoLocalDate<?> chronoDate = dateYearDay(y, 1);
1030 return chronoDate.plus((aw - 1) * 7, ChronoUnit.DAYS).with(nextOrSame(DayOfWeek.of(dow)));
1031 }
1032 }
1033 }
1034 return null;
1035 }
1036
1037 /**
1038 * Adds a field-value pair to the map, checking for conflicts.
1039 * <p>
1040 * If the field is not already present, then the field-value pair is added to the map.
1041 * If the field is already present and it has the same value as that specified, no action occurs.
1042 * If the field is already present and it has a different value to that specified, then
1043 * an exception is thrown.
1044 *
1045 * @param field the field to add, not null
1046 * @param value the value to add, not null
1047 * @throws DateTimeException if the field is already present with a different value
1048 */
1049 void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
1050 Long old = fieldValues.get(field); // check first for better error message
1051 if (old != null && old.longValue() != value) {
1052 throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
1053 }
1054 fieldValues.put(field, value);
|
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 static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH;
65 import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR;
66 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH;
67 import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
68 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
69 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
70 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
71 import static java.time.temporal.ChronoField.EPOCH_DAY;
72 import static java.time.temporal.ChronoField.ERA;
73 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
74 import static java.time.temporal.ChronoField.PROLEPTIC_MONTH;
75 import static java.time.temporal.ChronoField.YEAR;
76 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
77 import static java.time.temporal.ChronoUnit.DAYS;
78 import static java.time.temporal.ChronoUnit.MONTHS;
79 import static java.time.temporal.ChronoUnit.WEEKS;
80 import static java.time.temporal.TemporalAdjuster.nextOrSame;
81
82 import java.io.DataInput;
83 import java.io.DataOutput;
84 import java.io.IOException;
85 import java.io.InvalidObjectException;
86 import java.io.ObjectStreamException;
87 import java.io.Serializable;
88 import java.time.Clock;
89 import java.time.DateTimeException;
90 import java.time.DayOfWeek;
91 import java.time.Instant;
92 import java.time.LocalDate;
93 import java.time.LocalTime;
94 import java.time.Month;
95 import java.time.Year;
96 import java.time.ZoneId;
97 import java.time.format.DateTimeFormatterBuilder;
98 import java.time.format.ResolverStyle;
99 import java.time.format.TextStyle;
100 import java.time.temporal.ChronoField;
101 import java.time.temporal.Temporal;
102 import java.time.temporal.TemporalAccessor;
103 import java.time.temporal.TemporalAdjuster;
104 import java.time.temporal.TemporalField;
105 import java.time.temporal.TemporalQuery;
106 import java.time.temporal.UnsupportedTemporalTypeException;
107 import java.time.temporal.ValueRange;
108 import java.util.Comparator;
109 import java.util.HashSet;
110 import java.util.List;
111 import java.util.Locale;
112 import java.util.Map;
113 import java.util.Objects;
114 import java.util.ServiceLoader;
115 import java.util.Set;
116 import java.util.concurrent.ConcurrentHashMap;
117
118 import sun.util.logging.PlatformLogger;
119
120 /**
121 * A calendar system, used to organize and identify dates.
122 * <p>
123 * The main date and time API is built on the ISO calendar system.
176 * For lookup by id or calendarType, the system provided calendars are found
177 * first followed by application provided calendars.
178 * <p>
179 * Each chronology must define a chronology ID that is unique within the system.
180 * If the chronology represents a calendar system defined by the
181 * CLDR specification then the calendar type is the concatenation of the
182 * CLDR type and, if applicable, the CLDR variant,
183 *
184 * @implSpec
185 * This class must be implemented with care to ensure other classes operate correctly.
186 * All implementations that can be instantiated must be final, immutable and thread-safe.
187 * Subclasses should be Serializable wherever possible.
188 *
189 * @since 1.8
190 */
191 public abstract class Chronology implements Comparable<Chronology> {
192
193 /**
194 * ChronoLocalDate order constant.
195 */
196 static final Comparator<ChronoLocalDate> DATE_ORDER =
197 (Comparator<ChronoLocalDate> & Serializable) (date1, date2) -> {
198 return Long.compare(date1.toEpochDay(), date2.toEpochDay());
199 };
200 /**
201 * ChronoLocalDateTime order constant.
202 */
203 static final Comparator<ChronoLocalDateTime<?>> DATE_TIME_ORDER =
204 (Comparator<ChronoLocalDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
205 int cmp = Long.compare(dateTime1.toLocalDate().toEpochDay(), dateTime2.toLocalDate().toEpochDay());
206 if (cmp == 0) {
207 cmp = Long.compare(dateTime1.toLocalTime().toNanoOfDay(), dateTime2.toLocalTime().toNanoOfDay());
208 }
209 return cmp;
210 };
211 /**
212 * ChronoZonedDateTime order constant.
213 */
214 static final Comparator<ChronoZonedDateTime<?>> INSTANT_ORDER =
215 (Comparator<ChronoZonedDateTime<?>> & Serializable) (dateTime1, dateTime2) -> {
216 int cmp = Long.compare(dateTime1.toEpochSecond(), dateTime2.toEpochSecond());
217 if (cmp == 0) {
470 HashSet<Chronology> chronos = new HashSet<>(CHRONOS_BY_ID.values());
471
472 /// Add in Chronologies from the ServiceLoader configuration
473 @SuppressWarnings("rawtypes")
474 ServiceLoader<Chronology> loader = ServiceLoader.load(Chronology.class);
475 for (Chronology chrono : loader) {
476 chronos.add(chrono);
477 }
478 return chronos;
479 }
480
481 //-----------------------------------------------------------------------
482 /**
483 * Creates an instance.
484 */
485 protected Chronology() {
486 }
487
488 //-----------------------------------------------------------------------
489 /**
490 * Gets the ID of the chronology.
491 * <p>
492 * The ID uniquely identifies the {@code Chronology}.
493 * It can be used to lookup the {@code Chronology} using {@link #of(String)}.
494 *
495 * @return the chronology ID, not null
496 * @see #getCalendarType()
497 */
498 public abstract String getId();
499
500 /**
501 * Gets the calendar type of the calendar system.
502 * <p>
503 * The calendar type is an identifier defined by the CLDR and
504 * <em>Unicode Locale Data Markup Language (LDML)</em> specifications
505 * to uniquely identification a calendar.
506 * The {@code getCalendarType} is the concatenation of the CLDR calendar type
507 * and the variant, if applicable, is appended separated by "-".
508 * The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}.
509 *
510 * @return the calendar system type, null if the calendar is not defined by CLDR/LDML
511 * @see #getId()
512 */
513 public abstract String getCalendarType();
514
515 //-----------------------------------------------------------------------
516 /**
517 * Obtains a local date in this chronology from the era, year-of-era,
518 * month-of-year and day-of-month fields.
519 *
520 * @param era the era of the correct type for the chronology, not null
521 * @param yearOfEra the chronology year-of-era
522 * @param month the chronology month-of-year
523 * @param dayOfMonth the chronology day-of-month
524 * @return the local date in this chronology, not null
525 * @throws DateTimeException if unable to create the date
526 * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
527 */
528 public ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
529 return date(prolepticYear(era, yearOfEra), month, dayOfMonth);
530 }
531
532 /**
533 * Obtains a local date in this chronology from the proleptic-year,
534 * month-of-year and day-of-month fields.
535 *
536 * @param prolepticYear the chronology proleptic-year
537 * @param month the chronology month-of-year
538 * @param dayOfMonth the chronology day-of-month
539 * @return the local date in this chronology, not null
540 * @throws DateTimeException if unable to create the date
541 */
542 public abstract ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth);
543
544 /**
545 * Obtains a local date in this chronology from the era, year-of-era and
546 * day-of-year fields.
547 *
548 * @param era the era of the correct type for the chronology, not null
549 * @param yearOfEra the chronology year-of-era
550 * @param dayOfYear the chronology day-of-year
551 * @return the local date in this chronology, not null
552 * @throws DateTimeException if unable to create the date
553 * @throws ClassCastException if the {@code era} is not of the correct type for the chronology
554 */
555 public ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
556 return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear);
557 }
558
559 /**
560 * Obtains a local date in this chronology from the proleptic-year and
561 * day-of-year fields.
562 *
563 * @param prolepticYear the chronology proleptic-year
564 * @param dayOfYear the chronology day-of-year
565 * @return the local date in this chronology, not null
566 * @throws DateTimeException if unable to create the date
567 */
568 public abstract ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear);
569
570 /**
571 * Obtains a local date in this chronology from the epoch-day.
572 * <p>
573 * The definition of {@link ChronoField#EPOCH_DAY EPOCH_DAY} is the same
574 * for all calendar systems, thus it can be used for conversion.
575 *
576 * @param epochDay the epoch day
577 * @return the local date in this chronology, not null
578 * @throws DateTimeException if unable to create the date
579 */
580 public abstract ChronoLocalDate dateEpochDay(long epochDay);
581
582 //-----------------------------------------------------------------------
583 /**
584 * Obtains the current local date in this chronology from the system clock in the default time-zone.
585 * <p>
586 * This will query the {@link Clock#systemDefaultZone() system clock} in the default
587 * time-zone to obtain the current date.
588 * <p>
589 * Using this method will prevent the ability to use an alternate clock for testing
590 * because the clock is hard-coded.
591 * <p>
592 * This implementation uses {@link #dateNow(Clock)}.
593 *
594 * @return the current local date using the system clock and default time-zone, not null
595 * @throws DateTimeException if unable to create the date
596 */
597 public ChronoLocalDate dateNow() {
598 return dateNow(Clock.systemDefaultZone());
599 }
600
601 /**
602 * Obtains the current local date in this chronology from the system clock in the specified time-zone.
603 * <p>
604 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
605 * Specifying the time-zone avoids dependence on the default time-zone.
606 * <p>
607 * Using this method will prevent the ability to use an alternate clock for testing
608 * because the clock is hard-coded.
609 *
610 * @param zone the zone ID to use, not null
611 * @return the current local date using the system clock, not null
612 * @throws DateTimeException if unable to create the date
613 */
614 public ChronoLocalDate dateNow(ZoneId zone) {
615 return dateNow(Clock.system(zone));
616 }
617
618 /**
619 * Obtains the current local date in this chronology from the specified clock.
620 * <p>
621 * This will query the specified clock to obtain the current date - today.
622 * Using this method allows the use of an alternate clock for testing.
623 * The alternate clock may be introduced using {@link Clock dependency injection}.
624 *
625 * @param clock the clock to use, not null
626 * @return the current local date, not null
627 * @throws DateTimeException if unable to create the date
628 */
629 public ChronoLocalDate dateNow(Clock clock) {
630 Objects.requireNonNull(clock, "clock");
631 return date(LocalDate.now(clock));
632 }
633
634 //-----------------------------------------------------------------------
635 /**
636 * Obtains a local date in this chronology from another temporal object.
637 * <p>
638 * This obtains a date in this chronology based on the specified temporal.
639 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
640 * which this factory converts to an instance of {@code ChronoLocalDate}.
641 * <p>
642 * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
643 * field, which is standardized across calendar systems.
644 * <p>
645 * This method matches the signature of the functional interface {@link TemporalQuery}
646 * allowing it to be used as a query via method reference, {@code aChronology::date}.
647 *
648 * @param temporal the temporal object to convert, not null
649 * @return the local date in this chronology, not null
650 * @throws DateTimeException if unable to create the date
651 * @see ChronoLocalDate#from(TemporalAccessor)
652 */
653 public abstract ChronoLocalDate date(TemporalAccessor temporal);
654
655 /**
656 * Obtains a local date-time in this chronology from another temporal object.
657 * <p>
658 * This obtains a date-time in this chronology based on the specified temporal.
659 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
660 * which this factory converts to an instance of {@code ChronoLocalDateTime}.
661 * <p>
662 * The conversion extracts and combines the {@code ChronoLocalDate} and the
663 * {@code LocalTime} from the temporal object.
664 * Implementations are permitted to perform optimizations such as accessing
665 * those fields that are equivalent to the relevant objects.
666 * The result uses this chronology.
667 * <p>
668 * This method matches the signature of the functional interface {@link TemporalQuery}
669 * allowing it to be used as a query via method reference, {@code aChronology::localDateTime}.
670 *
671 * @param temporal the temporal object to convert, not null
672 * @return the local date-time in this chronology, not null
673 * @throws DateTimeException if unable to create the date-time
674 * @see ChronoLocalDateTime#from(TemporalAccessor)
675 */
676 public ChronoLocalDateTime<? extends ChronoLocalDate> localDateTime(TemporalAccessor temporal) {
677 try {
678 return date(temporal).atTime(LocalTime.from(temporal));
679 } catch (DateTimeException ex) {
680 throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex);
681 }
682 }
683
684 /**
685 * Obtains a {@code ChronoZonedDateTime} in this chronology from another temporal object.
686 * <p>
687 * This obtains a zoned date-time in this chronology based on the specified temporal.
688 * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
689 * which this factory converts to an instance of {@code ChronoZonedDateTime}.
690 * <p>
691 * The conversion will first obtain a {@code ZoneId} from the temporal object,
692 * falling back to a {@code ZoneOffset} if necessary. It will then try to obtain
693 * an {@code Instant}, falling back to a {@code ChronoLocalDateTime} if necessary.
694 * The result will be either the combination of {@code ZoneId} or {@code ZoneOffset}
695 * with {@code Instant} or {@code ChronoLocalDateTime}.
696 * Implementations are permitted to perform optimizations such as accessing
697 * those fields that are equivalent to the relevant objects.
698 * The result uses this chronology.
699 * <p>
700 * This method matches the signature of the functional interface {@link TemporalQuery}
701 * allowing it to be used as a query via method reference, {@code aChronology::zonedDateTime}.
702 *
703 * @param temporal the temporal object to convert, not null
704 * @return the zoned date-time in this chronology, not null
705 * @throws DateTimeException if unable to create the date-time
706 * @see ChronoZonedDateTime#from(TemporalAccessor)
707 */
708 public ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(TemporalAccessor temporal) {
709 try {
710 ZoneId zone = ZoneId.from(temporal);
711 try {
712 Instant instant = Instant.from(temporal);
713 return zonedDateTime(instant, zone);
714
715 } catch (DateTimeException ex1) {
716 ChronoLocalDateTimeImpl<?> cldt = ChronoLocalDateTimeImpl.ensureValid(this, localDateTime(temporal));
717 return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null);
718 }
719 } catch (DateTimeException ex) {
720 throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex);
721 }
722 }
723
724 /**
725 * Obtains a {@code ChronoZonedDateTime} in this chronology from an {@code Instant}.
726 * <p>
727 * This obtains a zoned date-time with the same instant as that specified.
728 *
729 * @param instant the instant to create the date-time from, not null
730 * @param zone the time-zone, not null
731 * @return the zoned date-time, not null
732 * @throws DateTimeException if the result exceeds the supported range
733 */
734 public ChronoZonedDateTime<? extends ChronoLocalDate> zonedDateTime(Instant instant, ZoneId zone) {
735 return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone);
736 }
737
738 //-----------------------------------------------------------------------
739 /**
740 * Checks if the specified year is a leap year.
741 * <p>
742 * A leap-year is a year of a longer length than normal.
743 * The exact meaning is determined by the chronology according to the following constraints.
744 * <p><ul>
745 * <li>a leap-year must imply a year-length longer than a non leap-year.
746 * <li>a chronology that does not support the concept of a year must return false.
747 * </ul><p>
748 *
749 * @param prolepticYear the proleptic-year to check, not validated for range
750 * @return true if the year is a leap year
751 */
752 public abstract boolean isLeapYear(long prolepticYear);
753
754 /**
862 @Override
863 public <R> R query(TemporalQuery<R> query) {
864 if (query == TemporalQuery.chronology()) {
865 return (R) Chronology.this;
866 }
867 return TemporalAccessor.super.query(query);
868 }
869 };
870 }
871
872 //-----------------------------------------------------------------------
873 /**
874 * Resolves parsed {@code ChronoField} values into a date during parsing.
875 * <p>
876 * Most {@code TemporalField} implementations are resolved using the
877 * resolve method on the field. By contrast, the {@code ChronoField} class
878 * defines fields that only have meaning relative to the chronology.
879 * As such, {@code ChronoField} date fields are resolved here in the
880 * context of a specific chronology.
881 * <p>
882 * {@code ChronoField} instances are resolved by this method, which may
883 * be overridden in subclasses.
884 * <ul>
885 * <li>{@code EPOCH_DAY} - If present, this is converted to a date and
886 * all other date fields are then cross-checked against the date.
887 * <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the
888 * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart
889 * then the field is validated.
890 * <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they
891 * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA}
892 * range is not validated, in smart and strict mode it is. The {@code ERA} is
893 * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is
894 * present, and the mode is smart or lenient, then the last available era
895 * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is
896 * left untouched. If only the {@code ERA} is present, then it is left untouched.
897 * <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} -
898 * If all three are present, then they are combined to form a date.
899 * In all three modes, the {@code YEAR} is validated.
900 * If the mode is smart or strict, then the month and day are validated.
901 * If the mode is lenient, then the date is combined in a manner equivalent to
902 * creating a date on the first day of the first month in the requested year,
903 * then adding the difference in months, then the difference in days.
904 * If the mode is smart, and the day-of-month is greater than the maximum for
905 * the year-month, then the day-of-month is adjusted to the last day-of-month.
906 * If the mode is strict, then the three fields must form a valid date.
907 * <li>{@code YEAR} and {@code DAY_OF_YEAR} -
908 * If both are present, then they are combined to form a date.
909 * In all three modes, the {@code YEAR} is validated.
910 * If the mode is lenient, then the date is combined in a manner equivalent to
911 * creating a date on the first day of the requested year, then adding
912 * the difference in days.
913 * If the mode is smart or strict, then the two fields must form a valid date.
914 * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
915 * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} -
916 * If all four are present, then they are combined to form a date.
917 * In all three modes, the {@code YEAR} is validated.
918 * If the mode is lenient, then the date is combined in a manner equivalent to
919 * creating a date on the first day of the first month in the requested year, then adding
920 * the difference in months, then the difference in weeks, then in days.
921 * If the mode is smart or strict, then the all four fields are validated to
922 * their outer ranges. The date is then combined in a manner equivalent to
923 * creating a date on the first day of the requested year and month, then adding
924 * the amount in weeks and days to reach their values. If the mode is strict,
925 * the date is additionally validated to check that the day and week adjustment
926 * did not change the month.
927 * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and
928 * {@code DAY_OF_WEEK} - If all four are present, then they are combined to
929 * form a date. The approach is the same as described above for
930 * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}.
931 * The day-of-week is adjusted as the next or same matching day-of-week once
932 * the years, months and weeks have been handled.
933 * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} -
934 * If all three are present, then they are combined to form a date.
935 * In all three modes, the {@code YEAR} is validated.
936 * If the mode is lenient, then the date is combined in a manner equivalent to
937 * creating a date on the first day of the requested year, then adding
938 * the difference in weeks, then in days.
939 * If the mode is smart or strict, then the all three fields are validated to
940 * their outer ranges. The date is then combined in a manner equivalent to
941 * creating a date on the first day of the requested year, then adding
942 * the amount in weeks and days to reach their values. If the mode is strict,
943 * the date is additionally validated to check that the day and week adjustment
944 * did not change the year.
945 * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} -
946 * If all three are present, then they are combined to form a date.
947 * The approach is the same as described above for years and weeks in
948 * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the
949 * next or same matching day-of-week once the years and weeks have been handled.
950 * </ul>
951 * <p>
952 * The default implementation is suitable for most calendar systems.
953 * If {@link ChronoField#YEAR_OF_ERA} is found without an {@link ChronoField#ERA}
954 * then the last era in {@link #eras()} is used.
955 * The implementation assumes a 7 day week, that the first day-of-month
956 * has the value 1, that first day-of-year has the value 1, and that the
957 * first of the month and year always exists.
958 *
959 * @param fieldValues the map of fields to values, which can be updated, not null
960 * @param resolverStyle the requested type of resolve, not null
961 * @return the resolved date, null if insufficient information to create a date
962 * @throws DateTimeException if the date cannot be resolved, typically
963 * because of a conflict in the input data
964 */
965 public ChronoLocalDate resolveDate(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
966 // check epoch-day before inventing era
967 if (fieldValues.containsKey(EPOCH_DAY)) {
968 return dateEpochDay(fieldValues.remove(EPOCH_DAY));
969 }
970
971 // fix proleptic month before inventing era
972 resolveProlepticMonth(fieldValues, resolverStyle);
973
974 // invent era if necessary to resolve year-of-era
975 ChronoLocalDate resolved = resolveYearOfEra(fieldValues, resolverStyle);
976 if (resolved != null) {
977 return resolved;
978 }
979
980 // build date
981 if (fieldValues.containsKey(YEAR)) {
982 if (fieldValues.containsKey(MONTH_OF_YEAR)) {
983 if (fieldValues.containsKey(DAY_OF_MONTH)) {
984 return resolveYMD(fieldValues, resolverStyle);
985 }
986 if (fieldValues.containsKey(ALIGNED_WEEK_OF_MONTH)) {
987 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_MONTH)) {
988 return resolveYMAA(fieldValues, resolverStyle);
989 }
990 if (fieldValues.containsKey(DAY_OF_WEEK)) {
991 return resolveYMAD(fieldValues, resolverStyle);
992 }
993 }
994 }
995 if (fieldValues.containsKey(DAY_OF_YEAR)) {
996 return resolveYD(fieldValues, resolverStyle);
997 }
998 if (fieldValues.containsKey(ALIGNED_WEEK_OF_YEAR)) {
999 if (fieldValues.containsKey(ALIGNED_DAY_OF_WEEK_IN_YEAR)) {
1000 return resolveYAA(fieldValues, resolverStyle);
1001 }
1002 if (fieldValues.containsKey(DAY_OF_WEEK)) {
1003 return resolveYAD(fieldValues, resolverStyle);
1004 }
1005 }
1006 }
1007 return null;
1008 }
1009
1010 void resolveProlepticMonth(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1011 Long pMonth = fieldValues.remove(PROLEPTIC_MONTH);
1012 if (pMonth != null) {
1013 if (resolverStyle != ResolverStyle.LENIENT) {
1014 PROLEPTIC_MONTH.checkValidValue(pMonth);
1015 }
1016 // first day-of-month is likely to be safest for setting proleptic-month
1017 // cannot add to year zero, as not all chronologies have a year zero
1018 ChronoLocalDate chronoDate = dateNow()
1019 .with(DAY_OF_MONTH, 1).with(PROLEPTIC_MONTH, pMonth);
1020 addFieldValue(fieldValues, MONTH_OF_YEAR, chronoDate.get(MONTH_OF_YEAR));
1021 addFieldValue(fieldValues, YEAR, chronoDate.get(YEAR));
1022 }
1023 }
1024
1025 ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1026 Long yoeLong = fieldValues.remove(YEAR_OF_ERA);
1027 if (yoeLong != null) {
1028 Long eraLong = fieldValues.remove(ERA);
1029 int yoe;
1030 if (resolverStyle != ResolverStyle.LENIENT) {
1031 yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);
1032 } else {
1033 yoe = Math.toIntExact(yoeLong);
1034 }
1035 if (eraLong != null) {
1036 Era eraObj = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));
1037 addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
1038 } else {
1039 if (fieldValues.containsKey(YEAR)) {
1040 int year = range(YEAR).checkValidIntValue(fieldValues.get(YEAR), YEAR);
1041 ChronoLocalDate chronoDate = dateYearDay(year, 1);
1042 addFieldValue(fieldValues, YEAR, prolepticYear(chronoDate.getEra(), yoe));
1043 } else if (resolverStyle == ResolverStyle.STRICT) {
1044 // do not invent era if strict
1045 // reinstate the field removed earlier, no cross-check issues
1046 fieldValues.put(YEAR_OF_ERA, yoeLong);
1047 } else {
1048 List<Era> eras = eras();
1049 if (eras.isEmpty()) {
1050 addFieldValue(fieldValues, YEAR, yoe);
1051 } else {
1052 Era eraObj = eras.get(eras.size() - 1);
1053 addFieldValue(fieldValues, YEAR, prolepticYear(eraObj, yoe));
1054 }
1055 }
1056 }
1057 } else if (fieldValues.containsKey(ERA)) {
1058 range(ERA).checkValidValue(fieldValues.get(ERA), ERA); // always validated
1059 }
1060 return null;
1061 }
1062
1063 ChronoLocalDate resolveYMD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1064 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1065 if (resolverStyle == ResolverStyle.LENIENT) {
1066 long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
1067 long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
1068 return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
1069 }
1070 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
1071 ValueRange domRange = range(DAY_OF_MONTH);
1072 int dom = domRange.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
1073 if (resolverStyle == ResolverStyle.SMART) { // previous valid
1074 try {
1075 return date(y, moy, dom);
1076 } catch (DateTimeException ex) {
1077 return date(y, moy, 1).with(TemporalAdjuster.lastDayOfMonth());
1078 }
1079 }
1080 return date(y, moy, dom);
1081 }
1082
1083 ChronoLocalDate resolveYD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1084 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1085 if (resolverStyle == ResolverStyle.LENIENT) {
1086 long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
1087 return dateYearDay(y, 1).plus(days, DAYS);
1088 }
1089 int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
1090 return dateYearDay(y, doy); // smart is same as strict
1091 }
1092
1093 ChronoLocalDate resolveYMAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1094 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1095 if (resolverStyle == ResolverStyle.LENIENT) {
1096 long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
1097 long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
1098 long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), 1);
1099 return date(y, 1, 1).plus(months, MONTHS).plus(weeks, WEEKS).plus(days, DAYS);
1100 }
1101 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
1102 int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
1103 int ad = range(ALIGNED_DAY_OF_WEEK_IN_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_MONTH), ALIGNED_DAY_OF_WEEK_IN_MONTH);
1104 ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
1105 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
1106 throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
1107 }
1108 return date;
1109 }
1110
1111 ChronoLocalDate resolveYMAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1112 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1113 if (resolverStyle == ResolverStyle.LENIENT) {
1114 long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
1115 long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), 1);
1116 long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
1117 return resolveAligned(date(y, 1, 1), months, weeks, dow);
1118 }
1119 int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
1120 int aw = range(ALIGNED_WEEK_OF_MONTH).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_MONTH), ALIGNED_WEEK_OF_MONTH);
1121 int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
1122 ChronoLocalDate date = date(y, moy, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
1123 if (resolverStyle == ResolverStyle.STRICT && date.get(MONTH_OF_YEAR) != moy) {
1124 throw new DateTimeException("Strict mode rejected resolved date as it is in a different month");
1125 }
1126 return date;
1127 }
1128
1129 ChronoLocalDate resolveYAA(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1130 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1131 if (resolverStyle == ResolverStyle.LENIENT) {
1132 long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
1133 long days = Math.subtractExact(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), 1);
1134 return dateYearDay(y, 1).plus(weeks, WEEKS).plus(days, DAYS);
1135 }
1136 int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
1137 int ad = range(ALIGNED_DAY_OF_WEEK_IN_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_DAY_OF_WEEK_IN_YEAR), ALIGNED_DAY_OF_WEEK_IN_YEAR);
1138 ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7 + (ad - 1), DAYS);
1139 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
1140 throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
1141 }
1142 return date;
1143 }
1144
1145 ChronoLocalDate resolveYAD(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
1146 int y = range(YEAR).checkValidIntValue(fieldValues.remove(YEAR), YEAR);
1147 if (resolverStyle == ResolverStyle.LENIENT) {
1148 long weeks = Math.subtractExact(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), 1);
1149 long dow = Math.subtractExact(fieldValues.remove(DAY_OF_WEEK), 1);
1150 return resolveAligned(dateYearDay(y, 1), 0, weeks, dow);
1151 }
1152 int aw = range(ALIGNED_WEEK_OF_YEAR).checkValidIntValue(fieldValues.remove(ALIGNED_WEEK_OF_YEAR), ALIGNED_WEEK_OF_YEAR);
1153 int dow = range(DAY_OF_WEEK).checkValidIntValue(fieldValues.remove(DAY_OF_WEEK), DAY_OF_WEEK);
1154 ChronoLocalDate date = dateYearDay(y, 1).plus((aw - 1) * 7, DAYS).with(nextOrSame(DayOfWeek.of(dow)));
1155 if (resolverStyle == ResolverStyle.STRICT && date.get(YEAR) != y) {
1156 throw new DateTimeException("Strict mode rejected resolved date as it is in a different year");
1157 }
1158 return date;
1159 }
1160
1161 ChronoLocalDate resolveAligned(ChronoLocalDate base, long months, long weeks, long dow) {
1162 ChronoLocalDate date = base.plus(months, MONTHS).plus(weeks, WEEKS);
1163 if (dow > 7) {
1164 date = date.plus((dow - 1) / 7, WEEKS);
1165 dow = ((dow - 1) % 7) + 1;
1166 } else if (dow < 1) {
1167 date = date.plus(Math.subtractExact(dow, 7) / 7, WEEKS);
1168 dow = ((dow + 6) % 7) + 1;
1169 }
1170 return date.with(nextOrSame(DayOfWeek.of((int) dow)));
1171 }
1172
1173 /**
1174 * Adds a field-value pair to the map, checking for conflicts.
1175 * <p>
1176 * If the field is not already present, then the field-value pair is added to the map.
1177 * If the field is already present and it has the same value as that specified, no action occurs.
1178 * If the field is already present and it has a different value to that specified, then
1179 * an exception is thrown.
1180 *
1181 * @param field the field to add, not null
1182 * @param value the value to add, not null
1183 * @throws DateTimeException if the field is already present with a different value
1184 */
1185 void addFieldValue(Map<TemporalField, Long> fieldValues, ChronoField field, long value) {
1186 Long old = fieldValues.get(field); // check first for better error message
1187 if (old != null && old.longValue() != value) {
1188 throw new DateTimeException("Conflict found: " + field + " " + old + " differs from " + field + " " + value);
1189 }
1190 fieldValues.put(field, value);
|