Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/time/Period.java
38829 views
/*1* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file is available under and governed by the GNU General Public27* License version 2 only, as published by the Free Software Foundation.28* However, the following notice accompanied the original version of this29* file:30*31* Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos32*33* All rights reserved.34*35* Redistribution and use in source and binary forms, with or without36* modification, are permitted provided that the following conditions are met:37*38* * Redistributions of source code must retain the above copyright notice,39* this list of conditions and the following disclaimer.40*41* * Redistributions in binary form must reproduce the above copyright notice,42* this list of conditions and the following disclaimer in the documentation43* and/or other materials provided with the distribution.44*45* * Neither the name of JSR-310 nor the names of its contributors46* may be used to endorse or promote products derived from this software47* without specific prior written permission.48*49* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS50* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT51* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR52* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR53* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,54* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,55* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR56* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF57* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING58* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS59* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.60*/61package java.time;6263import static java.time.temporal.ChronoUnit.DAYS;64import static java.time.temporal.ChronoUnit.MONTHS;65import static java.time.temporal.ChronoUnit.YEARS;6667import java.io.DataInput;68import java.io.DataOutput;69import java.io.IOException;70import java.io.InvalidObjectException;71import java.io.ObjectInputStream;72import java.io.Serializable;73import java.time.chrono.ChronoLocalDate;74import java.time.chrono.ChronoPeriod;75import java.time.chrono.Chronology;76import java.time.chrono.IsoChronology;77import java.time.format.DateTimeParseException;78import java.time.temporal.ChronoUnit;79import java.time.temporal.Temporal;80import java.time.temporal.TemporalAccessor;81import java.time.temporal.TemporalAmount;82import java.time.temporal.TemporalQueries;83import java.time.temporal.TemporalUnit;84import java.time.temporal.UnsupportedTemporalTypeException;85import java.util.Arrays;86import java.util.Collections;87import java.util.List;88import java.util.Objects;89import java.util.regex.Matcher;90import java.util.regex.Pattern;9192/**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 time100* when added to {@link ZonedDateTime}. A {@code Duration} will add an exact101* 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 maintain103* the local time.104* <p>105* For example, consider adding a period of one day and a duration of one day to106* 18:00 on the evening before a daylight savings gap. The {@code Period} will add107* 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 a109* {@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 today116* in most of the world. It is equivalent to the proleptic Gregorian calendar117* 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 the120* period may be negative.121*122* <p>123* This is a <a href="{@docRoot}/java/lang/doc-files/ValueBased.html">value-based</a>124* class; use of identity-sensitive operations (including reference equality125* ({@code ==}), identity hash code, or synchronization) on instances of126* {@code Period} may have unpredictable results and should be avoided.127* The {@code equals} method should be used for comparisons.128*129* @implSpec130* This class is immutable and thread-safe.131*132* @since 1.8133*/134public final class Period135implements ChronoPeriod, Serializable {136137/**138* A constant for a period of zero.139*/140public static final Period ZERO = new Period(0, 0, 0);141/**142* Serialization version.143*/144private static final long serialVersionUID = -3587258372562876L;145/**146* The pattern for parsing.147*/148private static final Pattern PATTERN =149Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)Y)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)W)?(?:([-+]?[0-9]+)D)?", Pattern.CASE_INSENSITIVE);150151/**152* The set of supported units.153*/154private static final List<TemporalUnit> SUPPORTED_UNITS =155Collections.unmodifiableList(Arrays.<TemporalUnit>asList(YEARS, MONTHS, DAYS));156157/**158* The number of years.159*/160private final int years;161/**162* The number of months.163*/164private final int months;165/**166* The number of days.167*/168private final int days;169170//-----------------------------------------------------------------------171/**172* Obtains a {@code Period} representing a number of years.173* <p>174* The resulting period will have the specified years.175* The months and days units will be zero.176*177* @param years the number of years, positive or negative178* @return the period of years, not null179*/180public static Period ofYears(int years) {181return create(years, 0, 0);182}183184/**185* Obtains a {@code Period} representing a number of months.186* <p>187* The resulting period will have the specified months.188* The years and days units will be zero.189*190* @param months the number of months, positive or negative191* @return the period of months, not null192*/193public static Period ofMonths(int months) {194return create(0, months, 0);195}196197/**198* Obtains a {@code Period} representing a number of weeks.199* <p>200* The resulting period will be day-based, with the amount of days201* equal to the number of weeks multiplied by 7.202* The years and months units will be zero.203*204* @param weeks the number of weeks, positive or negative205* @return the period, with the input weeks converted to days, not null206*/207public static Period ofWeeks(int weeks) {208return create(0, 0, Math.multiplyExact(weeks, 7));209}210211/**212* Obtains a {@code Period} representing a number of days.213* <p>214* The resulting period will have the specified days.215* The years and months units will be zero.216*217* @param days the number of days, positive or negative218* @return the period of days, not null219*/220public static Period ofDays(int days) {221return create(0, 0, days);222}223224//-----------------------------------------------------------------------225/**226* Obtains a {@code Period} representing a number of years, months and days.227* <p>228* This creates an instance based on years, months and days.229*230* @param years the amount of years, may be negative231* @param months the amount of months, may be negative232* @param days the amount of days, may be negative233* @return the period of years, months and days, not null234*/235public static Period of(int years, int months, int days) {236return create(years, months, days);237}238239//-----------------------------------------------------------------------240/**241* Obtains an instance of {@code Period} from a temporal amount.242* <p>243* This obtains a period based on the specified amount.244* A {@code TemporalAmount} represents an amount of time, which may be245* date-based or time-based, which this factory extracts to a {@code Period}.246* <p>247* The conversion loops around the set of units from the amount and uses248* the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS}249* and {@link ChronoUnit#DAYS DAYS} units to create a period.250* If any other units are found then an exception is thrown.251* <p>252* If the amount is a {@code ChronoPeriod} then it must use the ISO chronology.253*254* @param amount the temporal amount to convert, not null255* @return the equivalent period, not null256* @throws DateTimeException if unable to convert to a {@code Period}257* @throws ArithmeticException if the amount of years, months or days exceeds an int258*/259public static Period from(TemporalAmount amount) {260if (amount instanceof Period) {261return (Period) amount;262}263if (amount instanceof ChronoPeriod) {264if (IsoChronology.INSTANCE.equals(((ChronoPeriod) amount).getChronology()) == false) {265throw new DateTimeException("Period requires ISO chronology: " + amount);266}267}268Objects.requireNonNull(amount, "amount");269int years = 0;270int months = 0;271int days = 0;272for (TemporalUnit unit : amount.getUnits()) {273long unitAmount = amount.get(unit);274if (unit == ChronoUnit.YEARS) {275years = Math.toIntExact(unitAmount);276} else if (unit == ChronoUnit.MONTHS) {277months = Math.toIntExact(unitAmount);278} else if (unit == ChronoUnit.DAYS) {279days = Math.toIntExact(unitAmount);280} else {281throw new DateTimeException("Unit must be Years, Months or Days, but was " + unit);282}283}284return create(years, months, days);285}286287//-----------------------------------------------------------------------288/**289* Obtains a {@code Period} from a text string such as {@code PnYnMnD}.290* <p>291* This will parse the string produced by {@code toString()} which is292* based on the ISO-8601 period formats {@code PnYnMnD} and {@code PnW}.293* <p>294* The string starts with an optional sign, denoted by the ASCII negative295* or positive symbol. If negative, the whole period is negated.296* The ASCII letter "P" is next in upper or lower case.297* There are then four sections, each consisting of a number and a suffix.298* At least one of the four sections must be present.299* The sections have suffixes in ASCII of "Y", "M", "W" and "D" for300* years, months, weeks and days, accepted in upper or lower case.301* The suffixes must occur in order.302* The number part of each section must consist of ASCII digits.303* The number may be prefixed by the ASCII negative or positive symbol.304* The number must parse to an {@code int}.305* <p>306* The leading plus/minus sign, and negative values for other units are307* not part of the ISO-8601 standard. In addition, ISO-8601 does not308* permit mixing between the {@code PnYnMnD} and {@code PnW} formats.309* Any week-based input is multiplied by 7 and treated as a number of days.310* <p>311* For example, the following are valid inputs:312* <pre>313* "P2Y" -- Period.ofYears(2)314* "P3M" -- Period.ofMonths(3)315* "P4W" -- Period.ofWeeks(4)316* "P5D" -- Period.ofDays(5)317* "P1Y2M3D" -- Period.of(1, 2, 3)318* "P1Y2M3W4D" -- Period.of(1, 2, 25)319* "P-1Y2M" -- Period.of(-1, 2, 0)320* "-P1Y2M" -- Period.of(-1, -2, 0)321* </pre>322*323* @param text the text to parse, not null324* @return the parsed period, not null325* @throws DateTimeParseException if the text cannot be parsed to a period326*/327public static Period parse(CharSequence text) {328Objects.requireNonNull(text, "text");329Matcher matcher = PATTERN.matcher(text);330if (matcher.matches()) {331int negate = ("-".equals(matcher.group(1)) ? -1 : 1);332String yearMatch = matcher.group(2);333String monthMatch = matcher.group(3);334String weekMatch = matcher.group(4);335String dayMatch = matcher.group(5);336if (yearMatch != null || monthMatch != null || dayMatch != null || weekMatch != null) {337try {338int years = parseNumber(text, yearMatch, negate);339int months = parseNumber(text, monthMatch, negate);340int weeks = parseNumber(text, weekMatch, negate);341int days = parseNumber(text, dayMatch, negate);342days = Math.addExact(days, Math.multiplyExact(weeks, 7));343return create(years, months, days);344} catch (NumberFormatException ex) {345throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);346}347}348}349throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);350}351352private static int parseNumber(CharSequence text, String str, int negate) {353if (str == null) {354return 0;355}356int val = Integer.parseInt(str);357try {358return Math.multiplyExact(val, negate);359} catch (ArithmeticException ex) {360throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0, ex);361}362}363364//-----------------------------------------------------------------------365/**366* Obtains a {@code Period} consisting of the number of years, months,367* and days between two dates.368* <p>369* The start date is included, but the end date is not.370* The period is calculated by removing complete months, then calculating371* the remaining number of days, adjusting to ensure that both have the same sign.372* The number of months is then split into years and months based on a 12 month year.373* A month is considered if the end day-of-month is greater than or equal to the start day-of-month.374* For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.375* <p>376* The result of this method can be a negative period if the end is before the start.377* The negative sign will be the same in each of year, month and day.378*379* @param startDateInclusive the start date, inclusive, not null380* @param endDateExclusive the end date, exclusive, not null381* @return the period between this date and the end date, not null382* @see ChronoLocalDate#until(ChronoLocalDate)383*/384public static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) {385return startDateInclusive.until(endDateExclusive);386}387388//-----------------------------------------------------------------------389/**390* Creates an instance.391*392* @param years the amount393* @param months the amount394* @param days the amount395*/396private static Period create(int years, int months, int days) {397if ((years | months | days) == 0) {398return ZERO;399}400return new Period(years, months, days);401}402403/**404* Constructor.405*406* @param years the amount407* @param months the amount408* @param days the amount409*/410private Period(int years, int months, int days) {411this.years = years;412this.months = months;413this.days = days;414}415416//-----------------------------------------------------------------------417/**418* Gets the value of the requested unit.419* <p>420* This returns a value for each of the three supported units,421* {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and422* {@link ChronoUnit#DAYS DAYS}.423* All other units throw an exception.424*425* @param unit the {@code TemporalUnit} for which to return the value426* @return the long value of the unit427* @throws DateTimeException if the unit is not supported428* @throws UnsupportedTemporalTypeException if the unit is not supported429*/430@Override431public long get(TemporalUnit unit) {432if (unit == ChronoUnit.YEARS) {433return getYears();434} else if (unit == ChronoUnit.MONTHS) {435return getMonths();436} else if (unit == ChronoUnit.DAYS) {437return getDays();438} else {439throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit);440}441}442443/**444* Gets the set of units supported by this period.445* <p>446* The supported units are {@link ChronoUnit#YEARS YEARS},447* {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.448* They are returned in the order years, months, days.449* <p>450* This set can be used in conjunction with {@link #get(TemporalUnit)}451* to access the entire state of the period.452*453* @return a list containing the years, months and days units, not null454*/455@Override456public List<TemporalUnit> getUnits() {457return SUPPORTED_UNITS;458}459460/**461* Gets the chronology of this period, which is the ISO calendar system.462* <p>463* The {@code Chronology} represents the calendar system in use.464* The ISO-8601 calendar system is the modern civil calendar system used today465* in most of the world. It is equivalent to the proleptic Gregorian calendar466* system, in which today's rules for leap years are applied for all time.467*468* @return the ISO chronology, not null469*/470@Override471public IsoChronology getChronology() {472return IsoChronology.INSTANCE;473}474475//-----------------------------------------------------------------------476/**477* Checks if all three units of this period are zero.478* <p>479* A zero period has the value zero for the years, months and days units.480*481* @return true if this period is zero-length482*/483public boolean isZero() {484return (this == ZERO);485}486487/**488* Checks if any of the three units of this period are negative.489* <p>490* This checks whether the years, months or days units are less than zero.491*492* @return true if any unit of this period is negative493*/494public boolean isNegative() {495return years < 0 || months < 0 || days < 0;496}497498//-----------------------------------------------------------------------499/**500* Gets the amount of years of this period.501* <p>502* This returns the years unit.503* <p>504* The months unit is not automatically normalized with the years unit.505* This means that a period of "15 months" is different to a period506* of "1 year and 3 months".507*508* @return the amount of years of this period, may be negative509*/510public int getYears() {511return years;512}513514/**515* Gets the amount of months of this period.516* <p>517* This returns the months unit.518* <p>519* The months unit is not automatically normalized with the years unit.520* This means that a period of "15 months" is different to a period521* of "1 year and 3 months".522*523* @return the amount of months of this period, may be negative524*/525public int getMonths() {526return months;527}528529/**530* Gets the amount of days of this period.531* <p>532* This returns the days unit.533*534* @return the amount of days of this period, may be negative535*/536public int getDays() {537return days;538}539540//-----------------------------------------------------------------------541/**542* Returns a copy of this period with the specified amount of years.543* <p>544* This sets the amount of the years unit in a copy of this period.545* The months and days units are unaffected.546* <p>547* The months unit is not automatically normalized with the years unit.548* This means that a period of "15 months" is different to a period549* of "1 year and 3 months".550* <p>551* This instance is immutable and unaffected by this method call.552*553* @param years the years to represent, may be negative554* @return a {@code Period} based on this period with the requested years, not null555*/556public Period withYears(int years) {557if (years == this.years) {558return this;559}560return create(years, months, days);561}562563/**564* Returns a copy of this period with the specified amount of months.565* <p>566* This sets the amount of the months unit in a copy of this period.567* The years and days units are unaffected.568* <p>569* The months unit is not automatically normalized with the years unit.570* This means that a period of "15 months" is different to a period571* of "1 year and 3 months".572* <p>573* This instance is immutable and unaffected by this method call.574*575* @param months the months to represent, may be negative576* @return a {@code Period} based on this period with the requested months, not null577*/578public Period withMonths(int months) {579if (months == this.months) {580return this;581}582return create(years, months, days);583}584585/**586* Returns a copy of this period with the specified amount of days.587* <p>588* This sets the amount of the days unit in a copy of this period.589* The years and months units are unaffected.590* <p>591* This instance is immutable and unaffected by this method call.592*593* @param days the days to represent, may be negative594* @return a {@code Period} based on this period with the requested days, not null595*/596public Period withDays(int days) {597if (days == this.days) {598return this;599}600return create(years, months, days);601}602603//-----------------------------------------------------------------------604/**605* Returns a copy of this period with the specified period added.606* <p>607* This operates separately on the years, months and days.608* No normalization is performed.609* <p>610* For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"611* returns "3 years, 8 months and 5 days".612* <p>613* The specified amount is typically an instance of {@code Period}.614* Other types are interpreted using {@link Period#from(TemporalAmount)}.615* <p>616* This instance is immutable and unaffected by this method call.617*618* @param amountToAdd the amount to add, not null619* @return a {@code Period} based on this period with the requested period added, not null620* @throws DateTimeException if the specified amount has a non-ISO chronology or621* contains an invalid unit622* @throws ArithmeticException if numeric overflow occurs623*/624public Period plus(TemporalAmount amountToAdd) {625Period isoAmount = Period.from(amountToAdd);626return create(627Math.addExact(years, isoAmount.years),628Math.addExact(months, isoAmount.months),629Math.addExact(days, isoAmount.days));630}631632/**633* Returns a copy of this period with the specified years added.634* <p>635* This adds the amount to the years unit in a copy of this period.636* The months and days units are unaffected.637* For example, "1 year, 6 months and 3 days" plus 2 years returns "3 years, 6 months and 3 days".638* <p>639* This instance is immutable and unaffected by this method call.640*641* @param yearsToAdd the years to add, positive or negative642* @return a {@code Period} based on this period with the specified years added, not null643* @throws ArithmeticException if numeric overflow occurs644*/645public Period plusYears(long yearsToAdd) {646if (yearsToAdd == 0) {647return this;648}649return create(Math.toIntExact(Math.addExact(years, yearsToAdd)), months, days);650}651652/**653* Returns a copy of this period with the specified months added.654* <p>655* This adds the amount to the months unit in a copy of this period.656* The years and days units are unaffected.657* For example, "1 year, 6 months and 3 days" plus 2 months returns "1 year, 8 months and 3 days".658* <p>659* This instance is immutable and unaffected by this method call.660*661* @param monthsToAdd the months to add, positive or negative662* @return a {@code Period} based on this period with the specified months added, not null663* @throws ArithmeticException if numeric overflow occurs664*/665public Period plusMonths(long monthsToAdd) {666if (monthsToAdd == 0) {667return this;668}669return create(years, Math.toIntExact(Math.addExact(months, monthsToAdd)), days);670}671672/**673* Returns a copy of this period with the specified days added.674* <p>675* This adds the amount to the days unit in a copy of this period.676* The years and months units are unaffected.677* For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".678* <p>679* This instance is immutable and unaffected by this method call.680*681* @param daysToAdd the days to add, positive or negative682* @return a {@code Period} based on this period with the specified days added, not null683* @throws ArithmeticException if numeric overflow occurs684*/685public Period plusDays(long daysToAdd) {686if (daysToAdd == 0) {687return this;688}689return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));690}691692//-----------------------------------------------------------------------693/**694* Returns a copy of this period with the specified period subtracted.695* <p>696* This operates separately on the years, months and days.697* No normalization is performed.698* <p>699* For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"700* returns "-1 years, 4 months and 1 day".701* <p>702* The specified amount is typically an instance of {@code Period}.703* Other types are interpreted using {@link Period#from(TemporalAmount)}.704* <p>705* This instance is immutable and unaffected by this method call.706*707* @param amountToSubtract the amount to subtract, not null708* @return a {@code Period} based on this period with the requested period subtracted, not null709* @throws DateTimeException if the specified amount has a non-ISO chronology or710* contains an invalid unit711* @throws ArithmeticException if numeric overflow occurs712*/713public Period minus(TemporalAmount amountToSubtract) {714Period isoAmount = Period.from(amountToSubtract);715return create(716Math.subtractExact(years, isoAmount.years),717Math.subtractExact(months, isoAmount.months),718Math.subtractExact(days, isoAmount.days));719}720721/**722* Returns a copy of this period with the specified years subtracted.723* <p>724* This subtracts the amount from the years unit in a copy of this period.725* The months and days units are unaffected.726* For example, "1 year, 6 months and 3 days" minus 2 years returns "-1 years, 6 months and 3 days".727* <p>728* This instance is immutable and unaffected by this method call.729*730* @param yearsToSubtract the years to subtract, positive or negative731* @return a {@code Period} based on this period with the specified years subtracted, not null732* @throws ArithmeticException if numeric overflow occurs733*/734public Period minusYears(long yearsToSubtract) {735return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract));736}737738/**739* Returns a copy of this period with the specified months subtracted.740* <p>741* This subtracts the amount from the months unit in a copy of this period.742* The years and days units are unaffected.743* For example, "1 year, 6 months and 3 days" minus 2 months returns "1 year, 4 months and 3 days".744* <p>745* This instance is immutable and unaffected by this method call.746*747* @param monthsToSubtract the years to subtract, positive or negative748* @return a {@code Period} based on this period with the specified months subtracted, not null749* @throws ArithmeticException if numeric overflow occurs750*/751public Period minusMonths(long monthsToSubtract) {752return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract));753}754755/**756* Returns a copy of this period with the specified days subtracted.757* <p>758* This subtracts the amount from the days unit in a copy of this period.759* The years and months units are unaffected.760* For example, "1 year, 6 months and 3 days" minus 2 days returns "1 year, 6 months and 1 day".761* <p>762* This instance is immutable and unaffected by this method call.763*764* @param daysToSubtract the months to subtract, positive or negative765* @return a {@code Period} based on this period with the specified days subtracted, not null766* @throws ArithmeticException if numeric overflow occurs767*/768public Period minusDays(long daysToSubtract) {769return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));770}771772//-----------------------------------------------------------------------773/**774* Returns a new instance with each element in this period multiplied775* by the specified scalar.776* <p>777* This returns a period with each of the years, months and days units778* individually multiplied.779* For example, a period of "2 years, -3 months and 4 days" multiplied by780* 3 will return "6 years, -9 months and 12 days".781* No normalization is performed.782*783* @param scalar the scalar to multiply by, not null784* @return a {@code Period} based on this period with the amounts multiplied by the scalar, not null785* @throws ArithmeticException if numeric overflow occurs786*/787public Period multipliedBy(int scalar) {788if (this == ZERO || scalar == 1) {789return this;790}791return create(792Math.multiplyExact(years, scalar),793Math.multiplyExact(months, scalar),794Math.multiplyExact(days, scalar));795}796797/**798* Returns a new instance with each amount in this period negated.799* <p>800* This returns a period with each of the years, months and days units801* individually negated.802* For example, a period of "2 years, -3 months and 4 days" will be803* negated to "-2 years, 3 months and -4 days".804* No normalization is performed.805*806* @return a {@code Period} based on this period with the amounts negated, not null807* @throws ArithmeticException if numeric overflow occurs, which only happens if808* one of the units has the value {@code Long.MIN_VALUE}809*/810public Period negated() {811return multipliedBy(-1);812}813814//-----------------------------------------------------------------------815/**816* Returns a copy of this period with the years and months normalized.817* <p>818* This normalizes the years and months units, leaving the days unit unchanged.819* The months unit is adjusted to have an absolute value less than 11,820* with the years unit being adjusted to compensate. For example, a period of821* "1 Year and 15 months" will be normalized to "2 years and 3 months".822* <p>823* The sign of the years and months units will be the same after normalization.824* For example, a period of "1 year and -25 months" will be normalized to825* "-1 year and -1 month".826* <p>827* This instance is immutable and unaffected by this method call.828*829* @return a {@code Period} based on this period with excess months normalized to years, not null830* @throws ArithmeticException if numeric overflow occurs831*/832public Period normalized() {833long totalMonths = toTotalMonths();834long splitYears = totalMonths / 12;835int splitMonths = (int) (totalMonths % 12); // no overflow836if (splitYears == years && splitMonths == months) {837return this;838}839return create(Math.toIntExact(splitYears), splitMonths, days);840}841842/**843* Gets the total number of months in this period.844* <p>845* This returns the total number of months in the period by multiplying the846* number of years by 12 and adding the number of months.847* <p>848* This instance is immutable and unaffected by this method call.849*850* @return the total number of months in the period, may be negative851*/852public long toTotalMonths() {853return years * 12L + months; // no overflow854}855856//-------------------------------------------------------------------------857/**858* Adds this period to the specified temporal object.859* <p>860* This returns a temporal object of the same observable type as the input861* with this period added.862* If the temporal has a chronology, it must be the ISO chronology.863* <p>864* In most cases, it is clearer to reverse the calling pattern by using865* {@link Temporal#plus(TemporalAmount)}.866* <pre>867* // these two lines are equivalent, but the second approach is recommended868* dateTime = thisPeriod.addTo(dateTime);869* dateTime = dateTime.plus(thisPeriod);870* </pre>871* <p>872* The calculation operates as follows.873* First, the chronology of the temporal is checked to ensure it is ISO chronology or null.874* Second, if the months are zero, the years are added if non-zero, otherwise875* the combination of years and months is added if non-zero.876* Finally, any days are added.877* <p>878* This approach ensures that a partial period can be added to a partial date.879* For example, a period of years and/or months can be added to a {@code YearMonth},880* but a period including days cannot.881* The approach also adds years and months together when necessary, which ensures882* correct behaviour at the end of the month.883* <p>884* This instance is immutable and unaffected by this method call.885*886* @param temporal the temporal object to adjust, not null887* @return an object of the same type with the adjustment made, not null888* @throws DateTimeException if unable to add889* @throws ArithmeticException if numeric overflow occurs890*/891@Override892public Temporal addTo(Temporal temporal) {893validateChrono(temporal);894if (months == 0) {895if (years != 0) {896temporal = temporal.plus(years, YEARS);897}898} else {899long totalMonths = toTotalMonths();900if (totalMonths != 0) {901temporal = temporal.plus(totalMonths, MONTHS);902}903}904if (days != 0) {905temporal = temporal.plus(days, DAYS);906}907return temporal;908}909910/**911* Subtracts this period from the specified temporal object.912* <p>913* This returns a temporal object of the same observable type as the input914* with this period subtracted.915* If the temporal has a chronology, it must be the ISO chronology.916* <p>917* In most cases, it is clearer to reverse the calling pattern by using918* {@link Temporal#minus(TemporalAmount)}.919* <pre>920* // these two lines are equivalent, but the second approach is recommended921* dateTime = thisPeriod.subtractFrom(dateTime);922* dateTime = dateTime.minus(thisPeriod);923* </pre>924* <p>925* The calculation operates as follows.926* First, the chronology of the temporal is checked to ensure it is ISO chronology or null.927* Second, if the months are zero, the years are subtracted if non-zero, otherwise928* the combination of years and months is subtracted if non-zero.929* Finally, any days are subtracted.930* <p>931* This approach ensures that a partial period can be subtracted from a partial date.932* For example, a period of years and/or months can be subtracted from a {@code YearMonth},933* but a period including days cannot.934* The approach also subtracts years and months together when necessary, which ensures935* correct behaviour at the end of the month.936* <p>937* This instance is immutable and unaffected by this method call.938*939* @param temporal the temporal object to adjust, not null940* @return an object of the same type with the adjustment made, not null941* @throws DateTimeException if unable to subtract942* @throws ArithmeticException if numeric overflow occurs943*/944@Override945public Temporal subtractFrom(Temporal temporal) {946validateChrono(temporal);947if (months == 0) {948if (years != 0) {949temporal = temporal.minus(years, YEARS);950}951} else {952long totalMonths = toTotalMonths();953if (totalMonths != 0) {954temporal = temporal.minus(totalMonths, MONTHS);955}956}957if (days != 0) {958temporal = temporal.minus(days, DAYS);959}960return temporal;961}962963/**964* Validates that the temporal has the correct chronology.965*/966private void validateChrono(TemporalAccessor temporal) {967Objects.requireNonNull(temporal, "temporal");968Chronology temporalChrono = temporal.query(TemporalQueries.chronology());969if (temporalChrono != null && IsoChronology.INSTANCE.equals(temporalChrono) == false) {970throw new DateTimeException("Chronology mismatch, expected: ISO, actual: " + temporalChrono.getId());971}972}973974//-----------------------------------------------------------------------975/**976* Checks if this period is equal to another period.977* <p>978* The comparison is based on the type {@code Period} and each of the three amounts.979* To be equal, the years, months and days units must be individually equal.980* Note that this means that a period of "15 Months" is not equal to a period981* of "1 Year and 3 Months".982*983* @param obj the object to check, null returns false984* @return true if this is equal to the other period985*/986@Override987public boolean equals(Object obj) {988if (this == obj) {989return true;990}991if (obj instanceof Period) {992Period other = (Period) obj;993return years == other.years &&994months == other.months &&995days == other.days;996}997return false;998}9991000/**1001* A hash code for this period.1002*1003* @return a suitable hash code1004*/1005@Override1006public int hashCode() {1007return years + Integer.rotateLeft(months, 8) + Integer.rotateLeft(days, 16);1008}10091010//-----------------------------------------------------------------------1011/**1012* Outputs this period as a {@code String}, such as {@code P6Y3M1D}.1013* <p>1014* The output will be in the ISO-8601 period format.1015* A zero period will be represented as zero days, 'P0D'.1016*1017* @return a string representation of this period, not null1018*/1019@Override1020public String toString() {1021if (this == ZERO) {1022return "P0D";1023} else {1024StringBuilder buf = new StringBuilder();1025buf.append('P');1026if (years != 0) {1027buf.append(years).append('Y');1028}1029if (months != 0) {1030buf.append(months).append('M');1031}1032if (days != 0) {1033buf.append(days).append('D');1034}1035return buf.toString();1036}1037}10381039//-----------------------------------------------------------------------1040/**1041* Writes the object using a1042* <a href="../../serialized-form.html#java.time.Ser">dedicated serialized form</a>.1043* @serialData1044* <pre>1045* out.writeByte(14); // identifies a Period1046* out.writeInt(years);1047* out.writeInt(months);1048* out.writeInt(days);1049* </pre>1050*1051* @return the instance of {@code Ser}, not null1052*/1053private Object writeReplace() {1054return new Ser(Ser.PERIOD_TYPE, this);1055}10561057/**1058* Defend against malicious streams.1059*1060* @param s the stream to read1061* @throws java.io.InvalidObjectException always1062*/1063private void readObject(ObjectInputStream s) throws InvalidObjectException {1064throw new InvalidObjectException("Deserialization via serialization delegate");1065}10661067void writeExternal(DataOutput out) throws IOException {1068out.writeInt(years);1069out.writeInt(months);1070out.writeInt(days);1071}10721073static Period readExternal(DataInput in) throws IOException {1074int years = in.readInt();1075int months = in.readInt();1076int days = in.readInt();1077return Period.of(years, months, days);1078}10791080}108110821083