52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57 package java.time.temporal;
58
59 import static java.time.DayOfWeek.THURSDAY;
60 import static java.time.DayOfWeek.WEDNESDAY;
61 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
62 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
63 import static java.time.temporal.ChronoField.EPOCH_DAY;
64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
65 import static java.time.temporal.ChronoField.YEAR;
66 import static java.time.temporal.ChronoUnit.DAYS;
67 import static java.time.temporal.ChronoUnit.FOREVER;
68 import static java.time.temporal.ChronoUnit.MONTHS;
69 import static java.time.temporal.ChronoUnit.WEEKS;
70 import static java.time.temporal.ChronoUnit.YEARS;
71
72 import java.time.DateTimeException;
73 import java.time.Duration;
74 import java.time.LocalDate;
75 import java.time.chrono.Chronology;
76 import java.time.chrono.IsoChronology;
77 import java.util.HashMap;
78 import java.util.Map;
79
80 /**
81 * Fields and units specific to the ISO-8601 calendar system,
82 * including quarter-of-year and week-based-year.
83 * <p>
84 * This class defines fields and units that are specific to the ISO calendar system.
85 *
86 * <h3>Quarter of year</h3>
87 * The ISO-8601 standard is based on the standard civic 12 month year.
88 * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4.
89 * <p>
90 * January, February and March are in Q1.
91 * April, May and June are in Q2.
92 * July, August and September are in Q3.
93 * October, November and December are in Q4.
94 * <p>
95 * The complete date is expressed using three fields:
96 * <p><ul>
97 * <li>{@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92
98 * <li>{@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the week within the week-based-year
145 * This class is immutable and thread-safe.
146 *
147 * @since 1.8
148 */
149 public final class IsoFields {
150
151 /**
152 * The field that represents the day-of-quarter.
153 * <p>
154 * This field allows the day-of-quarter value to be queried and set.
155 * The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91
156 * in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4.
157 * <p>
158 * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year
159 * are available.
160 * <p>
161 * When setting this field, the value is allowed to be partially lenient, taking any
162 * value from 1 to 92. If the quarter has less than 92 days, then day 92, and
163 * potentially day 91, is in the following quarter.
164 * <p>
165 * This unit is an immutable and thread-safe singleton.
166 */
167 public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER;
168 /**
169 * The field that represents the quarter-of-year.
170 * <p>
171 * This field allows the quarter-of-year value to be queried and set.
172 * The quarter-of-year has values from 1 to 4.
173 * <p>
174 * The day-of-quarter can only be calculated if the month-of-year is available.
175 * <p>
176 * This unit is an immutable and thread-safe singleton.
177 */
178 public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR;
179 /**
180 * The field that represents the week-of-week-based-year.
181 * <p>
182 * This field allows the week of the week-based-year value to be queried and set.
183 * <p>
184 * This unit is an immutable and thread-safe singleton.
185 */
186 public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR;
187 /**
188 * The field that represents the week-based-year.
189 * <p>
190 * This field allows the week-based-year value to be queried and set.
191 * <p>
192 * This unit is an immutable and thread-safe singleton.
193 */
194 public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR;
195 /**
196 * The unit that represents week-based-years for the purpose of addition and subtraction.
197 * <p>
198 * This allows a number of week-based-years to be added to, or subtracted from, a date.
199 * The unit is equal to either 52 or 53 weeks.
200 * The estimated duration of a week-based-year is the same as that of a standard ISO
201 * year at {@code 365.2425 Days}.
202 * <p>
203 * The rules for addition add the number of week-based-years to the existing value
204 * for the week-based-year field. If the resulting week-based-year only has 52 weeks,
205 * then the date will be in week 1 of the following week-based-year.
206 * <p>
207 * This unit is an immutable and thread-safe singleton.
208 */
209 public static final TemporalUnit WEEK_BASED_YEARS = Unit.WEEK_BASED_YEARS;
210 /**
211 * Unit that represents the concept of a quarter-year.
236 @Override
237 public TemporalUnit getBaseUnit() {
238 return DAYS;
239 }
240 @Override
241 public TemporalUnit getRangeUnit() {
242 return QUARTER_YEARS;
243 }
244 @Override
245 public ValueRange range() {
246 return ValueRange.of(1, 90, 92);
247 }
248 @Override
249 public boolean isSupportedBy(TemporalAccessor temporal) {
250 return temporal.isSupported(DAY_OF_YEAR) && temporal.isSupported(MONTH_OF_YEAR) &&
251 temporal.isSupported(YEAR) && isIso(temporal);
252 }
253 @Override
254 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
255 if (isSupportedBy(temporal) == false) {
256 throw new DateTimeException("Unsupported field: DayOfQuarter");
257 }
258 long qoy = temporal.getLong(QUARTER_OF_YEAR);
259 if (qoy == 1) {
260 long year = temporal.getLong(YEAR);
261 return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90));
262 } else if (qoy == 2) {
263 return ValueRange.of(1, 91);
264 } else if (qoy == 3 || qoy == 4) {
265 return ValueRange.of(1, 92);
266 } // else value not from 1 to 4, so drop through
267 return range();
268 }
269 @Override
270 public long getFrom(TemporalAccessor temporal) {
271 if (isSupportedBy(temporal) == false) {
272 throw new DateTimeException("Unsupported field: DayOfQuarter");
273 }
274 int doy = temporal.get(DAY_OF_YEAR);
275 int moy = temporal.get(MONTH_OF_YEAR);
276 long year = temporal.getLong(YEAR);
277 return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)];
278 }
279 @SuppressWarnings("unchecked")
280 @Override
281 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
282 // calls getFrom() to check if supported
283 long curValue = getFrom(temporal);
284 range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check
285 return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue));
286 }
287 @Override
288 public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value) {
289 if ((temporal.isSupported(YEAR) && temporal.isSupported(DAY_OF_QUARTER)) == false) {
290 return null;
291 }
292 int y = temporal.get(YEAR);
293 int qoy = temporal.get(QUARTER_OF_YEAR);
294 range().checkValidValue(value, this); // leniently check from 1 to 92 TODO: check
295 LocalDate date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1).plusDays(value - 1);
296 Map<TemporalField, Long> result = new HashMap<>(4, 1.0f);
297 result.put(EPOCH_DAY, date.toEpochDay());
298 result.put(YEAR, null);
299 result.put(QUARTER_OF_YEAR, null);
300 return result;
301 }
302 },
303 QUARTER_OF_YEAR {
304 @Override
305 public String getName() {
306 return "QuarterOfYear";
307 }
308 @Override
309 public TemporalUnit getBaseUnit() {
310 return QUARTER_YEARS;
311 }
312 @Override
313 public TemporalUnit getRangeUnit() {
314 return YEARS;
315 }
316 @Override
317 public ValueRange range() {
318 return ValueRange.of(1, 4);
319 }
320 @Override
321 public boolean isSupportedBy(TemporalAccessor temporal) {
322 return temporal.isSupported(MONTH_OF_YEAR) && isIso(temporal);
323 }
324 @Override
325 public long getFrom(TemporalAccessor temporal) {
326 if (isSupportedBy(temporal) == false) {
327 throw new DateTimeException("Unsupported field: QuarterOfYear");
328 }
329 long moy = temporal.getLong(MONTH_OF_YEAR);
330 return ((moy + 2) / 3);
331 }
332 @SuppressWarnings("unchecked")
333 @Override
334 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
335 // calls getFrom() to check if supported
336 long curValue = getFrom(temporal);
337 range().checkValidValue(newValue, this); // strictly check from 1 to 4
338 return (R) temporal.with(MONTH_OF_YEAR, temporal.getLong(MONTH_OF_YEAR) + (newValue - curValue) * 3);
339 }
340 },
341 WEEK_OF_WEEK_BASED_YEAR {
342 @Override
343 public String getName() {
344 return "WeekOfWeekBasedYear";
345 }
346 @Override
347 public TemporalUnit getBaseUnit() {
348 return WEEKS;
349 }
350 @Override
351 public TemporalUnit getRangeUnit() {
352 return WEEK_BASED_YEARS;
353 }
354 @Override
355 public ValueRange range() {
356 return ValueRange.of(1, 52, 53);
357 }
358 @Override
359 public boolean isSupportedBy(TemporalAccessor temporal) {
360 return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
361 }
362 @Override
363 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
364 if (isSupportedBy(temporal) == false) {
365 throw new DateTimeException("Unsupported field: WeekOfWeekBasedYear");
366 }
367 return getWeekRange(LocalDate.from(temporal));
368 }
369 @Override
370 public long getFrom(TemporalAccessor temporal) {
371 if (isSupportedBy(temporal) == false) {
372 throw new DateTimeException("Unsupported field: WeekOfWeekBasedYear");
373 }
374 return getWeek(LocalDate.from(temporal));
375 }
376 @SuppressWarnings("unchecked")
377 @Override
378 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
379 // calls getFrom() to check if supported
380 range().checkValidValue(newValue, this); // lenient range
381 return (R) temporal.plus(Math.subtractExact(newValue, getFrom(temporal)), WEEKS);
382 }
383 @Override
384 public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long value) {
385 if ((temporal.isSupported(WEEK_BASED_YEAR) && temporal.isSupported(DAY_OF_WEEK)) == false) {
386 return null;
387 }
388 int wby = temporal.get(WEEK_BASED_YEAR);
389 int dow = temporal.get(DAY_OF_WEEK);
390 range().checkValidValue(value, this); // lenient range
391 LocalDate date = LocalDate.of(wby, 1, 4).plusWeeks(value - 1).with(DAY_OF_WEEK, dow);
392 Map<TemporalField, Long> result = new HashMap<>(2, 1.0f);
393 result.put(EPOCH_DAY, date.toEpochDay());
394 result.put(WEEK_BASED_YEAR, null);
395 result.put(DAY_OF_WEEK, null);
396 return result;
397 }
398 },
399 WEEK_BASED_YEAR {
400 @Override
401 public String getName() {
402 return "WeekBasedYear";
403 }
404 @Override
405 public TemporalUnit getBaseUnit() {
406 return WEEK_BASED_YEARS;
407 }
408 @Override
409 public TemporalUnit getRangeUnit() {
410 return FOREVER;
411 }
412 @Override
413 public ValueRange range() {
414 return YEAR.range();
415 }
416 @Override
417 public boolean isSupportedBy(TemporalAccessor temporal) {
418 return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
419 }
420 @Override
421 public long getFrom(TemporalAccessor temporal) {
422 if (isSupportedBy(temporal) == false) {
423 throw new DateTimeException("Unsupported field: WeekBasedYear");
424 }
425 return getWeekBasedYear(LocalDate.from(temporal));
426 }
427 @SuppressWarnings("unchecked")
428 @Override
429 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
430 if (isSupportedBy(temporal) == false) {
431 throw new DateTimeException("Unsupported field: WeekBasedYear");
432 }
433 int newVal = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check
434 LocalDate date = LocalDate.from(temporal);
435 int week = getWeek(date);
436 date = date.withDayOfYear(180).withYear(newVal).with(WEEK_OF_WEEK_BASED_YEAR, week);
437 return (R) date.with(date);
438 }
439 };
440
441 @Override
442 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
443 return range();
444 }
445
446 @Override
447 public String toString() {
448 return getName();
449 }
450
451 //-------------------------------------------------------------------------
452 private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274};
453
454 private static boolean isIso(TemporalAccessor temporal) {
455 return Chronology.from(temporal).equals(IsoChronology.INSTANCE);
456 }
457
458 private static ValueRange getWeekRange(LocalDate date) {
459 int wby = getWeekBasedYear(date);
460 date = date.withDayOfYear(1).withYear(wby);
461 // 53 weeks if standard year starts on Thursday, or Wed in a leap year
|
52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57 package java.time.temporal;
58
59 import static java.time.DayOfWeek.THURSDAY;
60 import static java.time.DayOfWeek.WEDNESDAY;
61 import static java.time.temporal.ChronoField.DAY_OF_WEEK;
62 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
63 import static java.time.temporal.ChronoField.EPOCH_DAY;
64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
65 import static java.time.temporal.ChronoField.YEAR;
66 import static java.time.temporal.ChronoUnit.DAYS;
67 import static java.time.temporal.ChronoUnit.FOREVER;
68 import static java.time.temporal.ChronoUnit.MONTHS;
69 import static java.time.temporal.ChronoUnit.WEEKS;
70 import static java.time.temporal.ChronoUnit.YEARS;
71
72 import java.time.Duration;
73 import java.time.LocalDate;
74 import java.time.chrono.Chronology;
75 import java.time.chrono.IsoChronology;
76 import java.time.format.ResolverStyle;
77 import java.util.HashMap;
78 import java.util.Locale;
79 import java.util.Map;
80 import java.util.Objects;
81 import java.util.ResourceBundle;
82
83 import sun.util.locale.provider.LocaleProviderAdapter;
84 import sun.util.locale.provider.LocaleResources;
85
86 /**
87 * Fields and units specific to the ISO-8601 calendar system,
88 * including quarter-of-year and week-based-year.
89 * <p>
90 * This class defines fields and units that are specific to the ISO calendar system.
91 *
92 * <h3>Quarter of year</h3>
93 * The ISO-8601 standard is based on the standard civic 12 month year.
94 * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4.
95 * <p>
96 * January, February and March are in Q1.
97 * April, May and June are in Q2.
98 * July, August and September are in Q3.
99 * October, November and December are in Q4.
100 * <p>
101 * The complete date is expressed using three fields:
102 * <p><ul>
103 * <li>{@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92
104 * <li>{@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the week within the week-based-year
151 * This class is immutable and thread-safe.
152 *
153 * @since 1.8
154 */
155 public final class IsoFields {
156
157 /**
158 * The field that represents the day-of-quarter.
159 * <p>
160 * This field allows the day-of-quarter value to be queried and set.
161 * The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91
162 * in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4.
163 * <p>
164 * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year
165 * are available.
166 * <p>
167 * When setting this field, the value is allowed to be partially lenient, taking any
168 * value from 1 to 92. If the quarter has less than 92 days, then day 92, and
169 * potentially day 91, is in the following quarter.
170 * <p>
171 * In the resolving phase of parsing, a date can be created from a year,
172 * quarter-of-year and day-of-quarter.
173 * <p>
174 * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
175 * validated against their range of valid values. The day-of-quarter field
176 * is validated from 1 to 90, 91 or 92 depending on the year and quarter.
177 * <p>
178 * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
179 * validated against their range of valid values. The day-of-quarter field is
180 * validated between 1 and 92, ignoring the actual range based on the year and quarter.
181 * If the day-of-quarter exceeds the actual range by one day, then the resulting date
182 * is one day later. If the day-of-quarter exceeds the actual range by two days,
183 * then the resulting date is two days later.
184 * <p>
185 * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the year is validated
186 * against the range of valid values. The resulting date is calculated equivalent to
187 * the following three stage approach. First, create a date on the first of January
188 * in the requested year. Then take the quarter-of-year, subtract one, and add the
189 * amount in quarters to the date. Finally, take the day-of-quarter, subtract one,
190 * and add the amount in days to the date.
191 * <p>
192 * This unit is an immutable and thread-safe singleton.
193 */
194 public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER;
195 /**
196 * The field that represents the quarter-of-year.
197 * <p>
198 * This field allows the quarter-of-year value to be queried and set.
199 * The quarter-of-year has values from 1 to 4.
200 * <p>
201 * The quarter-of-year can only be calculated if the month-of-year is available.
202 * <p>
203 * In the resolving phase of parsing, a date can be created from a year,
204 * quarter-of-year and day-of-quarter.
205 * See {@link #DAY_OF_QUARTER} for details.
206 * <p>
207 * This unit is an immutable and thread-safe singleton.
208 */
209 public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR;
210 /**
211 * The field that represents the week-of-week-based-year.
212 * <p>
213 * This field allows the week of the week-based-year value to be queried and set.
214 * The week-of-week-based-year has values from 1 to 52, or 53 if the
215 * week-based-year has 53 weeks.
216 * <p>
217 * In the resolving phase of parsing, a date can be created from a
218 * week-based-year, week-of-week-based-year and day-of-week.
219 * <p>
220 * In {@linkplain ResolverStyle#STRICT strict mode}, all three fields are
221 * validated against their range of valid values. The week-of-week-based-year
222 * field is validated from 1 to 52 or 53 depending on the week-based-year.
223 * <p>
224 * In {@linkplain ResolverStyle#SMART smart mode}, all three fields are
225 * validated against their range of valid values. The week-of-week-based-year
226 * field is validated between 1 and 53, ignoring the week-based-year.
227 * If the week-of-week-based-year is 53, but the week-based-year only has
228 * 52 weeks, then the resulting date is in week 1 of the following week-based-year.
229 * <p>
230 * In {@linkplain ResolverStyle#LENIENT lenient mode}, only the week-based-year
231 * is validated against the range of valid values. If the day-of-week is outside
232 * the range 1 to 7, then the resulting date is adjusted by a suitable number of
233 * weeks to reduce the day-of-week to the range 1 to 7. If the week-of-week-based-year
234 * value is outside the range 1 to 52, then any excess weeks are added or subtracted
235 * from the resulting date.
236 * <p>
237 * This unit is an immutable and thread-safe singleton.
238 */
239 public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR;
240 /**
241 * The field that represents the week-based-year.
242 * <p>
243 * This field allows the week-based-year value to be queried and set.
244 * <p>
245 * The field has a range that matches {@link LocalDate#MAX} and {@link LocalDate#MIN}.
246 * <p>
247 * In the resolving phase of parsing, a date can be created from a
248 * week-based-year, week-of-week-based-year and day-of-week.
249 * See {@link #WEEK_OF_WEEK_BASED_YEAR} for details.
250 * <p>
251 * This unit is an immutable and thread-safe singleton.
252 */
253 public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR;
254 /**
255 * The unit that represents week-based-years for the purpose of addition and subtraction.
256 * <p>
257 * This allows a number of week-based-years to be added to, or subtracted from, a date.
258 * The unit is equal to either 52 or 53 weeks.
259 * The estimated duration of a week-based-year is the same as that of a standard ISO
260 * year at {@code 365.2425 Days}.
261 * <p>
262 * The rules for addition add the number of week-based-years to the existing value
263 * for the week-based-year field. If the resulting week-based-year only has 52 weeks,
264 * then the date will be in week 1 of the following week-based-year.
265 * <p>
266 * This unit is an immutable and thread-safe singleton.
267 */
268 public static final TemporalUnit WEEK_BASED_YEARS = Unit.WEEK_BASED_YEARS;
269 /**
270 * Unit that represents the concept of a quarter-year.
295 @Override
296 public TemporalUnit getBaseUnit() {
297 return DAYS;
298 }
299 @Override
300 public TemporalUnit getRangeUnit() {
301 return QUARTER_YEARS;
302 }
303 @Override
304 public ValueRange range() {
305 return ValueRange.of(1, 90, 92);
306 }
307 @Override
308 public boolean isSupportedBy(TemporalAccessor temporal) {
309 return temporal.isSupported(DAY_OF_YEAR) && temporal.isSupported(MONTH_OF_YEAR) &&
310 temporal.isSupported(YEAR) && isIso(temporal);
311 }
312 @Override
313 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
314 if (isSupportedBy(temporal) == false) {
315 throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter");
316 }
317 long qoy = temporal.getLong(QUARTER_OF_YEAR);
318 if (qoy == 1) {
319 long year = temporal.getLong(YEAR);
320 return (IsoChronology.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90));
321 } else if (qoy == 2) {
322 return ValueRange.of(1, 91);
323 } else if (qoy == 3 || qoy == 4) {
324 return ValueRange.of(1, 92);
325 } // else value not from 1 to 4, so drop through
326 return range();
327 }
328 @Override
329 public long getFrom(TemporalAccessor temporal) {
330 if (isSupportedBy(temporal) == false) {
331 throw new UnsupportedTemporalTypeException("Unsupported field: DayOfQuarter");
332 }
333 int doy = temporal.get(DAY_OF_YEAR);
334 int moy = temporal.get(MONTH_OF_YEAR);
335 long year = temporal.getLong(YEAR);
336 return doy - QUARTER_DAYS[((moy - 1) / 3) + (IsoChronology.INSTANCE.isLeapYear(year) ? 4 : 0)];
337 }
338 @SuppressWarnings("unchecked")
339 @Override
340 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
341 // calls getFrom() to check if supported
342 long curValue = getFrom(temporal);
343 range().checkValidValue(newValue, this); // leniently check from 1 to 92 TODO: check
344 return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue));
345 }
346 @Override
347 public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long doq, ResolverStyle resolverStyle) {
348 if ((temporal.isSupported(YEAR) && temporal.isSupported(QUARTER_OF_YEAR)) == false) {
349 return null;
350 }
351 int y = temporal.get(YEAR); // validated
352 LocalDate date;
353 if (resolverStyle == ResolverStyle.LENIENT) {
354 long qoy = temporal.getLong(QUARTER_OF_YEAR); // unvalidated
355 date = LocalDate.of(y, 1, 1).plusMonths(Math.multiplyExact(Math.subtractExact(qoy, 1), 3));
356 } else {
357 int qoy = temporal.get(QUARTER_OF_YEAR); // validated
358 date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1);
359 if (doq < 1 || doq > 90) {
360 if (resolverStyle == ResolverStyle.STRICT) {
361 rangeRefinedBy(date).checkValidValue(doq, this); // only allow exact range
362 } else { // SMART
363 range().checkValidValue(doq, this); // allow 1-92 rolling into next quarter
364 }
365 }
366 }
367 long epochDay = Math.addExact(date.toEpochDay(), Math.subtractExact(doq, 1));
368 Map<TemporalField, Long> result = new HashMap<>(4, 1.0f);
369 result.put(EPOCH_DAY, epochDay);
370 result.put(YEAR, null);
371 result.put(QUARTER_OF_YEAR, null);
372 return result;
373 }
374 },
375 QUARTER_OF_YEAR {
376 @Override
377 public String getName() {
378 return "QuarterOfYear";
379 }
380 @Override
381 public TemporalUnit getBaseUnit() {
382 return QUARTER_YEARS;
383 }
384 @Override
385 public TemporalUnit getRangeUnit() {
386 return YEARS;
387 }
388 @Override
389 public ValueRange range() {
390 return ValueRange.of(1, 4);
391 }
392 @Override
393 public boolean isSupportedBy(TemporalAccessor temporal) {
394 return temporal.isSupported(MONTH_OF_YEAR) && isIso(temporal);
395 }
396 @Override
397 public long getFrom(TemporalAccessor temporal) {
398 if (isSupportedBy(temporal) == false) {
399 throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
400 }
401 long moy = temporal.getLong(MONTH_OF_YEAR);
402 return ((moy + 2) / 3);
403 }
404 @SuppressWarnings("unchecked")
405 @Override
406 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
407 // calls getFrom() to check if supported
408 long curValue = getFrom(temporal);
409 range().checkValidValue(newValue, this); // strictly check from 1 to 4
410 return (R) temporal.with(MONTH_OF_YEAR, temporal.getLong(MONTH_OF_YEAR) + (newValue - curValue) * 3);
411 }
412 },
413 WEEK_OF_WEEK_BASED_YEAR {
414 @Override
415 public String getName() {
416 return "WeekOfWeekBasedYear";
417 }
418
419 @Override
420 public String getDisplayName(Locale locale) {
421 Objects.requireNonNull(locale, "locale");
422 LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased()
423 .getLocaleResources(locale);
424 ResourceBundle rb = lr.getJavaTimeFormatData();
425 return rb.containsKey("field.week") ? rb.getString("field.week") : getName();
426 }
427
428 @Override
429 public TemporalUnit getBaseUnit() {
430 return WEEKS;
431 }
432 @Override
433 public TemporalUnit getRangeUnit() {
434 return WEEK_BASED_YEARS;
435 }
436 @Override
437 public ValueRange range() {
438 return ValueRange.of(1, 52, 53);
439 }
440 @Override
441 public boolean isSupportedBy(TemporalAccessor temporal) {
442 return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
443 }
444 @Override
445 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
446 if (isSupportedBy(temporal) == false) {
447 throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear");
448 }
449 return getWeekRange(LocalDate.from(temporal));
450 }
451 @Override
452 public long getFrom(TemporalAccessor temporal) {
453 if (isSupportedBy(temporal) == false) {
454 throw new UnsupportedTemporalTypeException("Unsupported field: WeekOfWeekBasedYear");
455 }
456 return getWeek(LocalDate.from(temporal));
457 }
458 @SuppressWarnings("unchecked")
459 @Override
460 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
461 // calls getFrom() to check if supported
462 range().checkValidValue(newValue, this); // lenient range
463 return (R) temporal.plus(Math.subtractExact(newValue, getFrom(temporal)), WEEKS);
464 }
465 @Override
466 public Map<TemporalField, Long> resolve(TemporalAccessor temporal, long wowby, ResolverStyle resolverStyle) {
467 if ((temporal.isSupported(WEEK_BASED_YEAR) && temporal.isSupported(DAY_OF_WEEK)) == false) {
468 return null;
469 }
470 int wby = temporal.get(WEEK_BASED_YEAR); // validated
471 LocalDate date = LocalDate.of(wby, 1, 4);
472 if (resolverStyle == ResolverStyle.LENIENT) {
473 long dow = temporal.getLong(DAY_OF_WEEK); // unvalidated
474 if (dow > 7) {
475 date = date.plusWeeks((dow - 1) / 7);
476 dow = ((dow - 1) % 7) + 1;
477 } else if (dow < 1) {
478 date = date.plusWeeks(Math.subtractExact(dow, 7) / 7);
479 dow = ((dow + 6) % 7) + 1;
480 }
481 date = date.plusWeeks(Math.subtractExact(wowby, 1)).with(DAY_OF_WEEK, dow);
482 } else {
483 int dow = temporal.get(DAY_OF_WEEK); // validated
484 if (wowby < 1 || wowby > 52) {
485 if (resolverStyle == ResolverStyle.STRICT) {
486 getWeekRange(date).checkValidValue(wowby, this); // only allow exact range
487 } else { // SMART
488 range().checkValidValue(wowby, this); // allow 1-53 rolling into next year
489 }
490 }
491 date = date.plusWeeks(wowby - 1).with(DAY_OF_WEEK, dow);
492 }
493 Map<TemporalField, Long> result = new HashMap<>(2, 1.0f);
494 result.put(EPOCH_DAY, date.toEpochDay());
495 result.put(WEEK_BASED_YEAR, null);
496 result.put(DAY_OF_WEEK, null);
497 return result;
498 }
499 },
500 WEEK_BASED_YEAR {
501 @Override
502 public String getName() {
503 return "WeekBasedYear";
504 }
505 @Override
506 public TemporalUnit getBaseUnit() {
507 return WEEK_BASED_YEARS;
508 }
509 @Override
510 public TemporalUnit getRangeUnit() {
511 return FOREVER;
512 }
513 @Override
514 public ValueRange range() {
515 return YEAR.range();
516 }
517 @Override
518 public boolean isSupportedBy(TemporalAccessor temporal) {
519 return temporal.isSupported(EPOCH_DAY) && isIso(temporal);
520 }
521 @Override
522 public long getFrom(TemporalAccessor temporal) {
523 if (isSupportedBy(temporal) == false) {
524 throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
525 }
526 return getWeekBasedYear(LocalDate.from(temporal));
527 }
528 @SuppressWarnings("unchecked")
529 @Override
530 public <R extends Temporal> R adjustInto(R temporal, long newValue) {
531 if (isSupportedBy(temporal) == false) {
532 throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
533 }
534 int newVal = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check
535 LocalDate date = LocalDate.from(temporal);
536 int week = getWeek(date);
537 date = date.withDayOfYear(180).withYear(newVal).with(WEEK_OF_WEEK_BASED_YEAR, week);
538 return (R) date.with(date);
539 }
540 };
541
542 @Override
543 public boolean isDateBased() {
544 return true;
545 }
546
547 @Override
548 public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
549 return range();
550 }
551
552 @Override
553 public String toString() {
554 return getName();
555 }
556
557 //-------------------------------------------------------------------------
558 private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274};
559
560 private static boolean isIso(TemporalAccessor temporal) {
561 return Chronology.from(temporal).equals(IsoChronology.INSTANCE);
562 }
563
564 private static ValueRange getWeekRange(LocalDate date) {
565 int wby = getWeekBasedYear(date);
566 date = date.withDayOfYear(1).withYear(wby);
567 // 53 weeks if standard year starts on Thursday, or Wed in a leap year
|