UNPKG

24.6 kBJavaScriptView Raw
1/*
2 * @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
3 * @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
4 * @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
5 */
6
7import {UnsupportedTemporalTypeException, IllegalStateException} from '../errors';
8
9import {DayOfWeek} from '../DayOfWeek';
10import {Duration} from '../Duration';
11import {MathUtil} from '../MathUtil';
12import {LocalDate} from '../LocalDate';
13
14import {ChronoField} from './ChronoField';
15import {ChronoUnit} from './ChronoUnit';
16import {TemporalField} from './TemporalField';
17import {TemporalUnit} from './TemporalUnit';
18import {ValueRange} from './ValueRange';
19
20import {IsoChronology} from '../chrono/IsoChronology';
21
22import {ResolverStyle} from '../format/ResolverStyle';
23
24/**
25 * Fields and units specific to the ISO-8601 calendar system,
26 * including quarter-of-year and week-based-year.
27 *
28 * This class defines fields and units that are specific to the ISO calendar system.
29 *
30 * ### Quarter of year
31 *
32 * The ISO-8601 standard is based on the standard civic 12 month year.
33 * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4.
34 *
35 * January, February and March are in Q1.
36 * April, May and June are in Q2.
37 * July, August and September are in Q3.
38 * October, November and December are in Q4.
39 *
40 * The complete date is expressed using three fields:
41 *
42 * * `IsoFields.DAY_OF_QUARTER` - the day within the quarter, from 1 to 90, 91 or 92
43 * * `QUARTER_OF_YEAR` - the week within the week-based-year
44 * * `ChronoField.YEAR` - the standard ISO year (see {@link ChronoField})
45 *
46 * ### Week based years
47 *
48 * The ISO-8601 standard was originally intended as a data interchange format,
49 * defining a string format for dates and times. However, it also defines an
50 * alternate way of expressing the date, based on the concept of week-based-year.
51 *
52 * The date is expressed using three fields:
53 *
54 * * `ChronoField.DAY_OF_WEEK` - the standard field defining the
55 * day-of-week from Monday (1) to Sunday (7) (see {@link ChronoField})
56 * * `WEEK_OF_WEEK_BASED_YEAR` - the week within the week-based-year
57 * * `WEEK_BASED_YEAR` - the week-based-year
58 *
59 * The week-based-year itself is defined relative to the standard ISO proleptic year.
60 * It differs from the standard year in that it always starts on a Monday.
61 *
62 * The first week of a week-based-year is the first Monday-based week of the standard
63 * ISO year that has at least 4 days in the new year.
64 *
65 * * If January 1st is Monday then week 1 starts on January 1st
66 * * If January 1st is Tuesday then week 1 starts on December 31st of the previous standard year
67 * * If January 1st is Wednesday then week 1 starts on December 30th of the previous standard year
68 * * If January 1st is Thursday then week 1 starts on December 29th of the previous standard year
69 * * If January 1st is Friday then week 1 starts on January 4th
70 * * If January 1st is Saturday then week 1 starts on January 3rd
71 * * If January 1st is Sunday then week 1 starts on January 2nd
72 *
73 * There are 52 weeks in most week-based years, however on occasion there are 53 weeks.
74 *
75 * For example:
76 *
77 * * Sunday, 2008-12-28: Week 52 of week-based-year 2008
78 * * Monday, 2008-12-29: Week 1 of week-based-year 2009
79 * * Wednesday, 2008-12-31: Week 1 of week-based-year 2009
80 * * Thursday, 2009-01-01: Week 1 of week-based-year 2009
81 * * Sunday, 2009-01-04: Week 1 of week-based-year 2009
82 * * Monday, 2009-01-05: Week 2 of week-based-year 2009
83 *
84 * @property {TemporalField} DAY_OF_QUARTER The field that represents the day-of-quarter.
85 *
86 * This field allows the day-of-quarter value to be queried and set.
87 * The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91
88 * in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4.
89 *
90 * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year
91 * are available.
92 *
93 * When setting this field, the value is allowed to be partially lenient, taking any
94 * value from 1 to 92. If the quarter has less than 92 days, then day 92, and
95 * potentially day 91, is in the following quarter.
96 *
97 * @property {TemporalField} QUARTER_OF_YEAR The field that represents the quarter-of-year.
98 *
99 * This field allows the quarter-of-year value to be queried and set.
100 * The quarter-of-year has values from 1 to 4.
101 *
102 * The day-of-quarter can only be calculated if the month-of-year is available.
103 *
104 * @property {TemporalField} WEEK_OF_WEEK_BASED_YEAR The field that represents the
105 * week-of-week-based-year.
106 *
107 * This field allows the week of the week-based-year value to be queried and set.
108 *
109 * @property {TemporalField} WEEK_BASED_YEAR The field that represents the week-based-year.
110 *
111 * This field allows the week-based-year value to be queried and set.
112 *
113 * @property {TemporalField} WEEK_BASED_YEARS The unit that represents week-based-years for
114 * the purpose of addition and subtraction.
115 *
116 * This allows a number of week-based-years to be added to, or subtracted from, a date.
117 * The unit is equal to either 52 or 53 weeks.
118 * The estimated duration of a week-based-year is the same as that of a standard ISO
119 * year at 365.2425 days.
120 *
121 * The rules for addition add the number of week-based-years to the existing value
122 * for the week-based-year field. If the resulting week-based-year only has 52 weeks,
123 * then the date will be in week 1 of the following week-based-year.
124 *
125 * @property {TemporalField} QUARTER_YEARS Unit that represents the concept of a quarter-year.
126 * For the ISO calendar system, it is equal to 3 months.
127 * The estimated duration of a quarter-year is one quarter of 365.2425 days.
128 *
129 * @typedef {Object} IsoFields
130 * @type {Object}
131 */
132export const IsoFields = {};
133
134//-----------------------------------------------------------------------
135
136const QUARTER_DAYS = [0, 90, 181, 273, 0, 91, 182, 274];
137
138/**
139 * Implementation of the field.
140 * @private
141 */
142class Field extends TemporalField{
143
144 /**
145 *
146 * @returns {boolean}
147 */
148 isDateBased() {
149 return true;
150 }
151
152 /**
153 *
154 * @returns {boolean}
155 */
156 isTimeBased() {
157 return false;
158 }
159
160 /**
161 *
162 * @returns {boolean}
163 */
164 _isIso() {
165 return true;
166 }
167
168 /**
169 *
170 * @param {LocalDate} date
171 * @returns {ValueRange}
172 */
173 static _getWeekRangeByLocalDate(date) {
174 const wby = Field._getWeekBasedYear(date);
175 return ValueRange.of(1, Field._getWeekRangeByYear(wby));
176 }
177
178 /**
179 *
180 * @param {number} wby
181 * @returns {number}
182 */
183 static _getWeekRangeByYear(wby) {
184 const date = LocalDate.of(wby, 1, 1);
185 // 53 weeks if standard year starts on Thursday, or Wed in a leap year
186 if (date.dayOfWeek() === DayOfWeek.THURSDAY || (date.dayOfWeek() === DayOfWeek.WEDNESDAY && date.isLeapYear())) {
187 return 53;
188 }
189 return 52;
190 }
191
192 /**
193 *
194 * @param {LocalDate} date
195 * @returns {number}
196 */
197 static _getWeek(date) {
198 const dow0 = date.dayOfWeek().ordinal();
199 const doy0 = date.dayOfYear() - 1;
200 const doyThu0 = doy0 + (3 - dow0); // adjust to mid-week Thursday (which is 3 indexed from zero)
201 const alignedWeek = MathUtil.intDiv(doyThu0, 7);
202 const firstThuDoy0 = doyThu0 - (alignedWeek * 7);
203 let firstMonDoy0 = firstThuDoy0 - 3;
204 if (firstMonDoy0 < -3) {
205 firstMonDoy0 += 7;
206 }
207 if (doy0 < firstMonDoy0) {
208 return Field._getWeekRangeByLocalDate(date.withDayOfYear(180).minusYears(1)).maximum();
209 }
210 let week = MathUtil.intDiv((doy0 - firstMonDoy0), 7) + 1;
211 if (week === 53) {
212 if ((firstMonDoy0 === -3 || (firstMonDoy0 === -2 && date.isLeapYear())) === false) {
213 week = 1;
214 }
215 }
216 return week;
217 }
218
219 /**
220 *
221 * @param {LocalDate} date
222 * @returns {number}
223 */
224 static _getWeekBasedYear(date) {
225 let year = date.year();
226 let doy = date.dayOfYear();
227 if (doy <= 3) {
228 const dow = date.dayOfWeek().ordinal();
229 if (doy - dow < -2) {
230 year--;
231 }
232 } else if (doy >= 363) {
233 const dow = date.dayOfWeek().ordinal();
234 doy = doy - 363 - (date.isLeapYear() ? 1 : 0);
235 if (doy - dow >= 0) {
236 year++;
237 }
238 }
239 return year;
240 }
241
242 /**
243 *
244 * @returns {string}
245 */
246 displayName(/*locale*/) {
247 return this.toString();
248 }
249
250 /**
251 *
252 * @returns {null}
253 */
254 resolve() {
255 return null;
256 }
257
258 name(){
259 return this.toString();
260 }
261
262}
263
264/**
265 * @private
266 */
267class DAY_OF_QUARTER_FIELD extends Field {
268
269 /**
270 *
271 * @returns {string}
272 */
273 toString() {
274 return 'DayOfQuarter';
275 }
276
277 /**
278 *
279 * @returns {TemporalUnit}
280 */
281 baseUnit() {
282 return ChronoUnit.DAYS;
283 }
284
285 /**
286 *
287 * @returns {TemporalUnit}
288 */
289 rangeUnit() {
290 return QUARTER_YEARS;
291 }
292
293 /**
294 *
295 * @returns {ValueRange}
296 */
297 range() {
298 return ValueRange.of(1, 90, 92);
299 }
300
301 /**
302 *
303 * @param {TemporalAccessor} temporal
304 * @returns {boolean}
305 */
306 isSupportedBy(temporal) {
307 return temporal.isSupported(ChronoField.DAY_OF_YEAR) && temporal.isSupported(ChronoField.MONTH_OF_YEAR) &&
308 temporal.isSupported(ChronoField.YEAR) && this._isIso(temporal);
309 }
310
311
312 /**
313 *
314 * @param {TemporalAccessor} temporal
315 * @returns {ValueRange}
316 */
317 rangeRefinedBy(temporal) {
318 if (temporal.isSupported(this) === false) {
319 throw new UnsupportedTemporalTypeException('Unsupported field: DayOfQuarter');
320 }
321 const qoy = temporal.getLong(QUARTER_OF_YEAR);
322 if (qoy === 1) {
323 const year = temporal.getLong(ChronoField.YEAR);
324 return (IsoChronology.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90));
325 } else if (qoy === 2) {
326 return ValueRange.of(1, 91);
327 } else if (qoy === 3 || qoy === 4) {
328 return ValueRange.of(1, 92);
329 } // else value not from 1 to 4, so drop through
330 return this.range();
331 }
332
333 /**
334 *
335 * @param {TemporalAccessor} temporal
336 * @returns {number}
337 */
338 getFrom(temporal) {
339 if (temporal.isSupported(this) === false) {
340 throw new UnsupportedTemporalTypeException('Unsupported field: DayOfQuarter');
341 }
342 const doy = temporal.get(ChronoField.DAY_OF_YEAR);
343 const moy = temporal.get(ChronoField.MONTH_OF_YEAR);
344 const year = temporal.getLong(ChronoField.YEAR);
345 return doy - QUARTER_DAYS[MathUtil.intDiv((moy - 1), 3) + (IsoChronology.isLeapYear(year) ? 4 : 0)];
346 }
347
348 /**
349 *
350 * @param {Temporal} temporal
351 * @param {number} newValue
352 * @returns {temporal}
353 */
354 adjustInto(temporal, newValue) {
355 const curValue = this.getFrom(temporal);
356 this.range().checkValidValue(newValue, this);
357 return temporal.with(ChronoField.DAY_OF_YEAR, temporal.getLong(ChronoField.DAY_OF_YEAR) + (newValue - curValue));
358 }
359
360 /**
361 *
362 * @param {Map<TemporalField, number>} fieldValues
363 * @param {TemporalAccessor} partialTemporal
364 * @param {ResolverStyle} resolverStyle
365 * @returns {ValueRange}
366 */
367 resolve(fieldValues, partialTemporal, resolverStyle) {
368 const yearLong = fieldValues.get(ChronoField.YEAR);
369 const qoyLong = fieldValues.get(QUARTER_OF_YEAR);
370 if (yearLong == null || qoyLong == null) {
371 return null;
372 }
373 const y = ChronoField.YEAR.checkValidIntValue(yearLong);
374 const doq = fieldValues.get(DAY_OF_QUARTER);
375 let date;
376 if (resolverStyle === ResolverStyle.LENIENT) {
377 const qoy = qoyLong;
378 date = LocalDate.of(y, 1, 1);
379 date = date.plusMonths(MathUtil.safeMultiply(MathUtil.safeSubtract(qoy, 1), 3));
380 date = date.plusDays(MathUtil.safeSubtract(doq, 1));
381 } else {
382 const qoy = QUARTER_OF_YEAR.range().checkValidIntValue(qoyLong, QUARTER_OF_YEAR);
383 if (resolverStyle === ResolverStyle.STRICT) {
384 let max = 92;
385 if (qoy === 1) {
386 max = (IsoChronology.isLeapYear(y) ? 91 : 90);
387 } else if (qoy === 2) {
388 max = 91;
389 }
390 ValueRange.of(1, max).checkValidValue(doq, this);
391 } else {
392 this.range().checkValidValue(doq, this); // leniently check from 1 to 92
393 }
394 date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1).plusDays(doq - 1);
395 }
396 fieldValues.remove(this);
397 fieldValues.remove(ChronoField.YEAR);
398 fieldValues.remove(QUARTER_OF_YEAR);
399 return date;
400 }
401}
402
403/**
404 * @private
405 */
406class QUARTER_OF_YEAR_FIELD extends Field {
407
408 /**
409 *
410 * @returns {string}
411 */
412 toString() {
413 return 'QuarterOfYear';
414 }
415
416 /**
417 *
418 * @returns {TemporalUnit}
419 */
420 baseUnit() {
421 return QUARTER_YEARS;
422 }
423
424 /**
425 *
426 * @returns {TemporalUnit}
427 */
428 rangeUnit() {
429 return ChronoUnit.YEARS;
430 }
431
432 /**
433 *
434 * @returns {ValueRange}
435 */
436 range() {
437 return ValueRange.of(1, 4);
438 }
439
440 /**
441 *
442 * @param {TemporalAccessor} temporal
443 * @returns {boolean}
444 */
445 isSupportedBy(temporal) {
446 return temporal.isSupported(ChronoField.MONTH_OF_YEAR) && this._isIso(temporal);
447 }
448
449
450 /**
451 *
452 * @param {TemporalAccessor} temporal
453 * @returns {ValueRange}
454 */
455 //eslint-disable-next-line no-unused-vars
456 rangeRefinedBy(temporal) {
457 return this.range();
458 }
459
460 /**
461 *
462 * @param {TemporalAccessor} temporal
463 * @returns {number}
464 */
465 getFrom(temporal) {
466 if (temporal.isSupported(this) === false) {
467 throw new UnsupportedTemporalTypeException('Unsupported field: QuarterOfYear');
468 }
469 const moy = temporal.getLong(ChronoField.MONTH_OF_YEAR);
470 return MathUtil.intDiv((moy + 2), 3);
471 }
472
473 /**
474 *
475 * @param {Temporal} temporal
476 * @param {number} newValue
477 * @returns {temporal}
478 */
479 adjustInto(temporal, newValue) {
480 const curValue = this.getFrom(temporal);
481 this.range().checkValidValue(newValue, this);
482 return temporal.with(ChronoField.MONTH_OF_YEAR, temporal.getLong(ChronoField.MONTH_OF_YEAR) + (newValue - curValue) * 3);
483 }
484
485}
486
487/**
488 * @private
489 */
490class WEEK_OF_WEEK_BASED_YEAR_FIELD extends Field {
491
492 /**
493 *
494 * @returns {string}
495 */
496 toString() {
497 return 'WeekOfWeekBasedYear';
498 }
499
500 /**
501 *
502 * @returns {TemporalUnit}
503 */
504 baseUnit() {
505 return ChronoUnit.WEEKS;
506 }
507
508 /**
509 *
510 * @returns {TemporalUnit}
511 */
512 rangeUnit() {
513 return WEEK_BASED_YEARS;
514 }
515
516 /**
517 *
518 * @returns {ValueRange}
519 */
520 range() {
521 return ValueRange.of(1, 52, 53);
522 }
523
524 /**
525 *
526 * @param {TemporalAccessor} temporal
527 * @returns {boolean}
528 */
529 isSupportedBy(temporal) {
530 return temporal.isSupported(ChronoField.EPOCH_DAY) && this._isIso(temporal);
531 }
532
533
534 /**
535 *
536 * @param {TemporalAccessor} temporal
537 * @returns {ValueRange}
538 */
539 rangeRefinedBy(temporal) {
540 if (temporal.isSupported(this) === false) {
541 throw new UnsupportedTemporalTypeException('Unsupported field: WeekOfWeekBasedYear');
542 }
543 return Field._getWeekRangeByLocalDate(LocalDate.from(temporal));
544 }
545
546 /**
547 *
548 * @param {TemporalAccessor} temporal
549 * @returns {number}
550 */
551 getFrom(temporal) {
552 if (temporal.isSupported(this) === false) {
553 throw new UnsupportedTemporalTypeException('Unsupported field: WeekOfWeekBasedYear');
554 }
555 return Field._getWeek(LocalDate.from(temporal));
556 }
557
558 /**
559 *
560 * @param {Temporal} temporal
561 * @param {number} newValue
562 * @returns {temporal}
563 */
564 adjustInto(temporal, newValue) {
565 this.range().checkValidValue(newValue, this);
566 return temporal.plus(MathUtil.safeSubtract(newValue, this.getFrom(temporal)), ChronoUnit.WEEKS);
567 }
568
569 /**
570 *
571 * @param {Map<TemporalField, number>} fieldValues
572 * @param {TemporalAccessor} partialTemporal
573 * @param {ResolverStyle} resolverStyle
574 * @returns {ValueRange}
575 */
576 resolve(fieldValues, partialTemporal, resolverStyle) {
577 const wbyLong = fieldValues.get(WEEK_BASED_YEAR);
578 const dowLong = fieldValues.get(ChronoField.DAY_OF_WEEK);
579 if (wbyLong == null || dowLong == null) {
580 return null;
581 }
582 const wby = WEEK_BASED_YEAR.range().checkValidIntValue(wbyLong, WEEK_BASED_YEAR);
583 const wowby = fieldValues.get(WEEK_OF_WEEK_BASED_YEAR);
584 let date;
585 if (resolverStyle === ResolverStyle.LENIENT) {
586 let dow = dowLong;
587 let weeks = 0;
588 if (dow > 7) {
589 weeks = MathUtil.intDiv((dow - 1), 7);
590 dow = (MathUtil.intMod((dow - 1), 7) + 1);
591 } else if (dow < 1) {
592 weeks = MathUtil.intDiv(dow, 7) - 1;
593 dow = MathUtil.intMod(dow, 7) + 7;
594 }
595 date = LocalDate.of(wby, 1, 4).plusWeeks(wowby - 1).plusWeeks(weeks).with(ChronoField.DAY_OF_WEEK, dow);
596 } else {
597 const dow = ChronoField.DAY_OF_WEEK.checkValidIntValue(dowLong);
598 if (resolverStyle === ResolverStyle.STRICT) {
599 const temp = LocalDate.of(wby, 1, 4);
600 const range = Field._getWeekRangeByLocalDate(temp);
601 range.checkValidValue(wowby, this);
602 } else {
603 this.range().checkValidValue(wowby, this); // leniently check from 1 to 53
604 }
605 date = LocalDate.of(wby, 1, 4).plusWeeks(wowby - 1).with(ChronoField.DAY_OF_WEEK, dow);
606 }
607 fieldValues.remove(this);
608 fieldValues.remove(WEEK_BASED_YEAR);
609 fieldValues.remove(ChronoField.DAY_OF_WEEK);
610 return date;
611 }
612
613 /**
614 *
615 * @returns {string}
616 */
617 displayName() {
618 return 'Week';
619 }
620
621}
622
623/**
624 * @private
625 */
626class WEEK_BASED_YEAR_FIELD extends Field {
627
628 /**
629 *
630 * @returns {string}
631 */
632 toString() {
633 return 'WeekBasedYear';
634 }
635
636 /**
637 *
638 * @returns {TemporalUnit}
639 */
640 baseUnit() {
641 return WEEK_BASED_YEARS;
642 }
643
644 /**
645 *
646 * @returns {TemporalUnit}
647 */
648 rangeUnit() {
649 return ChronoUnit.FOREVER;
650 }
651
652 /**
653 *
654 * @returns {ValueRange}
655 */
656 range() {
657 return ChronoField.YEAR.range();
658 }
659
660 /**
661 *
662 * @param {TemporalAccessor} temporal
663 * @returns {boolean}
664 */
665 isSupportedBy(temporal) {
666 return temporal.isSupported(ChronoField.EPOCH_DAY) && this._isIso(temporal);
667 }
668
669
670 /**
671 *
672 * @param {TemporalAccessor} temporal
673 * @returns {ValueRange}
674 */
675 //eslint-disable-next-line no-unused-vars
676 rangeRefinedBy(temporal) {
677 return ChronoField.YEAR.range();
678 }
679
680 /**
681 *
682 * @param {TemporalAccessor} temporal
683 * @returns {number}
684 */
685 getFrom(temporal) {
686 if (temporal.isSupported(this) === false) {
687 throw new UnsupportedTemporalTypeException('Unsupported field: WeekBasedYear');
688 }
689 return Field._getWeekBasedYear(LocalDate.from(temporal));
690 }
691
692 /**
693 *
694 * @param {Temporal} temporal
695 * @param {number} newValue
696 * @returns {temporal}
697 */
698 adjustInto(temporal, newValue) {
699 if (this.isSupportedBy(temporal) === false) {
700 throw new UnsupportedTemporalTypeException('Unsupported field: WeekBasedYear');
701 }
702 const newWby = this.range().checkValidIntValue(newValue, WEEK_BASED_YEAR); // strict check
703 const date = LocalDate.from(temporal);
704 const dow = date.get(ChronoField.DAY_OF_WEEK);
705 let week = Field._getWeek(date);
706 if (week === 53 && Field._getWeekRangeByYear(newWby) === 52) {
707 week = 52;
708 }
709 let resolved = LocalDate.of(newWby, 1, 4); // 4th is guaranteed to be in week one
710 const days = (dow - resolved.get(ChronoField.DAY_OF_WEEK)) + ((week - 1) * 7);
711 resolved = resolved.plusDays(days);
712 return temporal.with(resolved);
713 }
714
715}
716
717//-----------------------------------------------------------------------
718/**
719 * Implementation of the period unit.
720 * @private
721 */
722class Unit extends TemporalUnit {
723
724 /**
725 *
726 * @param {string} name
727 * @param {Duration} estimatedDuration
728 * @private
729 */
730 constructor(name, estimatedDuration) {
731 super();
732 this._name = name;
733 this._duration = estimatedDuration;
734 }
735
736 /**
737 *
738 * @returns {Duration}
739 */
740 duration() {
741 return this._duration;
742 }
743
744 /**
745 *
746 * @returns {boolean}
747 */
748 isDurationEstimated() {
749 return true;
750 }
751
752 /**
753 *
754 * @returns {boolean}
755 */
756 isDateBased() {
757 return true;
758 }
759
760 /**
761 *
762 * @returns {boolean}
763 */
764 isTimeBased() {
765 return false;
766 }
767
768 /**
769 *
770 * @param {Temporal} temporal
771 * @returns {boolean}
772 */
773 isSupportedBy(temporal) {
774 return temporal.isSupported(ChronoField.EPOCH_DAY);
775 }
776
777 /**
778 *
779 * @param {Temporal} temporal
780 * @param {number} periodToAdd
781 * @returns {number}
782 */
783 addTo(temporal, periodToAdd) {
784 switch(this) {
785 case WEEK_BASED_YEARS: {
786 const added = MathUtil.safeAdd(temporal.get(WEEK_BASED_YEAR), periodToAdd);
787 return temporal.with(WEEK_BASED_YEAR, added);
788 }
789 case QUARTER_YEARS:
790 // no overflow (256 is multiple of 4)
791 return temporal.plus(MathUtil.intDiv(periodToAdd, 256), ChronoUnit.YEARS).plus(MathUtil.intMod(periodToAdd, 256) * 3, ChronoUnit.MONTHS);
792 default:
793 throw new IllegalStateException('Unreachable');
794 }
795 }
796
797 /**
798 *
799 * @param {Temporal} temporal1
800 * @param {Temporal} temporal2
801 * @returns {number}
802 */
803 between(temporal1, temporal2) {
804 switch(this) {
805 case WEEK_BASED_YEARS:
806 return MathUtil.safeSubtract(temporal2.getLong(WEEK_BASED_YEAR), temporal1.getLong(WEEK_BASED_YEAR));
807 case QUARTER_YEARS:
808 return MathUtil.intDiv(temporal1.until(temporal2, ChronoUnit.MONTHS), 3);
809 default:
810 throw new IllegalStateException('Unreachable');
811 }
812 }
813
814 toString() {
815 return name;
816 }
817}
818
819let DAY_OF_QUARTER = null;
820let QUARTER_OF_YEAR = null;
821let WEEK_OF_WEEK_BASED_YEAR = null;
822let WEEK_BASED_YEAR = null;
823let WEEK_BASED_YEARS = null;
824let QUARTER_YEARS = null;
825
826export function _init() {
827 DAY_OF_QUARTER = new DAY_OF_QUARTER_FIELD();
828 QUARTER_OF_YEAR = new QUARTER_OF_YEAR_FIELD();
829 WEEK_OF_WEEK_BASED_YEAR = new WEEK_OF_WEEK_BASED_YEAR_FIELD();
830 WEEK_BASED_YEAR = new WEEK_BASED_YEAR_FIELD();
831
832 WEEK_BASED_YEARS = new Unit('WeekBasedYears', Duration.ofSeconds(31556952));
833 QUARTER_YEARS = new Unit('QuarterYears', Duration.ofSeconds(31556952 / 4));
834
835 IsoFields.DAY_OF_QUARTER = DAY_OF_QUARTER;
836 IsoFields.QUARTER_OF_YEAR = QUARTER_OF_YEAR;
837 IsoFields.WEEK_OF_WEEK_BASED_YEAR = WEEK_OF_WEEK_BASED_YEAR;
838 IsoFields.WEEK_BASED_YEAR = WEEK_BASED_YEAR;
839 IsoFields.WEEK_BASED_YEARS = WEEK_BASED_YEARS;
840 IsoFields.QUARTER_YEARS = QUARTER_YEARS;
841
842 // this differs from threeten, but for ease of use we bring back good old joda time functionality
843 /**
844 * the week of the week based year as defined by the ISO8601 Standard with a Monday-based week
845 *
846 * @returns {number} the week a the week based year
847 */
848 LocalDate.prototype.isoWeekOfWeekyear = function () {
849 return this.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
850 };
851 /**
852 * the year of the week based year as defined by the ISO8601 Standard with a Monday-based week
853 *
854 * @returns {number} the year a the week based year
855 */
856 LocalDate.prototype.isoWeekyear = function () {
857 return this.get(IsoFields.WEEK_BASED_YEAR);
858 };
859}