src/share/classes/java/time/temporal/IsoFields.java

Print this page

        

*** 67,83 **** import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; - import java.time.DateTimeException; import java.time.Duration; import java.time.LocalDate; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.util.HashMap; import java.util.Map; /** * Fields and units specific to the ISO-8601 calendar system, * including quarter-of-year and week-based-year. * <p> --- 67,89 ---- import static java.time.temporal.ChronoUnit.FOREVER; import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import java.time.Duration; import java.time.LocalDate; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; + import java.time.format.ResolverStyle; import java.util.HashMap; + import java.util.Locale; import java.util.Map; + import java.util.Objects; + import java.util.ResourceBundle; + + import sun.util.locale.provider.LocaleProviderAdapter; + import sun.util.locale.provider.LocaleResources; /** * Fields and units specific to the ISO-8601 calendar system, * including quarter-of-year and week-based-year. * <p>
*** 160,196 **** * <p> * When setting this field, the value is allowed to be partially lenient, taking any * value from 1 to 92. If the quarter has less than 92 days, then day 92, and * potentially day 91, is in the following quarter. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER; /** * The field that represents the quarter-of-year. * <p> * This field allows the quarter-of-year value to be queried and set. * The quarter-of-year has values from 1 to 4. * <p> ! * The day-of-quarter can only be calculated if the month-of-year is available. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR; /** * The field that represents the week-of-week-based-year. * <p> * This field allows the week of the week-based-year value to be queried and set. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR; /** * The field that represents the week-based-year. * <p> * This field allows the week-based-year value to be queried and set. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR; /** * The unit that represents week-based-years for the purpose of addition and subtraction. --- 166,255 ---- * <p> * When setting this field, the value is allowed to be partially lenient, taking any * value from 1 to 92. If the quarter has less than 92 days, then day 92, and * potentially day 91, is in the following quarter. * <p> + * In the resolving phase of parsing, a date can be created from a year, + * quarter-of-year and day-of-quarter. + * <p> + * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are + * validated against their range of valid values. The day-of-quarter field + * is validated from 1 to 90, 91 or 92 depending on the year and quarter. + * <p> + * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are + * validated against their range of valid values. The day-of-quarter field is + * validated between 1 and 92, ignoring the actual range based on the year and quarter. + * If the day-of-quarter exceeds the actual range by one day, then the resulting date + * is one day later. If the day-of-quarter exceeds the actual range by two days, + * then the resulting date is two days later. + * <p> + * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the year is validated + * against the range of valid values. The resulting date is calculated equivalent to + * the following three stage approach. First, create a date on the first of January + * in the requested year. Then take the quarter-of-year, subtract one, and add the + * amount in quarters to the date. Finally, take the day-of-quarter, subtract one, + * and add the amount in days to the date. + * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER; /** * The field that represents the quarter-of-year. * <p> * This field allows the quarter-of-year value to be queried and set. * The quarter-of-year has values from 1 to 4. * <p> ! * The quarter-of-year can only be calculated if the month-of-year is available. ! * <p> ! * In the resolving phase of parsing, a date can be created from a year, ! * quarter-of-year and day-of-quarter. ! * See {@link #DAY_OF_QUARTER} for details. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR; /** * The field that represents the week-of-week-based-year. * <p> * This field allows the week of the week-based-year value to be queried and set. + * The week-of-week-based-year has values from 1 to 52, or 53 if the + * week-based-year has 53 weeks. + * <p> + * In the resolving phase of parsing, a date can be created from a + * week-based-year, week-of-week-based-year and day-of-week. + * <p> + * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are + * validated against their range of valid values. The week-of-week-based-year + * field is validated from 1 to 52 or 53 depending on the week-based-year. + * <p> + * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are + * validated against their range of valid values. The week-of-week-based-year + * field is validated between 1 and 53, ignoring the week-based-year. + * If the week-of-week-based-year is 53, but the week-based-year only has + * 52 weeks, then the resulting date is in week 1 of the following week-based-year. + * <p> + * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the week-based-year + * is validated against the range of valid values. If the day-of-week is outside + * the range 1 to 7, then the resulting date is adjusted by a suitable number of + * weeks to reduce the day-of-week to the range 1 to 7. If the week-of-week-based-year + * value is outside the range 1 to 52, then any excess weeks are added or subtracted + * from the resulting date. * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR; /** * The field that represents the week-based-year. * <p> * This field allows the week-based-year value to be queried and set. * <p> + * The field has a range that matches {@link LocalDate#MAX} and {@link LocalDate#MIN}. + * <p> + * In the resolving phase of parsing, a date can be created from a + * week-based-year, week-of-week-based-year and day-of-week. + * See {@link #WEEK_OF_WEEK_BASED_YEAR} for details. + * <p> * This unit is an immutable and thread-safe singleton. */ public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR; /** * The unit that represents week-based-years for the purpose of addition and subtraction.
*** 251,261 **** temporal.isSupported(YEAR) && isIso(temporal); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: DayOfQuarter"); } long qoy = temporal.getLong(QUARTER_OF_YEAR); if (qoy == 1) { long year = temporal.getLong(YEAR); return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90)); --- 310,320 ---- temporal.isSupported(YEAR) && isIso(temporal); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter"); } long qoy = temporal.getLong(QUARTER_OF_YEAR); if (qoy == 1) { long year = temporal.getLong(YEAR); return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90));
*** 267,277 **** return range(); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: DayOfQuarter"); } int doy = temporal.get(DAY_OF_YEAR); int moy = temporal.get(MONTH_OF_YEAR); long year = temporal.getLong(YEAR); return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)]; --- 326,336 ---- return range(); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter"); } int doy = temporal.get(DAY_OF_YEAR); int moy = temporal.get(MONTH_OF_YEAR); long year = temporal.getLong(YEAR); return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)];
*** 283,302 **** long curValue = getFrom(temporal); range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue)); } @Override ! public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value) { ! if ((temporal.isSupported(YEAR) && temporal.isSupported(DAY_OF_QUARTER)) == false) { return null; } ! int y = temporal.get(YEAR); ! int qoy = temporal.get(QUARTER_OF_YEAR); ! range().checkValidValue(value, this); // leniently check from 1 to 92 TODO: check ! LocalDate date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1).plusDays(value - 1); Map<TemporalField, Long> result = new HashMap<>(4, 1.0f); ! result.put(EPOCH_DAY, date.toEpochDay()); result.put(YEAR, null); result.put(QUARTER_OF_YEAR, null); return result; } }, --- 342,374 ---- long curValue = getFrom(temporal); range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue)); } @Override ! public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long doq, ResolverStyle resolverStyle) { ! if ((temporal.isSupported(YEAR) && temporal.isSupported(QUARTER_OF_YEAR)) == false) { return null; } ! int y = temporal.get(YEAR); // validated ! LocalDate date; ! if (resolverStyle == ResolverStyle.LENIENT) { ! long qoy = temporal.getLong(QUARTER_OF_YEAR); // unvalidated ! date = LocalDate.of(y, 1, 1).plusMonths(Math.multiplyExact(Math.subtractExact(qoy, 1), 3)); ! } else { ! int qoy = temporal.get(QUARTER_OF_YEAR); // validated ! date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1); ! if (doq < 1 || doq > 90) { ! if (resolverStyle == ResolverStyle.STRICT) { ! rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range ! } else { // SMART ! range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter ! } ! } ! } ! long epochDay = Math.addExact(date.toEpochDay(), Math.subtractExact(doq, 1)); Map<TemporalField, Long> result = new HashMap<>(4, 1.0f); ! result.put(EPOCH_DAY, epochDay); result.put(YEAR, null); result.put(QUARTER_OF_YEAR, null); return result; } },
*** 322,332 **** return temporal.isSupported(MONTH_OF_YEAR) && isIso(temporal); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: QuarterOfYear"); } long moy = temporal.getLong(MONTH_OF_YEAR); return ((moy + 2) / 3); } @SuppressWarnings("unchecked") --- 394,404 ---- return temporal.isSupported(MONTH_OF_YEAR) && isIso(temporal); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear"); } long moy = temporal.getLong(MONTH_OF_YEAR); return ((moy + 2) / 3); } @SuppressWarnings("unchecked")
*** 341,350 **** --- 413,432 ---- WEEK_OF_WEEK_BASED_YEAR { @Override public String getName() { return "WeekOfWeekBasedYear"; } + + @Override + public String getDisplayName(Locale locale) { + Objects.requireNonNull(locale, "locale"); + LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased() + .getLocaleResources(locale); + ResourceBundle rb = lr.getJavaTimeFormatData(); + return rb.containsKey("field.week") ? rb.getString("field.week") : getName(); + } + @Override public TemporalUnit getBaseUnit() { return WEEKS; } @Override
*** 360,377 **** return temporal.isSupported(EPOCH_DAY) && isIso(temporal); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: WeekOfWeekBasedYear"); } return getWeekRange(LocalDate.from(temporal)); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: WeekOfWeekBasedYear"); } return getWeek(LocalDate.from(temporal)); } @SuppressWarnings("unchecked") @Override --- 442,459 ---- return temporal.isSupported(EPOCH_DAY) && isIso(temporal); } @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear"); } return getWeekRange(LocalDate.from(temporal)); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear"); } return getWeek(LocalDate.from(temporal)); } @SuppressWarnings("unchecked") @Override
*** 379,396 **** // calls getFrom() to check if supported range().checkValidValue(newValue, this); // lenient range return (R) temporal.plus(Math.subtractExact(newValue, getFrom(temporal)), WEEKS); } @Override ! public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value) { if ((temporal.isSupported(WEEK_BASED_YEAR) && temporal.isSupported(DAY_OF_WEEK)) == false) { return null; } ! int wby = temporal.get(WEEK_BASED_YEAR); ! int dow = temporal.get(DAY_OF_WEEK); ! range().checkValidValue(value, this); // lenient range ! LocalDate date = LocalDate.of(wby, 1, 4).plusWeeks(value - 1).with(DAY_OF_WEEK, dow); Map<TemporalField, Long> result = new HashMap<>(2, 1.0f); result.put(EPOCH_DAY, date.toEpochDay()); result.put(WEEK_BASED_YEAR, null); result.put(DAY_OF_WEEK, null); return result; --- 461,497 ---- // calls getFrom() to check if supported range().checkValidValue(newValue, this); // lenient range return (R) temporal.plus(Math.subtractExact(newValue, getFrom(temporal)), WEEKS); } @Override ! public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long wowby, ResolverStyle resolverStyle) { if ((temporal.isSupported(WEEK_BASED_YEAR) && temporal.isSupported(DAY_OF_WEEK)) == false) { return null; } ! int wby = temporal.get(WEEK_BASED_YEAR); // validated ! LocalDate date = LocalDate.of(wby, 1, 4); ! if (resolverStyle == ResolverStyle.LENIENT) { ! long dow = temporal.getLong(DAY_OF_WEEK); // unvalidated ! if (dow > 7) { ! date = date.plusWeeks((dow - 1) / 7); ! dow = ((dow - 1) % 7) + 1; ! } else if (dow < 1) { ! date = date.plusWeeks(Math.subtractExact(dow, 7) / 7); ! dow = ((dow + 6) % 7) + 1; ! } ! date = date.plusWeeks(Math.subtractExact(wowby, 1)).with(DAY_OF_WEEK, dow); ! } else { ! int dow = temporal.get(DAY_OF_WEEK); // validated ! if (wowby < 1 || wowby > 52) { ! if (resolverStyle == ResolverStyle.STRICT) { ! getWeekRange(date).checkValidValue(wowby, this); // only allow exact range ! } else { // SMART ! range().checkValidValue(wowby, this); // allow 1-53 rolling into next year ! } ! } ! date = date.plusWeeks(wowby - 1).with(DAY_OF_WEEK, dow); ! } Map<TemporalField, Long> result = new HashMap<>(2, 1.0f); result.put(EPOCH_DAY, date.toEpochDay()); result.put(WEEK_BASED_YEAR, null); result.put(DAY_OF_WEEK, null); return result;
*** 418,446 **** return temporal.isSupported(EPOCH_DAY) && isIso(temporal); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: WeekBasedYear"); } return getWeekBasedYear(LocalDate.from(temporal)); } @SuppressWarnings("unchecked") @Override public <R extends Temporal> R adjustInto(R temporal, long newValue) { if (isSupportedBy(temporal) == false) { ! throw new DateTimeException("Unsupported field: WeekBasedYear"); } int newVal = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check LocalDate date = LocalDate.from(temporal); int week = getWeek(date); date = date.withDayOfYear(180).withYear(newVal).with(WEEK_OF_WEEK_BASED_YEAR, week); return (R) date.with(date); } }; @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { return range(); } @Override --- 519,552 ---- return temporal.isSupported(EPOCH_DAY) && isIso(temporal); } @Override public long getFrom(TemporalAccessor temporal) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear"); } return getWeekBasedYear(LocalDate.from(temporal)); } @SuppressWarnings("unchecked") @Override public <R extends Temporal> R adjustInto(R temporal, long newValue) { if (isSupportedBy(temporal) == false) { ! throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear"); } int newVal = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check LocalDate date = LocalDate.from(temporal); int week = getWeek(date); date = date.withDayOfYear(180).withYear(newVal).with(WEEK_OF_WEEK_BASED_YEAR, week); return (R) date.with(date); } }; @Override + public boolean isDateBased() { + return true; + } + + @Override public ValueRange rangeRefinedBy(TemporalAccessor temporal) { return range(); } @Override