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