• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *  * Redistributions of source code must retain the above copyright notice,
10  *    this list of conditions and the following disclaimer.
11  *
12  *  * Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  *  * Neither the name of JSR-310 nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.threeten.bp;
33 
34 import static org.threeten.bp.temporal.ChronoUnit.DAYS;
35 import static org.threeten.bp.temporal.ChronoUnit.MONTHS;
36 import static org.threeten.bp.temporal.ChronoUnit.YEARS;
37 
38 import java.io.Serializable;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.List;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 
45 import org.threeten.bp.chrono.ChronoLocalDate;
46 import org.threeten.bp.chrono.ChronoPeriod;
47 import org.threeten.bp.chrono.Chronology;
48 import org.threeten.bp.chrono.IsoChronology;
49 import org.threeten.bp.format.DateTimeParseException;
50 import org.threeten.bp.jdk8.Jdk8Methods;
51 import org.threeten.bp.temporal.ChronoUnit;
52 import org.threeten.bp.temporal.Temporal;
53 import org.threeten.bp.temporal.TemporalAmount;
54 import org.threeten.bp.temporal.TemporalUnit;
55 import org.threeten.bp.temporal.UnsupportedTemporalTypeException;
56 
57 /**
58  * A date-based amount of time, such as '2 years, 3 months and 4 days'.
59  * <p>
60  * This class models a quantity or amount of time in terms of years, months and days.
61  * See {@link Duration} for the time-based equivalent to this class.
62  * <p>
63  * Durations and period differ in their treatment of daylight savings time
64  * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact
65  * number of seconds, thus a duration of one day is always exactly 24 hours.
66  * By contrast, a {@code Period} will add a conceptual day, trying to maintain
67  * the local time.
68  * <p>
69  * For example, consider adding a period of one day and a duration of one day to
70  * 18:00 on the evening before a daylight savings gap. The {@code Period} will add
71  * the conceptual day and result in a {@code ZonedDateTime} at 18:00 the following day.
72  * By contrast, the {@code Duration} will add exactly 24 hours, resulting in a
73  * {@code ZonedDateTime} at 19:00 the following day (assuming a one hour DST gap).
74  * <p>
75  * The supported units of a period are {@link ChronoUnit#YEARS YEARS},
76  * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
77  * All three fields are always present, but may be set to zero.
78  * <p>
79  * The period may be used with any calendar system.
80  * The meaning of a "year" or "month" is only applied when the object is added to a date.
81  * <p>
82  * The period is modeled as a directed amount of time, meaning that individual parts of the
83  * period may be negative.
84  * <p>
85  * The months and years fields may be {@linkplain #normalized() normalized}.
86  * The normalization assumes a 12 month year, so is not appropriate for all calendar systems.
87  *
88  * <h3>Specification for implementors</h3>
89  * This class is immutable and thread-safe.
90  */
91 public final class Period
92         extends ChronoPeriod
93         implements Serializable {
94 
95     /**
96      * A constant for a period of zero.
97      */
98     public static final Period ZERO = new Period(0, 0, 0);
99     /**
100      * Serialization version.
101      */
102     private static final long serialVersionUID = -8290556941213247973L;
103     /**
104      * The pattern for parsing.
105      */
106     private final static Pattern PATTERN =
107             Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE);
108 
109     /**
110      * The number of years.
111      */
112     private final int years;
113     /**
114      * The number of months.
115      */
116     private final int months;
117     /**
118      * The number of days.
119      */
120     private final int days;
121 
122     //-----------------------------------------------------------------------
123     /**
124      * Obtains a {@code Period} representing a number of years.
125      * <p>
126      * The resulting period will have the specified years.
127      * The months and days units will be zero.
128      *
129      * @param years  the number of years, positive or negative
130      * @return the period of years, not null
131      */
ofYears(int years)132     public static Period ofYears(int years) {
133         return create(years, 0, 0);
134     }
135 
136     /**
137      * Obtains a {@code Period} representing a number of months.
138      * <p>
139      * The resulting period will have the specified months.
140      * The years and days units will be zero.
141      *
142      * @param months  the number of months, positive or negative
143      * @return the period of months, not null
144      */
ofMonths(int months)145     public static Period ofMonths(int months) {
146         return create(0, months, 0);
147     }
148 
149     /**
150      * Obtains a {@code Period} representing a number of weeks.
151      * <p>
152      * The resulting period will have days equal to the weeks multiplied by seven.
153      * The years and months units will be zero.
154      *
155      * @param weeks  the number of weeks, positive or negative
156      * @return the period of days, not null
157      */
ofWeeks(int weeks)158     public static Period ofWeeks(int weeks) {
159         return create(0, 0, Jdk8Methods.safeMultiply(weeks, 7));
160     }
161 
162     /**
163      * Obtains a {@code Period} representing a number of days.
164      * <p>
165      * The resulting period will have the specified days.
166      * The years and months units will be zero.
167      *
168      * @param days  the number of days, positive or negative
169      * @return the period of days, not null
170      */
ofDays(int days)171     public static Period ofDays(int days) {
172         return create(0, 0, days);
173     }
174 
175     //-----------------------------------------------------------------------
176     /**
177      * Obtains a {@code Period} representing a number of years, months and days.
178      * <p>
179      * This creates an instance based on years, months and days.
180      *
181      * @param years  the amount of years, may be negative
182      * @param months  the amount of months, may be negative
183      * @param days  the amount of days, may be negative
184      * @return the period of years, months and days, not null
185      */
of(int years, int months, int days)186     public static Period of(int years, int months, int days) {
187         return create(years, months, days);
188     }
189 
190     //-----------------------------------------------------------------------
191     /**
192      * Obtains an instance of {@code Period} from a temporal amount.
193      * <p>
194      * This obtains a period based on the specified amount.
195      * A {@code TemporalAmount} represents an  amount of time, which may be
196      * date-based or time-based, which this factory extracts to a {@code Period}.
197      * <p>
198      * The conversion loops around the set of units from the amount and uses
199      * the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS}
200      * and {@link ChronoUnit#DAYS DAYS} units to create a period.
201      * If any other units are found then an exception is thrown.
202      * <p>
203      * If the amount is a {@code ChronoPeriod} then it must use the ISO chronology.
204      *
205      * @param amount  the temporal amount to convert, not null
206      * @return the equivalent period, not null
207      * @throws DateTimeException if unable to convert to a {@code Period}
208      * @throws ArithmeticException if the amount of years, months or days exceeds an int
209      */
from(TemporalAmount amount)210     public static Period from(TemporalAmount amount) {
211         if (amount instanceof Period) {
212             return (Period) amount;
213         }
214         if (amount instanceof ChronoPeriod) {
215             if (IsoChronology.INSTANCE.equals(((ChronoPeriod) amount).getChronology()) == false) {
216                 throw new DateTimeException("Period requires ISO chronology: " + amount);
217             }
218         }
219         Jdk8Methods.requireNonNull(amount, "amount");
220         int years = 0;
221         int months = 0;
222         int days = 0;
223         for (TemporalUnit unit : amount.getUnits()) {
224             long unitAmount = amount.get(unit);
225             if (unit == ChronoUnit.YEARS) {
226                 years = Jdk8Methods.safeToInt(unitAmount);
227             } else if (unit == ChronoUnit.MONTHS) {
228                 months = Jdk8Methods.safeToInt(unitAmount);
229             } else if (unit == ChronoUnit.DAYS) {
230                 days = Jdk8Methods.safeToInt(unitAmount);
231             } else {
232                 throw new DateTimeException("Unit must be Years, Months or Days, but was " + unit);
233             }
234         }
235         return create(years, months, days);
236     }
237 
238     //-----------------------------------------------------------------------
239     /**
240      * Obtains a {@code Period} consisting of the number of years, months,
241      * and days between two dates.
242      * <p>
243      * The start date is included, but the end date is not.
244      * The period is calculated by removing complete months, then calculating
245      * the remaining number of days, adjusting to ensure that both have the same sign.
246      * The number of months is then split into years and months based on a 12 month year.
247      * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
248      * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
249      * <p>
250      * The result of this method can be a negative period if the end is before the start.
251      * The negative sign will be the same in each of year, month and day.
252      *
253      * @param startDate  the start date, inclusive, not null
254      * @param endDate  the end date, exclusive, not null
255      * @return the period between this date and the end date, not null
256      * @see ChronoLocalDate#until(ChronoLocalDate)
257      */
between(LocalDate startDate, LocalDate endDate)258     public static Period between(LocalDate startDate, LocalDate endDate) {
259         return startDate.until(endDate);
260     }
261 
262     //-----------------------------------------------------------------------
263     /**
264      * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
265      * <p>
266      * This will parse the string produced by {@code toString()} which is
267      * based on the ISO-8601 period formats {@code PnYnMnD} and {@code PnW}.
268      * <p>
269      * The string starts with an optional sign, denoted by the ASCII negative
270      * or positive symbol. If negative, the whole period is negated.
271      * The ASCII letter "P" is next in upper or lower case.
272      * There are then four sections, each consisting of a number and a suffix.
273      * At least one of the four sections must be present.
274      * The sections have suffixes in ASCII of "Y", "M", "W" and "D" for
275      * years, months, weeks and days, accepted in upper or lower case.
276      * The suffixes must occur in order.
277      * The number part of each section must consist of ASCII digits.
278      * The number may be prefixed by the ASCII negative or positive symbol.
279      * The number must parse to an {@code int}.
280      * <p>
281      * The leading plus/minus sign, and negative values for other units are
282      * not part of the ISO-8601 standard. In addition, ISO-8601 does not
283      * permit mixing between the {@code PnYnMnD} and {@code PnW} formats.
284      * Any week-based input is multiplied by 7 and treated as a number of days.
285      * <p>
286      * For example, the following are valid inputs:
287      * <pre>
288      *   "P2Y"             -- Period.ofYears(2)
289      *   "P3M"             -- Period.ofMonths(3)
290      *   "P4W"             -- Period.ofWeeks(4)
291      *   "P5D"             -- Period.ofDays(5)
292      *   "P1Y2M3D"         -- Period.of(1, 2, 3)
293      *   "P1Y2M3W4D"       -- Period.of(1, 2, 25)
294      *   "P-1Y2M"          -- Period.of(-1, 2, 0)
295      *   "-P1Y2M"          -- Period.of(-1, -2, 0)
296      * </pre>
297      *
298      * @param text  the text to parse, not null
299      * @return the parsed period, not null
300      * @throws DateTimeParseException if the text cannot be parsed to a period
301      */
parse(CharSequence text)302     public static Period parse(CharSequence text) {
303         Jdk8Methods.requireNonNull(text, "text");
304         Matcher matcher = PATTERN.matcher(text);
305         if (matcher.matches()) {
306             int negate = ("-".equals(matcher.group(1)) ? -1 : 1);
307             String yearMatch = matcher.group(2);
308             String monthMatch = matcher.group(3);
309             String weekMatch = matcher.group(4);
310             String dayMatch = matcher.group(5);
311             if (yearMatch != null || monthMatch != null || weekMatch != null || dayMatch != null) {
312                 try {
313                     int years = parseNumber(text, yearMatch, negate);
314                     int months = parseNumber(text, monthMatch, negate);
315                     int weeks = parseNumber(text, weekMatch, negate);
316                     int days = parseNumber(text, dayMatch, negate);
317                     days = Jdk8Methods.safeAdd(days, Jdk8Methods.safeMultiply(weeks, 7));
318                     return create(years, months, days);
319                 } catch (NumberFormatException ex) {
320                     throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
321                 }
322             }
323         }
324         throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
325     }
326 
parseNumber(CharSequence text, String str, int negate)327     private static int parseNumber(CharSequence text, String str, int negate) {
328         if (str == null) {
329             return 0;
330         }
331         int val = Integer.parseInt(str);
332         try {
333             return Jdk8Methods.safeMultiply(val, negate);
334         } catch (ArithmeticException ex) {
335             throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
336         }
337     }
338 
339     //-----------------------------------------------------------------------
340     /**
341      * Creates an instance.
342      *
343      * @param years  the amount
344      * @param months  the amount
345      * @param days  the amount
346      */
create(int years, int months, int days)347     private static Period create(int years, int months, int days) {
348         if ((years | months | days) == 0) {
349             return ZERO;
350         }
351         return new Period(years, months, days);
352     }
353 
354     /**
355      * Constructor.
356      *
357      * @param years  the amount
358      * @param months  the amount
359      * @param days  the amount
360      */
Period(int years, int months, int days)361     private Period(int years, int months, int days) {
362         this.years = years;
363         this.months = months;
364         this.days = days;
365     }
366 
367     /**
368      * Resolves singletons.
369      *
370      * @return the resolved instance
371      */
readResolve()372     private Object readResolve() {
373         if ((years | months | days) == 0) {
374             return ZERO;
375         }
376         return this;
377     }
378 
379     //-----------------------------------------------------------------------
380     @Override
getUnits()381     public List<TemporalUnit> getUnits() {
382         return Collections.<TemporalUnit>unmodifiableList(Arrays.asList(YEARS, MONTHS, DAYS));
383     }
384 
385     @Override
getChronology()386     public Chronology getChronology() {
387         return IsoChronology.INSTANCE;
388     }
389 
390     @Override
get(TemporalUnit unit)391     public long get(TemporalUnit unit) {
392         if (unit == YEARS) {
393             return years;
394         }
395         if (unit == MONTHS) {
396             return months;
397         }
398         if (unit == DAYS) {
399             return days;
400         }
401         throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);
402     }
403 
404     //-----------------------------------------------------------------------
405     /**
406      * Checks if all three units of this period are zero.
407      * <p>
408      * A zero period has the value zero for the years, months and days units.
409      *
410      * @return true if this period is zero-length
411      */
isZero()412     public boolean isZero() {
413         return (this == ZERO);
414     }
415 
416     /**
417      * Checks if any of the three units of this period are negative.
418      * <p>
419      * This checks whether the years, months or days units are less than zero.
420      *
421      * @return true if any unit of this period is negative
422      */
isNegative()423     public boolean isNegative() {
424         return years < 0 || months < 0 || days < 0;
425     }
426 
427     //-----------------------------------------------------------------------
428     /**
429      * Gets the amount of years of this period.
430      * <p>
431      * This returns the years unit.
432      * <p>
433      * The months unit is not normalized with the years unit.
434      * This means that a period of "15 months" is different to a period
435      * of "1 year and 3 months".
436      *
437      * @return the amount of years of this period, may be negative
438      */
getYears()439     public int getYears() {
440         return years;
441     }
442 
443     /**
444      * Gets the amount of months of this period.
445      * <p>
446      * This returns the months unit.
447      * <p>
448      * The months unit is not normalized with the years unit.
449      * This means that a period of "15 months" is different to a period
450      * of "1 year and 3 months".
451      *
452      * @return the amount of months of this period, may be negative
453      */
getMonths()454     public int getMonths() {
455         return months;
456     }
457 
458     /**
459      * Gets the amount of days of this period.
460      * <p>
461      * This returns the days unit.
462      *
463      * @return the amount of days of this period, may be negative
464      */
getDays()465     public int getDays() {
466         return days;
467     }
468 
469     //-----------------------------------------------------------------------
470     /**
471      * Returns a copy of this period with the specified amount of years.
472      * <p>
473      * This sets the amount of the years unit in a copy of this period.
474      * The months and days units are unaffected.
475      * <p>
476      * The months unit is not normalized with the years unit.
477      * This means that a period of "15 months" is different to a period
478      * of "1 year and 3 months".
479      * <p>
480      * This instance is immutable and unaffected by this method call.
481      *
482      * @param years  the years to represent, may be negative
483      * @return a {@code Period} based on this period with the requested years, not null
484      */
withYears(int years)485     public Period withYears(int years) {
486         if (years == this.years) {
487             return this;
488         }
489         return create(years, months, days);
490     }
491 
492     /**
493      * Returns a copy of this period with the specified amount of months.
494      * <p>
495      * This sets the amount of the months unit in a copy of this period.
496      * The years and days units are unaffected.
497      * <p>
498      * The months unit is not normalized with the years unit.
499      * This means that a period of "15 months" is different to a period
500      * of "1 year and 3 months".
501      * <p>
502      * This instance is immutable and unaffected by this method call.
503      *
504      * @param months  the months to represent, may be negative
505      * @return a {@code Period} based on this period with the requested months, not null
506      */
withMonths(int months)507     public Period withMonths(int months) {
508         if (months == this.months) {
509             return this;
510         }
511         return create(years, months, days);
512     }
513 
514     /**
515      * Returns a copy of this period with the specified amount of days.
516      * <p>
517      * This sets the amount of the days unit in a copy of this period.
518      * The years and months units are unaffected.
519      * <p>
520      * This instance is immutable and unaffected by this method call.
521      *
522      * @param days  the days to represent, may be negative
523      * @return a {@code Period} based on this period with the requested days, not null
524      */
withDays(int days)525     public Period withDays(int days) {
526         if (days == this.days) {
527             return this;
528         }
529         return create(years, months, days);
530     }
531 
532     //-----------------------------------------------------------------------
533     /**
534      * Returns a copy of this period with the specified amount added.
535      * <p>
536      * This input amount is converted to a {@code Period} using {@code from(TemporalAmount)}.
537      * This operates separately on the years, months and days.
538      * <p>
539      * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"
540      * returns "3 years, 8 months and 5 days".
541      * <p>
542      * This instance is immutable and unaffected by this method call.
543      *
544      * @param amountToAdd  the period to add, not null
545      * @return a {@code Period} based on this period with the requested period added, not null
546      * @throws ArithmeticException if numeric overflow occurs
547      */
plus(TemporalAmount amountToAdd)548     public Period plus(TemporalAmount amountToAdd) {
549         Period amount = Period.from(amountToAdd);
550         return create(
551                 Jdk8Methods.safeAdd(years, amount.years),
552                 Jdk8Methods.safeAdd(months, amount.months),
553                 Jdk8Methods.safeAdd(days, amount.days));
554     }
555 
556     /**
557      * Returns a copy of this period with the specified years added.
558      * <p>
559      * This adds the amount to the years unit in a copy of this period.
560      * The months and days units are unaffected.
561      * For example, "1 year, 6 months and 3 days" plus 2 years returns "3 years, 6 months and 3 days".
562      * <p>
563      * This instance is immutable and unaffected by this method call.
564      *
565      * @param yearsToAdd  the years to add, positive or negative
566      * @return a {@code Period} based on this period with the specified years added, not null
567      * @throws ArithmeticException if numeric overflow occurs
568      */
plusYears(long yearsToAdd)569     public Period plusYears(long yearsToAdd) {
570         if (yearsToAdd == 0) {
571             return this;
572         }
573         return create(Jdk8Methods.safeToInt(Jdk8Methods.safeAdd(years, yearsToAdd)), months, days);
574     }
575 
576     /**
577      * Returns a copy of this period with the specified months added.
578      * <p>
579      * This adds the amount to the months unit in a copy of this period.
580      * The years and days units are unaffected.
581      * For example, "1 year, 6 months and 3 days" plus 2 months returns "1 year, 8 months and 3 days".
582      * <p>
583      * This instance is immutable and unaffected by this method call.
584      *
585      * @param monthsToAdd  the months to add, positive or negative
586      * @return a {@code Period} based on this period with the specified months added, not null
587      * @throws ArithmeticException if numeric overflow occurs
588      */
plusMonths(long monthsToAdd)589     public Period plusMonths(long monthsToAdd) {
590         if (monthsToAdd == 0) {
591             return this;
592         }
593         return create(years, Jdk8Methods.safeToInt(Jdk8Methods.safeAdd(months, monthsToAdd)), days);
594     }
595 
596     /**
597      * Returns a copy of this period with the specified days added.
598      * <p>
599      * This adds the amount to the days unit in a copy of this period.
600      * The years and months units are unaffected.
601      * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
602      * <p>
603      * This instance is immutable and unaffected by this method call.
604      *
605      * @param daysToAdd  the days to add, positive or negative
606      * @return a {@code Period} based on this period with the specified days added, not null
607      * @throws ArithmeticException if numeric overflow occurs
608      */
plusDays(long daysToAdd)609     public Period plusDays(long daysToAdd) {
610         if (daysToAdd == 0) {
611             return this;
612         }
613         return create(years, months, Jdk8Methods.safeToInt(Jdk8Methods.safeAdd(days, daysToAdd)));
614     }
615 
616     //-----------------------------------------------------------------------
617     /**
618      * Returns a copy of this period with the specified amount subtracted.
619      * <p>
620      * This input amount is converted to a {@code Period} using {@code from(TemporalAmount)}.
621      * This operates separately on the years, months and days.
622      * <p>
623      * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"
624      * returns "-1 years, 4 months and 1 day".
625      * <p>
626      * This instance is immutable and unaffected by this method call.
627      *
628      * @param amountToSubtract  the period to subtract, not null
629      * @return a {@code Period} based on this period with the requested period subtracted, not null
630      * @throws ArithmeticException if numeric overflow occurs
631      */
minus(TemporalAmount amountToSubtract)632     public Period minus(TemporalAmount amountToSubtract) {
633         Period amount = Period.from(amountToSubtract);
634         return create(
635                 Jdk8Methods.safeSubtract(years, amount.years),
636                 Jdk8Methods.safeSubtract(months, amount.months),
637                 Jdk8Methods.safeSubtract(days, amount.days));
638     }
639 
640     /**
641      * Returns a copy of this period with the specified years subtracted.
642      * <p>
643      * This subtracts the amount from the years unit in a copy of this period.
644      * The months and days units are unaffected.
645      * For example, "1 year, 6 months and 3 days" minus 2 years returns "-1 years, 6 months and 3 days".
646      * <p>
647      * This instance is immutable and unaffected by this method call.
648      *
649      * @param yearsToSubtract  the years to subtract, positive or negative
650      * @return a {@code Period} based on this period with the specified years subtracted, not null
651      * @throws ArithmeticException if numeric overflow occurs
652      */
minusYears(long yearsToSubtract)653     public Period minusYears(long yearsToSubtract) {
654         return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));
655     }
656 
657     /**
658      * Returns a copy of this period with the specified months subtracted.
659      * <p>
660      * This subtracts the amount from the months unit in a copy of this period.
661      * The years and days units are unaffected.
662      * For example, "1 year, 6 months and 3 days" minus 2 months returns "1 year, 4 months and 3 days".
663      * <p>
664      * This instance is immutable and unaffected by this method call.
665      *
666      * @param monthsToSubtract  the years to subtract, positive or negative
667      * @return a {@code Period} based on this period with the specified months subtracted, not null
668      * @throws ArithmeticException if numeric overflow occurs
669      */
minusMonths(long monthsToSubtract)670     public Period minusMonths(long monthsToSubtract) {
671         return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));
672     }
673 
674     /**
675      * Returns a copy of this period with the specified days subtracted.
676      * <p>
677      * This subtracts the amount from the days unit in a copy of this period.
678      * The years and months units are unaffected.
679      * For example, "1 year, 6 months and 3 days" minus 2 days returns "1 year, 6 months and 1 day".
680      * <p>
681      * This instance is immutable and unaffected by this method call.
682      *
683      * @param daysToSubtract  the months to subtract, positive or negative
684      * @return a {@code Period} based on this period with the specified days subtracted, not null
685      * @throws ArithmeticException if numeric overflow occurs
686      */
minusDays(long daysToSubtract)687     public Period minusDays(long daysToSubtract) {
688         return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
689     }
690 
691     //-----------------------------------------------------------------------
692     /**
693      * Returns a new instance with each element in this period multiplied
694      * by the specified scalar.
695      * <p>
696      * This simply multiplies each field, years, months, days and normalized time,
697      * by the scalar. No normalization is performed.
698      *
699      * @param scalar  the scalar to multiply by, not null
700      * @return a {@code Period} based on this period with the amounts multiplied by the scalar, not null
701      * @throws ArithmeticException if numeric overflow occurs
702      */
multipliedBy(int scalar)703     public Period multipliedBy(int scalar) {
704         if (this == ZERO || scalar == 1) {
705             return this;
706         }
707         return create(
708                 Jdk8Methods.safeMultiply(years, scalar),
709                 Jdk8Methods.safeMultiply(months, scalar),
710                 Jdk8Methods.safeMultiply(days, scalar));
711     }
712 
713     /**
714      * Returns a new instance with each amount in this period negated.
715      *
716      * @return a {@code Period} based on this period with the amounts negated, not null
717      * @throws ArithmeticException if numeric overflow occurs
718      */
negated()719     public Period negated() {
720         return multipliedBy(-1);
721     }
722 
723     //-----------------------------------------------------------------------
724     /**
725      * Returns a copy of this period with the years and months normalized
726      * using a 12 month year.
727      * <p>
728      * This normalizes the years and months units, leaving the days unit unchanged.
729      * The months unit is adjusted to have an absolute value less than 11,
730      * with the years unit being adjusted to compensate. For example, a period of
731      * "1 Year and 15 months" will be normalized to "2 years and 3 months".
732      * <p>
733      * The sign of the years and months units will be the same after normalization.
734      * For example, a period of "1 year and -25 months" will be normalized to
735      * "-1 year and -1 month".
736      * <p>
737      * This normalization uses a 12 month year which is not valid for all calendar systems.
738      * <p>
739      * This instance is immutable and unaffected by this method call.
740      *
741      * @return a {@code Period} based on this period with excess months normalized to years, not null
742      * @throws ArithmeticException if numeric overflow occurs
743      */
normalized()744     public Period normalized() {
745         long totalMonths = toTotalMonths();
746         long splitYears = totalMonths / 12;
747         int splitMonths = (int) (totalMonths % 12);  // no overflow
748         if (splitYears == years && splitMonths == months) {
749             return this;
750         }
751         return create(Jdk8Methods.safeToInt(splitYears), splitMonths, days);
752     }
753 
754     /**
755      * Gets the total number of months in this period using a 12 month year.
756      * <p>
757      * This returns the total number of months in the period by multiplying the
758      * number of years by 12 and adding the number of months.
759      * <p>
760      * This uses a 12 month year which is not valid for all calendar systems.
761      * <p>
762      * This instance is immutable and unaffected by this method call.
763      *
764      * @return the total number of months in the period, may be negative
765      */
toTotalMonths()766     public long toTotalMonths() {
767         return years * 12L + months;  // no overflow
768     }
769 
770     //-------------------------------------------------------------------------
771     /**
772      * Adds this period to the specified temporal object.
773      * <p>
774      * This returns a temporal object of the same observable type as the input
775      * with this period added.
776      * <p>
777      * In most cases, it is clearer to reverse the calling pattern by using
778      * {@link Temporal#plus(TemporalAmount)}.
779      * <pre>
780      *   // these two lines are equivalent, but the second approach is recommended
781      *   dateTime = thisPeriod.addTo(dateTime);
782      *   dateTime = dateTime.plus(thisPeriod);
783      * </pre>
784      * <p>
785      * The calculation will add the years, then months, then days.
786      * Only non-zero amounts will be added.
787      * If the date-time has a calendar system with a fixed number of months in a
788      * year, then the years and months will be combined before being added.
789      * <p>
790      * This instance is immutable and unaffected by this method call.
791      *
792      * @param temporal  the temporal object to adjust, not null
793      * @return an object of the same type with the adjustment made, not null
794      * @throws DateTimeException if unable to add
795      * @throws ArithmeticException if numeric overflow occurs
796      */
797     @Override
addTo(Temporal temporal)798     public Temporal addTo(Temporal temporal) {
799         Jdk8Methods.requireNonNull(temporal, "temporal");
800         if (years != 0) {
801             if (months != 0) {
802                 temporal = temporal.plus(toTotalMonths(), MONTHS);
803             } else {
804                 temporal = temporal.plus(years, YEARS);
805             }
806         } else if (months != 0) {
807             temporal = temporal.plus(months, MONTHS);
808         }
809         if (days != 0) {
810             temporal = temporal.plus(days, DAYS);
811         }
812         return temporal;
813     }
814 
815     /**
816      * Subtracts this period from the specified temporal object.
817      * <p>
818      * This returns a temporal object of the same observable type as the input
819      * with this period subtracted.
820      * <p>
821      * In most cases, it is clearer to reverse the calling pattern by using
822      * {@link Temporal#minus(TemporalAmount)}.
823      * <pre>
824      *   // these two lines are equivalent, but the second approach is recommended
825      *   dateTime = thisPeriod.subtractFrom(dateTime);
826      *   dateTime = dateTime.minus(thisPeriod);
827      * </pre>
828      * <p>
829      * The calculation operates as follows.
830      * First, the chronology of the temporal is checked to ensure it is ISO chronology or null.
831      * Second, if the months are zero, the years are added if non-zero, otherwise
832      * the combination of years and months is added if non-zero.
833      * Finally, any days are added.
834      *
835      * The calculation will subtract the years, then months, then days.
836      * Only non-zero amounts will be subtracted.
837      * If the date-time has a calendar system with a fixed number of months in a
838      * year, then the years and months will be combined before being subtracted.
839      * <p>
840      * This instance is immutable and unaffected by this method call.
841      *
842      * @param temporal  the temporal object to adjust, not null
843      * @return an object of the same type with the adjustment made, not null
844      * @throws DateTimeException if unable to subtract
845      * @throws ArithmeticException if numeric overflow occurs
846      */
847     @Override
subtractFrom(Temporal temporal)848     public Temporal subtractFrom(Temporal temporal) {
849         Jdk8Methods.requireNonNull(temporal, "temporal");
850         if (years != 0) {
851             if (months != 0) {
852                 temporal = temporal.minus(toTotalMonths(), MONTHS);
853             } else {
854                 temporal = temporal.minus(years, YEARS);
855             }
856         } else if (months != 0) {
857             temporal = temporal.minus(months, MONTHS);
858         }
859         if (days != 0) {
860             temporal = temporal.minus(days, DAYS);
861         }
862         return temporal;
863     }
864 
865     //-----------------------------------------------------------------------
866     /**
867      * Checks if this period is equal to another period.
868      * <p>
869      * The comparison is based on the amounts held in the period.
870      * To be equal, the years, months and days units must be individually equal.
871      * Note that this means that a period of "15 Months" is not equal to a period
872      * of "1 Year and 3 Months".
873      *
874      * @param obj  the object to check, null returns false
875      * @return true if this is equal to the other period
876      */
877     @Override
equals(Object obj)878     public boolean equals(Object obj) {
879         if (this == obj) {
880             return true;
881         }
882         if (obj instanceof Period) {
883             Period other = (Period) obj;
884             return years == other.years &&
885                     months == other.months &&
886                     days == other.days;
887         }
888         return false;
889     }
890 
891     /**
892      * A hash code for this period.
893      *
894      * @return a suitable hash code
895      */
896     @Override
hashCode()897     public int hashCode() {
898         return years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16);
899     }
900 
901     //-----------------------------------------------------------------------
902     /**
903      * Outputs this period as a {@code String}, such as {@code P6Y3M1D}.
904      * <p>
905      * The output will be in the ISO-8601 period format.
906      * A zero period will be represented as zero days, 'P0D'.
907      *
908      * @return a string representation of this period, not null
909      */
910     @Override
toString()911     public String toString() {
912         if (this == ZERO) {
913             return "P0D";
914         } else {
915             StringBuilder buf = new StringBuilder();
916             buf.append('P');
917             if (years != 0) {
918                 buf.append(years).append('Y');
919             }
920             if (months != 0) {
921                 buf.append(months).append('M');
922             }
923             if (days != 0) {
924                 buf.append(days).append('D');
925             }
926             return buf.toString();
927         }
928     }
929 
930 }
931