62 package java.time;
63
64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
65 import static java.time.temporal.ChronoUnit.DAYS;
66 import static java.time.temporal.ChronoUnit.MONTHS;
67 import static java.time.temporal.ChronoUnit.YEARS;
68
69 import java.io.DataInput;
70 import java.io.DataOutput;
71 import java.io.IOException;
72 import java.io.InvalidObjectException;
73 import java.io.ObjectStreamException;
74 import java.io.Serializable;
75 import java.time.chrono.ChronoLocalDate;
76 import java.time.chrono.Chronology;
77 import java.time.format.DateTimeParseException;
78 import java.time.temporal.ChronoUnit;
79 import java.time.temporal.Temporal;
80 import java.time.temporal.TemporalAmount;
81 import java.time.temporal.TemporalUnit;
82 import java.time.temporal.ValueRange;
83 import java.util.Arrays;
84 import java.util.Collections;
85 import java.util.List;
86 import java.util.Objects;
87 import java.util.regex.Matcher;
88 import java.util.regex.Pattern;
89
90 /**
91 * A date-based amount of time, such as '2 years, 3 months and 4 days'.
92 * <p>
93 * This class models a quantity or amount of time in terms of years, months and days.
94 * See {@link Duration} for the time-based equivalent to this class.
95 * <p>
96 * Durations and period differ in their treatment of daylight savings time
97 * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact
98 * number of seconds, thus a duration of one day is always exactly 24 hours.
99 * By contrast, a {@code Period} will add a conceptual day, trying to maintain
100 * the local time.
101 * <p>
198 return create(0, 0, days);
199 }
200
201 //-----------------------------------------------------------------------
202 /**
203 * Obtains a {@code Period} representing a number of years, months and days.
204 * <p>
205 * This creates an instance based on years, months and days.
206 *
207 * @param years the amount of years, may be negative
208 * @param months the amount of months, may be negative
209 * @param days the amount of days, may be negative
210 * @return the period of years, months and days, not null
211 */
212 public static Period of(int years, int months, int days) {
213 return create(years, months, days);
214 }
215
216 //-----------------------------------------------------------------------
217 /**
218 * Obtains a {@code Period} consisting of the number of years, months,
219 * and days between two dates.
220 * <p>
221 * The start date is included, but the end date is not.
222 * The period is calculated by removing complete months, then calculating
223 * the remaining number of days, adjusting to ensure that both have the same sign.
224 * The number of months is then split into years and months based on a 12 month year.
225 * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
226 * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
227 * <p>
228 * The result of this method can be a negative period if the end is before the start.
229 * The negative sign will be the same in each of year, month and day.
230 *
231 * @param startDate the start date, inclusive, not null
232 * @param endDate the end date, exclusive, not null
233 * @return the period between this date and the end date, not null
234 * @see ChronoLocalDate#periodUntil(ChronoLocalDate)
235 */
236 public static Period between(LocalDate startDate, LocalDate endDate) {
237 return startDate.periodUntil(endDate);
238 }
239
240 //-----------------------------------------------------------------------
241 /**
242 * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
243 * <p>
244 * This will parse the string produced by {@code toString()} which is
245 * based on the ISO-8601 period format {@code PnYnMnD}.
246 * <p>
247 * The string starts with an optional sign, denoted by the ASCII negative
248 * or positive symbol. If negative, the whole period is negated.
249 * The ASCII letter "P" is next in upper or lower case.
250 * There are then three sections, each consisting of a number and a suffix.
251 * At least one of the three sections must be present.
252 * The sections have suffixes in ASCII of "Y", "M" and "D" for
253 * years, months and days, accepted in upper or lower case.
254 * The suffixes must occur in order.
255 * The number part of each section must consist of ASCII digits.
256 * The number may be prefixed by the ASCII negative or positive symbol.
257 * The number must parse to an {@code int}.
281 }
282 }
283 }
284 throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
285 }
286
287 private static int parseNumber(CharSequence text, String str, int negate) {
288 if (str == null) {
289 return 0;
290 }
291 int val = Integer.parseInt(str);
292 try {
293 return Math.multiplyExact(val, negate);
294 } catch (ArithmeticException ex) {
295 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
296 }
297 }
298
299 //-----------------------------------------------------------------------
300 /**
301 * Creates an instance.
302 *
303 * @param years the amount
304 * @param months the amount
305 * @param days the amount
306 */
307 private static Period create(int years, int months, int days) {
308 if ((years | months | days) == 0) {
309 return ZERO;
310 }
311 return new Period(years, months, days);
312 }
313
314 /**
315 * Constructor.
316 *
317 * @param years the amount
318 * @param months the amount
319 * @param days the amount
320 */
321 private Period(int years, int months, int days) {
322 this.years = years;
323 this.months = months;
324 this.days = days;
325 }
326
327 //-----------------------------------------------------------------------
328 /**
329 * Gets the value of the requested unit.
330 * <p>
331 * This returns a value for each of the three supported units,
332 * {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and
333 * {@link ChronoUnit#DAYS DAYS}.
334 * All other units throw an exception.
335 *
336 * @param unit the {@code TemporalUnit} for which to return the value
337 * @return the long value of the unit
338 * @throws DateTimeException if the unit is not supported
339 */
340 @Override
341 public long get(TemporalUnit unit) {
342 if (unit == ChronoUnit.YEARS) {
343 return getYears();
344 } else if (unit == ChronoUnit.MONTHS) {
345 return getMonths();
346 } else if (unit == ChronoUnit.DAYS) {
347 return getDays();
348 } else {
349 throw new DateTimeException("Unsupported unit: " + unit.getName());
350 }
351 }
352
353 /**
354 * Gets the set of units supported by this period.
355 * <p>
356 * The supported units are {@link ChronoUnit#YEARS YEARS},
357 * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
358 * They are returned in the order years, months, days.
359 * <p>
360 * This set can be used in conjunction with {@link #get(TemporalUnit)}
361 * to access the entire state of the period.
362 *
363 * @return a list containing the years, months and days units, not null
364 */
365 @Override
366 public List<TemporalUnit> getUnits() {
367 return SUPPORTED_UNITS;
368 }
369
482 * <p>
483 * This sets the amount of the days unit in a copy of this period.
484 * The years and months units are unaffected.
485 * <p>
486 * This instance is immutable and unaffected by this method call.
487 *
488 * @param days the days to represent, may be negative
489 * @return a {@code Period} based on this period with the requested days, not null
490 */
491 public Period withDays(int days) {
492 if (days == this.days) {
493 return this;
494 }
495 return create(years, months, days);
496 }
497
498 //-----------------------------------------------------------------------
499 /**
500 * Returns a copy of this period with the specified period added.
501 * <p>
502 * This operates separately on the years, months, days and the normalized time.
503 * There is no further normalization beyond the normalized time.
504 * <p>
505 * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"
506 * returns "3 years, 8 months and 5 days".
507 * <p>
508 * This instance is immutable and unaffected by this method call.
509 *
510 * @param amountToAdd the period to add, not null
511 * @return a {@code Period} based on this period with the requested period added, not null
512 * @throws ArithmeticException if numeric overflow occurs
513 */
514 public Period plus(Period amountToAdd) {
515 return create(
516 Math.addExact(years, amountToAdd.years),
517 Math.addExact(months, amountToAdd.months),
518 Math.addExact(days, amountToAdd.days));
519 }
520
521 /**
522 * Returns a copy of this period with the specified years added.
523 * <p>
565 * The years and months units are unaffected.
566 * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
567 * <p>
568 * This instance is immutable and unaffected by this method call.
569 *
570 * @param daysToAdd the days to add, positive or negative
571 * @return a {@code Period} based on this period with the specified days added, not null
572 * @throws ArithmeticException if numeric overflow occurs
573 */
574 public Period plusDays(long daysToAdd) {
575 if (daysToAdd == 0) {
576 return this;
577 }
578 return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
579 }
580
581 //-----------------------------------------------------------------------
582 /**
583 * Returns a copy of this period with the specified period subtracted.
584 * <p>
585 * This operates separately on the years, months, days and the normalized time.
586 * There is no further normalization beyond the normalized time.
587 * <p>
588 * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"
589 * returns "-1 years, 4 months and 1 day".
590 * <p>
591 * This instance is immutable and unaffected by this method call.
592 *
593 * @param amountToSubtract the period to subtract, not null
594 * @return a {@code Period} based on this period with the requested period subtracted, not null
595 * @throws ArithmeticException if numeric overflow occurs
596 */
597 public Period minus(Period amountToSubtract) {
598 return create(
599 Math.subtractExact(years, amountToSubtract.years),
600 Math.subtractExact(months, amountToSubtract.months),
601 Math.subtractExact(days, amountToSubtract.days));
602 }
603
604 /**
605 * Returns a copy of this period with the specified years subtracted.
606 * <p>
828 temporal = temporal.minus(years, YEARS);
829 }
830 if (months != 0) {
831 temporal = temporal.minus(months, MONTHS);
832 }
833 }
834 }
835 if (days != 0) {
836 temporal = temporal.minus(days, DAYS);
837 }
838 return temporal;
839 }
840
841 /**
842 * Calculates the range of months based on the temporal.
843 *
844 * @param temporal the temporal, not null
845 * @return the month range, negative if not fixed range
846 */
847 private long monthRange(Temporal temporal) {
848 ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR);
849 if (startRange.isFixed() && startRange.isIntValue()) {
850 return startRange.getMaximum() - startRange.getMinimum() + 1;
851 }
852 return -1;
853 }
854
855 //-----------------------------------------------------------------------
856 /**
857 * Checks if this period is equal to another period.
858 * <p>
859 * The comparison is based on the amounts held in the period.
860 * To be equal, the years, months and days units must be individually equal.
861 * Note that this means that a period of "15 Months" is not equal to a period
862 * of "1 Year and 3 Months".
863 *
864 * @param obj the object to check, null returns false
865 * @return true if this is equal to the other period
866 */
867 @Override
868 public boolean equals(Object obj) {
869 if (this == obj) {
870 return true;
871 }
|
62 package java.time;
63
64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
65 import static java.time.temporal.ChronoUnit.DAYS;
66 import static java.time.temporal.ChronoUnit.MONTHS;
67 import static java.time.temporal.ChronoUnit.YEARS;
68
69 import java.io.DataInput;
70 import java.io.DataOutput;
71 import java.io.IOException;
72 import java.io.InvalidObjectException;
73 import java.io.ObjectStreamException;
74 import java.io.Serializable;
75 import java.time.chrono.ChronoLocalDate;
76 import java.time.chrono.Chronology;
77 import java.time.format.DateTimeParseException;
78 import java.time.temporal.ChronoUnit;
79 import java.time.temporal.Temporal;
80 import java.time.temporal.TemporalAmount;
81 import java.time.temporal.TemporalUnit;
82 import java.time.temporal.UnsupportedTemporalTypeException;
83 import java.time.temporal.ValueRange;
84 import java.util.Arrays;
85 import java.util.Collections;
86 import java.util.List;
87 import java.util.Objects;
88 import java.util.regex.Matcher;
89 import java.util.regex.Pattern;
90
91 /**
92 * A date-based amount of time, such as '2 years, 3 months and 4 days'.
93 * <p>
94 * This class models a quantity or amount of time in terms of years, months and days.
95 * See {@link Duration} for the time-based equivalent to this class.
96 * <p>
97 * Durations and period differ in their treatment of daylight savings time
98 * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact
99 * number of seconds, thus a duration of one day is always exactly 24 hours.
100 * By contrast, a {@code Period} will add a conceptual day, trying to maintain
101 * the local time.
102 * <p>
199 return create(0, 0, days);
200 }
201
202 //-----------------------------------------------------------------------
203 /**
204 * Obtains a {@code Period} representing a number of years, months and days.
205 * <p>
206 * This creates an instance based on years, months and days.
207 *
208 * @param years the amount of years, may be negative
209 * @param months the amount of months, may be negative
210 * @param days the amount of days, may be negative
211 * @return the period of years, months and days, not null
212 */
213 public static Period of(int years, int months, int days) {
214 return create(years, months, days);
215 }
216
217 //-----------------------------------------------------------------------
218 /**
219 * Obtains an instance of {@code Period} from a temporal amount.
220 * <p>
221 * This obtains a period based on the specified amount.
222 * A {@code TemporalAmount} represents an amount of time, which may be
223 * date-based or time-based, which this factory extracts to a period.
224 * <p>
225 * The conversion loops around the set of units from the amount and uses
226 * the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS}
227 * and {@link ChronoUnit#DAYS DAYS} units to create a period.
228 * If any other units are found then an exception is thrown.
229 *
230 * @param amount the temporal amount to convert, not null
231 * @return the equivalent period, not null
232 * @throws DateTimeException if unable to convert to a {@code Period}
233 * @throws ArithmeticException if the amount of years, months or days exceeds an int
234 */
235 public static Period from(TemporalAmount amount) {
236 Objects.requireNonNull(amount, "amount");
237 int years = 0;
238 int months = 0;
239 int days = 0;
240 for (TemporalUnit unit : amount.getUnits()) {
241 long unitAmount = amount.get(unit);
242 if (unit == ChronoUnit.YEARS) {
243 years = Math.toIntExact(unitAmount);
244 } else if (unit == ChronoUnit.MONTHS) {
245 months = Math.toIntExact(unitAmount);
246 } else if (unit == ChronoUnit.DAYS) {
247 days = Math.toIntExact(unitAmount);
248 } else {
249 throw new DateTimeException("Unit must be Years, Months or Days, but was " + unit);
250 }
251 }
252 return create(years, months, days);
253 }
254
255 //-----------------------------------------------------------------------
256 /**
257 * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
258 * <p>
259 * This will parse the string produced by {@code toString()} which is
260 * based on the ISO-8601 period format {@code PnYnMnD}.
261 * <p>
262 * The string starts with an optional sign, denoted by the ASCII negative
263 * or positive symbol. If negative, the whole period is negated.
264 * The ASCII letter "P" is next in upper or lower case.
265 * There are then three sections, each consisting of a number and a suffix.
266 * At least one of the three sections must be present.
267 * The sections have suffixes in ASCII of "Y", "M" and "D" for
268 * years, months and days, accepted in upper or lower case.
269 * The suffixes must occur in order.
270 * The number part of each section must consist of ASCII digits.
271 * The number may be prefixed by the ASCII negative or positive symbol.
272 * The number must parse to an {@code int}.
296 }
297 }
298 }
299 throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
300 }
301
302 private static int parseNumber(CharSequence text, String str, int negate) {
303 if (str == null) {
304 return 0;
305 }
306 int val = Integer.parseInt(str);
307 try {
308 return Math.multiplyExact(val, negate);
309 } catch (ArithmeticException ex) {
310 throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
311 }
312 }
313
314 //-----------------------------------------------------------------------
315 /**
316 * Obtains a {@code Period} consisting of the number of years, months,
317 * and days between two dates.
318 * <p>
319 * The start date is included, but the end date is not.
320 * The period is calculated by removing complete months, then calculating
321 * the remaining number of days, adjusting to ensure that both have the same sign.
322 * The number of months is then split into years and months based on a 12 month year.
323 * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
324 * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
325 * <p>
326 * The result of this method can be a negative period if the end is before the start.
327 * The negative sign will be the same in each of year, month and day.
328 *
329 * @param startDate the start date, inclusive, not null
330 * @param endDate the end date, exclusive, not null
331 * @return the period between this date and the end date, not null
332 * @see ChronoLocalDate#periodUntil(ChronoLocalDate)
333 */
334 public static Period between(LocalDate startDate, LocalDate endDate) {
335 return startDate.periodUntil(endDate);
336 }
337
338 //-----------------------------------------------------------------------
339 /**
340 * Creates an instance.
341 *
342 * @param years the amount
343 * @param months the amount
344 * @param days the amount
345 */
346 private static Period create(int years, int months, int days) {
347 if ((years | months | days) == 0) {
348 return ZERO;
349 }
350 return new Period(years, months, days);
351 }
352
353 /**
354 * Constructor.
355 *
356 * @param years the amount
357 * @param months the amount
358 * @param days the amount
359 */
360 private Period(int years, int months, int days) {
361 this.years = years;
362 this.months = months;
363 this.days = days;
364 }
365
366 //-----------------------------------------------------------------------
367 /**
368 * Gets the value of the requested unit.
369 * <p>
370 * This returns a value for each of the three supported units,
371 * {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and
372 * {@link ChronoUnit#DAYS DAYS}.
373 * All other units throw an exception.
374 *
375 * @param unit the {@code TemporalUnit} for which to return the value
376 * @return the long value of the unit
377 * @throws DateTimeException if the unit is not supported
378 * @throws UnsupportedTemporalTypeException if the unit is not supported
379 */
380 @Override
381 public long get(TemporalUnit unit) {
382 if (unit == ChronoUnit.YEARS) {
383 return getYears();
384 } else if (unit == ChronoUnit.MONTHS) {
385 return getMonths();
386 } else if (unit == ChronoUnit.DAYS) {
387 return getDays();
388 } else {
389 throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
390 }
391 }
392
393 /**
394 * Gets the set of units supported by this period.
395 * <p>
396 * The supported units are {@link ChronoUnit#YEARS YEARS},
397 * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
398 * They are returned in the order years, months, days.
399 * <p>
400 * This set can be used in conjunction with {@link #get(TemporalUnit)}
401 * to access the entire state of the period.
402 *
403 * @return a list containing the years, months and days units, not null
404 */
405 @Override
406 public List<TemporalUnit> getUnits() {
407 return SUPPORTED_UNITS;
408 }
409
522 * <p>
523 * This sets the amount of the days unit in a copy of this period.
524 * The years and months units are unaffected.
525 * <p>
526 * This instance is immutable and unaffected by this method call.
527 *
528 * @param days the days to represent, may be negative
529 * @return a {@code Period} based on this period with the requested days, not null
530 */
531 public Period withDays(int days) {
532 if (days == this.days) {
533 return this;
534 }
535 return create(years, months, days);
536 }
537
538 //-----------------------------------------------------------------------
539 /**
540 * Returns a copy of this period with the specified period added.
541 * <p>
542 * This operates separately on the years, months and days.
543 * <p>
544 * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"
545 * returns "3 years, 8 months and 5 days".
546 * <p>
547 * This instance is immutable and unaffected by this method call.
548 *
549 * @param amountToAdd the period to add, not null
550 * @return a {@code Period} based on this period with the requested period added, not null
551 * @throws ArithmeticException if numeric overflow occurs
552 */
553 public Period plus(Period amountToAdd) {
554 return create(
555 Math.addExact(years, amountToAdd.years),
556 Math.addExact(months, amountToAdd.months),
557 Math.addExact(days, amountToAdd.days));
558 }
559
560 /**
561 * Returns a copy of this period with the specified years added.
562 * <p>
604 * The years and months units are unaffected.
605 * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
606 * <p>
607 * This instance is immutable and unaffected by this method call.
608 *
609 * @param daysToAdd the days to add, positive or negative
610 * @return a {@code Period} based on this period with the specified days added, not null
611 * @throws ArithmeticException if numeric overflow occurs
612 */
613 public Period plusDays(long daysToAdd) {
614 if (daysToAdd == 0) {
615 return this;
616 }
617 return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
618 }
619
620 //-----------------------------------------------------------------------
621 /**
622 * Returns a copy of this period with the specified period subtracted.
623 * <p>
624 * This operates separately on the years, months and days.
625 * <p>
626 * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"
627 * returns "-1 years, 4 months and 1 day".
628 * <p>
629 * This instance is immutable and unaffected by this method call.
630 *
631 * @param amountToSubtract the period to subtract, not null
632 * @return a {@code Period} based on this period with the requested period subtracted, not null
633 * @throws ArithmeticException if numeric overflow occurs
634 */
635 public Period minus(Period amountToSubtract) {
636 return create(
637 Math.subtractExact(years, amountToSubtract.years),
638 Math.subtractExact(months, amountToSubtract.months),
639 Math.subtractExact(days, amountToSubtract.days));
640 }
641
642 /**
643 * Returns a copy of this period with the specified years subtracted.
644 * <p>
866 temporal = temporal.minus(years, YEARS);
867 }
868 if (months != 0) {
869 temporal = temporal.minus(months, MONTHS);
870 }
871 }
872 }
873 if (days != 0) {
874 temporal = temporal.minus(days, DAYS);
875 }
876 return temporal;
877 }
878
879 /**
880 * Calculates the range of months based on the temporal.
881 *
882 * @param temporal the temporal, not null
883 * @return the month range, negative if not fixed range
884 */
885 private long monthRange(Temporal temporal) {
886 if (temporal.isSupported(MONTH_OF_YEAR)) {
887 ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR);
888 if (startRange.isFixed() && startRange.isIntValue()) {
889 return startRange.getMaximum() - startRange.getMinimum() + 1;
890 }
891 }
892 return -1;
893 }
894
895 //-----------------------------------------------------------------------
896 /**
897 * Checks if this period is equal to another period.
898 * <p>
899 * The comparison is based on the amounts held in the period.
900 * To be equal, the years, months and days units must be individually equal.
901 * Note that this means that a period of "15 Months" is not equal to a period
902 * of "1 Year and 3 Months".
903 *
904 * @param obj the object to check, null returns false
905 * @return true if this is equal to the other period
906 */
907 @Override
908 public boolean equals(Object obj) {
909 if (this == obj) {
910 return true;
911 }
|