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