/** * Resolves parsed {@code ChronoField} values into a date during parsing. * <p> * Most {@code TemporalField} implementations are resolved using the * resolve method on the field. By contrast, the {@code ChronoField} class * defines fields that only have meaning relative to the chronology. * As such, {@code ChronoField} date fields are resolved here in the * context of a specific chronology. * <p> * {@code ChronoField} instances are resolved by this method, which may * be overridden in subclasses. * <ul> * <li>{@code EPOCH_DAY} - If present, this is converted to a date and * all other date fields are then cross-checked against the date. * <li>{@code PROLEPTIC_MONTH} - If present, then it is split into the * {@code YEAR} and {@code MONTH_OF_YEAR}. If the mode is strict or smart * then the field is validated. * <li>{@code YEAR_OF_ERA} and {@code ERA} - If both are present, then they * are combined to form a {@code YEAR}. In lenient mode, the {@code YEAR_OF_ERA} * range is not validated, in smart and strict mode it is. The {@code ERA} is * validated for range in all three modes. If only the {@code YEAR_OF_ERA} is * present, and the mode is smart or lenient, then the last available era * is assumed. In strict mode, no era is assumed and the {@code YEAR_OF_ERA} is * left untouched. If only the {@code ERA} is present, then it is left untouched. * <li>{@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} - * If all three are present, then they are combined to form a date. * In all three modes, the {@code YEAR} is validated. * If the mode is smart or strict, then the month and day are validated. * If the mode is lenient, then the date is combined in a manner equivalent to * creating a date on the first day of the first month in the requested year, * then adding the difference in months, then the difference in days. * If the mode is smart, and the day-of-month is greater than the maximum for * the year-month, then the day-of-month is adjusted to the last day-of-month. * If the mode is strict, then the three fields must form a valid date. * <li>{@code YEAR} and {@code DAY_OF_YEAR} - * If both are present, then they are combined to form a date. * In all three modes, the {@code YEAR} is validated. * If the mode is lenient, then the date is combined in a manner equivalent to * creating a date on the first day of the requested year, then adding * the difference in days. * If the mode is smart or strict, then the two fields must form a valid date. * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and * {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} - * If all four are present, then they are combined to form a date. * In all three modes, the {@code YEAR} is validated. * If the mode is lenient, then the date is combined in a manner equivalent to * creating a date on the first day of the first month in the requested year, then adding * the difference in months, then the difference in weeks, then in days. * If the mode is smart or strict, then the all four fields are validated to * their outer ranges. The date is then combined in a manner equivalent to * creating a date on the first day of the requested year and month, then adding * the amount in weeks and days to reach their values. If the mode is strict, * the date is additionally validated to check that the day and week adjustment * did not change the month. * <li>{@code YEAR}, {@code MONTH_OF_YEAR}, {@code ALIGNED_WEEK_OF_MONTH} and * {@code DAY_OF_WEEK} - If all four are present, then they are combined to * form a date. The approach is the same as described above for * years, months and weeks in {@code ALIGNED_DAY_OF_WEEK_IN_MONTH}. * The day-of-week is adjusted as the next or same matching day-of-week once * the years, months and weeks have been handled. * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} - * If all three are present, then they are combined to form a date. * In all three modes, the {@code YEAR} is validated. * If the mode is lenient, then the date is combined in a manner equivalent to * creating a date on the first day of the requested year, then adding * the difference in weeks, then in days. * If the mode is smart or strict, then the all three fields are validated to * their outer ranges. The date is then combined in a manner equivalent to * creating a date on the first day of the requested year, then adding * the amount in weeks and days to reach their values. If the mode is strict, * the date is additionally validated to check that the day and week adjustment * did not change the year. * <li>{@code YEAR}, {@code ALIGNED_WEEK_OF_YEAR} and {@code DAY_OF_WEEK} - * If all three are present, then they are combined to form a date. * The approach is the same as described above for years and weeks in * {@code ALIGNED_DAY_OF_WEEK_IN_YEAR}. The day-of-week is adjusted as the * next or same matching day-of-week once the years and weeks have been handled. * </ul> * <p> * The default implementation is suitable for most calendar systems. * If {@link java.time.temporal.ChronoField#YEAR_OF_ERA} is found without an {@link java.time.temporal.ChronoField#ERA} * then the last era in {@link #eras()} is used. * The implementation assumes a 7 day week, that the first day-of-month * has the value 1, that first day-of-year has the value 1, and that the * first of the month and year always exists. * * @param FieldValues $fieldValues the map of fields to values, which can be updated, not null * @param ResolverStyle $resolverStyle the requested type of resolve, not null * @return ChronoLocalDate the resolved date, null if insufficient information to create a date * @throws DateTimeException if the date cannot be resolved, typically * because of a conflict in the input data */ public function resolveDate(FieldValues $fieldValues, ResolverStyle $resolverStyle) { // check epoch-day before inventing era if ($fieldValues->has(ChronoField::EPOCH_DAY())) { return $this->dateEpochDay($fieldValues->remove(ChronoField::EPOCH_DAY())); } // fix proleptic month before inventing era $this->resolveProlepticMonth($fieldValues, $resolverStyle); // invent era if necessary to resolve year-of-era $resolved = $this->resolveYearOfEra($fieldValues, $resolverStyle); if ($resolved !== null) { return $resolved; } // build date if ($fieldValues->has(ChronoField::YEAR())) { if ($fieldValues->has(ChronoField::MONTH_OF_YEAR())) { if ($fieldValues->has(ChronoField::DAY_OF_MONTH())) { return $this->resolveYMD($fieldValues, $resolverStyle); } if ($fieldValues->has(ChronoField::ALIGNED_WEEK_OF_MONTH())) { if ($fieldValues->has(ChronoField::ALIGNED_DAY_OF_WEEK_IN_MONTH())) { return $this->resolveYMAA($fieldValues, $resolverStyle); } if ($fieldValues->has(ChronoField::DAY_OF_WEEK())) { return $this->resolveYMAD($fieldValues, $resolverStyle); } } } if ($fieldValues->has(ChronoField::DAY_OF_YEAR())) { return $this->resolveYD($fieldValues, $resolverStyle); } if ($fieldValues->has(ChronoField::ALIGNED_WEEK_OF_YEAR())) { if ($fieldValues->has(ChronoField::ALIGNED_DAY_OF_WEEK_IN_YEAR())) { return $this->resolveYAA($fieldValues, $resolverStyle); } if ($fieldValues->has(ChronoField::DAY_OF_WEEK())) { return $this->resolveYAD($fieldValues, $resolverStyle); } } } return null; }
public function resolve(FieldValues $fieldValues, TemporalAccessor $partialTemporal, ResolverStyle $resolverStyle) { $value = $fieldValues->get($this); $newValue = Math::toIntExact($value); // broad limit makes overflow checking lighter // first convert localized day-of-week to ISO day-of-week // doing this first handles case where both ISO and localized were parsed and might mismatch // day-of-week is always strict as two different day-of-week values makes lenient complex if ($this->rangeUnit == ChronoUnit::WEEKS()) { // day-of-week $checkedValue = $this->range->checkValidIntValue($value, $this); // no leniency as too complex $startDow = $this->weekDef->getFirstDayOfWeek()->getValue(); $isoDow = Math::floorMod($startDow - 1 + ($checkedValue - 1), 7) + 1; $fieldValues->remove($this); $fieldValues->put(CF::DAY_OF_WEEK(), $isoDow); return null; } // can only build date if ISO day-of-week is present if (!$fieldValues->has(CF::DAY_OF_WEEK())) { return null; } $isoDow = CF::DAY_OF_WEEK()->checkValidIntValue($fieldValues->get(CF::DAY_OF_WEEK())); $dow = $this->localizedDayOfWeekNumerical($isoDow); // build date $chrono = AbstractChronology::from($partialTemporal); if ($fieldValues->has(CF::YEAR())) { $year = CF::YEAR()->checkValidIntValue($fieldValues->get(CF::YEAR())); // validate if ($this->rangeUnit == ChronoUnit::MONTHS() && $fieldValues->has(CF::MONTH_OF_YEAR())) { // week-of-month $month = $fieldValues->get(CF::MONTH_OF_YEAR()); // not validated yet return $this->resolveWoM($fieldValues, $chrono, $year, $month, $newValue, $dow, $resolverStyle); } if ($this->rangeUnit == ChronoUnit::YEARS()) { // week-of-year return $this->resolveWoY($fieldValues, $chrono, $year, $newValue, $dow, $resolverStyle); } } else { if (($this->rangeUnit == IsoFields::WEEK_BASED_YEARS() || $this->rangeUnit == ChronoUnit::FOREVER()) && $fieldValues->has($this->weekDef->weekBasedYear) && $fieldValues->has($this->weekDef->weekOfWeekBasedYear)) { // week-of-week-based-year and year-of-week-based-year return $this->resolveWBY($fieldValues, $chrono, $dow, $resolverStyle); } } return null; }
private function resolveFractional() { // ensure fractional seconds available as CF requires // resolveTimeLenient() will have merged CF::MICRO_OF_SECOND()/MILLI_OF_SECOND to NANO_OF_SECOND if ($this->time == null && ($this->fieldValues->has(CF::INSTANT_SECONDS()) || $this->fieldValues->has(CF::SECOND_OF_DAY()) || $this->fieldValues->has(CF::SECOND_OF_MINUTE()))) { if ($this->fieldValues->has(CF::NANO_OF_SECOND())) { $nos = $this->fieldValues->get(CF::NANO_OF_SECOND()); $this->fieldValues->put(CF::MICRO_OF_SECOND(), Math::div($nos, 1000)); $this->fieldValues->put(CF::MILLI_OF_SECOND(), Math::div($nos, 1000000)); } else { $this->fieldValues->put(CF::NANO_OF_SECOND(), 0); $this->fieldValues->put(CF::MICRO_OF_SECOND(), 0); $this->fieldValues->put(CF::MILLI_OF_SECOND(), 0); } } }
public function isSupported(TemporalField $field) { return $this->fields->has($field); }