1 /* 2 * Copyright (c) 2012, 2013, 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.LocalTime.NANOS_PER_DAY; 65 import static java.time.LocalTime.NANOS_PER_HOUR; 66 import static java.time.LocalTime.NANOS_PER_MINUTE; 67 import static java.time.LocalTime.NANOS_PER_SECOND; 68 import static java.time.temporal.ChronoField.DAY_OF_MONTH; 69 import static java.time.temporal.ChronoField.EPOCH_MONTH; 70 import static java.time.temporal.ChronoField.MONTH_OF_YEAR; 71 import static java.time.temporal.ChronoField.NANO_OF_DAY; 72 import static java.time.temporal.ChronoField.YEAR; 73 import static java.time.temporal.ChronoUnit.DAYS; 74 import static java.time.temporal.ChronoUnit.MONTHS; 75 import static java.time.temporal.ChronoUnit.NANOS; 76 import static java.time.temporal.ChronoUnit.YEARS; 77 78 import java.io.Serializable; 79 import java.time.format.DateTimeParseException; 80 import java.time.temporal.Chrono; 81 import java.time.temporal.ChronoField; 82 import java.time.temporal.ChronoUnit; 83 import java.time.temporal.Temporal; 84 import java.time.temporal.TemporalAccessor; 85 import java.time.temporal.TemporalAdder; 86 import java.time.temporal.TemporalSubtractor; 87 import java.time.temporal.TemporalUnit; 88 import java.time.temporal.ValueRange; 89 import java.util.Objects; 90 91 /** 92 * A period of time, measured using the most common units, such as '3 Months, 4 Days and 7 Hours'. 93 * <p> 94 * A {@code Period} represents an amount of time measured in terms of the most commonly used units: 95 * <p><ul> 96 * <li>{@link ChronoUnit#YEARS YEARS}</li> 97 * <li>{@link ChronoUnit#MONTHS MONTHS}</li> 98 * <li>{@link ChronoUnit#DAYS DAYS}</li> 99 * <li>time units with an {@linkplain TemporalUnit#isDurationEstimated() exact duration}</li> 100 * </ul><p> 101 * The period may be used with any calendar system with the exception is methods with an "ISO" suffix. 102 * The meaning of a "year" or a "month" is only applied when the object is added to a date. 103 * <p> 104 * The period is modeled as a directed amount of time, meaning that individual parts of the 105 * period may be negative. 106 * 107 * <h3>Specification for implementors</h3> 108 * This class is immutable and thread-safe. 109 * The maximum number of hours that can be stored is about 2.5 million, limited by storing 110 * a single {@code long} nanoseconds for all time units internally. 111 * 112 * @since 1.8 113 */ 114 public final class Period 115 implements TemporalAdder, TemporalSubtractor, Serializable { 116 // maximum hours is 2,562,047 117 118 /** 119 * A constant for a period of zero. 120 */ 121 public static final Period ZERO = new Period(0, 0, 0, 0); 122 /** 123 * Serialization version. 124 */ 125 private static final long serialVersionUID = -8290556941213247973L; 126 127 /** 128 * The number of years. 129 */ 130 private final int years; 131 /** 132 * The number of months. 133 */ 134 private final int months; 135 /** 136 * The number of days. 137 */ 138 private final int days; 139 /** 140 * The number of nanoseconds. 141 */ 142 private final long nanos; 143 144 //----------------------------------------------------------------------- 145 /** 146 * Obtains a {@code Period} from date-based and time-based fields. 147 * <p> 148 * This creates an instance based on years, months, days, hours, minutes and seconds. 149 * Within a period, the time fields are always normalized. 150 * 151 * @param years the amount of years, may be negative 152 * @param months the amount of months, may be negative 153 * @param days the amount of days, may be negative 154 * @param hours the amount of hours, may be negative 155 * @param minutes the amount of minutes, may be negative 156 * @param seconds the amount of seconds, may be negative 157 * @return the period, not null 158 */ 159 public static Period of(int years, int months, int days, int hours, int minutes, int seconds) { 160 return of(years, months, days, hours, minutes, seconds, 0); 161 } 162 163 /** 164 * Obtains a {@code Period} from date-based and time-based fields. 165 * <p> 166 * This creates an instance based on years, months, days, hours, minutes, seconds and nanoseconds. 167 * Within a period, the time fields are always normalized. 168 * 169 * @param years the amount of years, may be negative 170 * @param months the amount of months, may be negative 171 * @param days the amount of days, may be negative 172 * @param hours the amount of hours, may be negative 173 * @param minutes the amount of minutes, may be negative 174 * @param seconds the amount of seconds, may be negative 175 * @param nanos the amount of nanos, may be negative 176 * @return the period, not null 177 */ 178 public static Period of(int years, int months, int days, int hours, int minutes, int seconds, long nanos) { 179 if ((years | months | days | hours | minutes | seconds | nanos) == 0) { 180 return ZERO; 181 } 182 long totSecs = Math.addExact(hours * 3600L, minutes * 60L) + seconds; 183 long totNanos = Math.addExact(Math.multiplyExact(totSecs, 1_000_000_000L), nanos); 184 return create(years, months, days, totNanos); 185 } 186 187 //----------------------------------------------------------------------- 188 /** 189 * Obtains a {@code Period} from date-based fields. 190 * <p> 191 * This creates an instance based on years, months and days. 192 * 193 * @param years the amount of years, may be negative 194 * @param months the amount of months, may be negative 195 * @param days the amount of days, may be negative 196 * @return the period, not null 197 */ 198 public static Period ofDate(int years, int months, int days) { 199 return of(years, months, days, 0, 0, 0, 0); 200 } 201 202 //----------------------------------------------------------------------- 203 /** 204 * Obtains a {@code Period} from time-based fields. 205 * <p> 206 * This creates an instance based on hours, minutes and seconds. 207 * Within a period, the time fields are always normalized. 208 * 209 * @param hours the amount of hours, may be negative 210 * @param minutes the amount of minutes, may be negative 211 * @param seconds the amount of seconds, may be negative 212 * @return the period, not null 213 */ 214 public static Period ofTime(int hours, int minutes, int seconds) { 215 return of(0, 0, 0, hours, minutes, seconds, 0); 216 } 217 218 /** 219 * Obtains a {@code Period} from time-based fields. 220 * <p> 221 * This creates an instance based on hours, minutes, seconds and nanoseconds. 222 * Within a period, the time fields are always normalized. 223 * 224 * @param hours the amount of hours, may be negative 225 * @param minutes the amount of minutes, may be negative 226 * @param seconds the amount of seconds, may be negative 227 * @param nanos the amount of nanos, may be negative 228 * @return the period, not null 229 */ 230 public static Period ofTime(int hours, int minutes, int seconds, long nanos) { 231 return of(0, 0, 0, hours, minutes, seconds, nanos); 232 } 233 234 //----------------------------------------------------------------------- 235 /** 236 * Obtains an instance of {@code Period} from a period in the specified unit. 237 * <p> 238 * The parameters represent the two parts of a phrase like '6 Days'. For example: 239 * <pre> 240 * Period.of(3, SECONDS); 241 * Period.of(5, YEARS); 242 * </pre> 243 * The specified unit must be one of the supported units from {@link ChronoUnit}, 244 * {@code YEARS}, {@code MONTHS} or {@code DAYS} or be a time unit with an 245 * {@linkplain TemporalUnit#isDurationEstimated() exact duration}. 246 * Other units throw an exception. 247 * 248 * @param amount the amount of the period, measured in terms of the unit, positive or negative 249 * @param unit the unit that the period is measured in, must have an exact duration, not null 250 * @return the period, not null 251 * @throws DateTimeException if the period unit is invalid 252 * @throws ArithmeticException if a numeric overflow occurs 253 */ 254 public static Period of(long amount, TemporalUnit unit) { 255 return ZERO.plus(amount, unit); 256 } 257 258 //----------------------------------------------------------------------- 259 /** 260 * Obtains a {@code Period} from a {@code Duration}. 261 * <p> 262 * This converts the duration to a period. 263 * Within a period, the time fields are always normalized. 264 * The years, months and days fields will be zero. 265 * <p> 266 * To populate the days field, call {@link #normalizedHoursToDays()} on the created period. 267 * 268 * @param duration the duration to convert, not null 269 * @return the period, not null 270 * @throws ArithmeticException if numeric overflow occurs 271 */ 272 public static Period of(Duration duration) { 273 Objects.requireNonNull(duration, "duration"); 274 if (duration.isZero()) { 275 return ZERO; 276 } 277 return new Period(0, 0, 0, duration.toNanos()); 278 } 279 280 //----------------------------------------------------------------------- 281 /** 282 * Returns a {@code Period} consisting of the number of years, months, days, 283 * hours, minutes, seconds, and nanoseconds between two {@code TemporalAccessor} instances. 284 * <p> 285 * The start date is included, but the end date is not. Only whole years count. 286 * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days. 287 * <p> 288 * This method examines the {@link ChronoField fields} {@code YEAR}, {@code MONTH_OF_YEAR}, 289 * {@code DAY_OF_MONTH} and {@code NANO_OF_DAY} 290 * The difference between each of the fields is calculated independently from the others. 291 * At least one of the four fields must be present. 292 * <p> 293 * The four units are typically retained without normalization. 294 * However, years and months are normalized if the range of months is fixed, as it is with ISO. 295 * <p> 296 * The result of this method can be a negative period if the end is before the start. 297 * The negative sign can be different in each of the four major units. 298 * 299 * @param start the start date, inclusive, not null 300 * @param end the end date, exclusive, not null 301 * @return the period between the date-times, not null 302 * @throws DateTimeException if the two date-times do have similar available fields 303 * @throws ArithmeticException if numeric overflow occurs 304 */ 305 public static Period between(TemporalAccessor start, TemporalAccessor end) { 306 if (Chrono.from(start).equals(Chrono.from(end)) == false) { 307 throw new DateTimeException("Unable to calculate period as date-times have different chronologies"); 308 } 309 int years = 0; 310 int months = 0; 311 int days = 0; 312 long nanos = 0; 313 boolean valid = false; 314 if (start.isSupported(YEAR)) { 315 years = Math.toIntExact(Math.subtractExact(end.getLong(YEAR), start.getLong(YEAR))); 316 valid = true; 317 } 318 if (start.isSupported(MONTH_OF_YEAR)) { 319 months = Math.toIntExact(Math.subtractExact(end.getLong(MONTH_OF_YEAR), start.getLong(MONTH_OF_YEAR))); 320 ValueRange startRange = Chrono.from(start).range(MONTH_OF_YEAR); 321 ValueRange endRange = Chrono.from(end).range(MONTH_OF_YEAR); 322 if (startRange.isFixed() && startRange.isIntValue() && startRange.equals(endRange)) { 323 int monthCount = (int) (startRange.getMaximum() - startRange.getMinimum() + 1); 324 long totMonths = ((long) months) + years * monthCount; 325 months = (int) (totMonths % monthCount); 326 years = Math.toIntExact(totMonths / monthCount); 327 } 328 valid = true; 329 } 330 if (start.isSupported(DAY_OF_MONTH)) { 331 days = Math.toIntExact(Math.subtractExact(end.getLong(DAY_OF_MONTH), start.getLong(DAY_OF_MONTH))); 332 valid = true; 333 } 334 if (start.isSupported(NANO_OF_DAY)) { 335 nanos = Math.subtractExact(end.getLong(NANO_OF_DAY), start.getLong(NANO_OF_DAY)); 336 valid = true; 337 } 338 if (valid == false) { 339 throw new DateTimeException("Unable to calculate period as date-times do not have any valid fields"); 340 } 341 return create(years, months, days, nanos); 342 } 343 344 //----------------------------------------------------------------------- 345 /** 346 * Obtains a {@code Period} consisting of the number of years, months, 347 * and days between two dates. 348 * <p> 349 * The start date is included, but the end date is not. 350 * The period is calculated by removing complete months, then calculating 351 * the remaining number of days, adjusting to ensure that both have the same sign. 352 * The number of months is then split into years and months based on a 12 month year. 353 * A month is considered if the end day-of-month is greater than or equal to the start day-of-month. 354 * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days. 355 * <p> 356 * The result of this method can be a negative period if the end is before the start. 357 * The negative sign will be the same in each of year, month and day. 358 * 359 * @param startDate the start date, inclusive, not null 360 * @param endDate the end date, exclusive, not null 361 * @return the period between the dates, not null 362 * @throws ArithmeticException if numeric overflow occurs 363 */ 364 public static Period betweenISO(LocalDate startDate, LocalDate endDate) { 365 long startMonth = startDate.getLong(EPOCH_MONTH); 366 long endMonth = endDate.getLong(EPOCH_MONTH); 367 long totalMonths = endMonth - startMonth; // safe 368 int days = endDate.getDayOfMonth() - startDate.getDayOfMonth(); 369 if (totalMonths > 0 && days < 0) { 370 totalMonths--; 371 LocalDate calcDate = startDate.plusMonths(totalMonths); 372 days = (int) (endDate.toEpochDay() - calcDate.toEpochDay()); // safe 373 } else if (totalMonths < 0 && days > 0) { 374 totalMonths++; 375 days -= endDate.lengthOfMonth(); 376 } 377 long years = totalMonths / 12; // safe 378 int months = (int) (totalMonths % 12); // safe 379 return ofDate(Math.toIntExact(years), months, days); 380 } 381 382 //----------------------------------------------------------------------- 383 /** 384 * Obtains a {@code Period} consisting of the number of hours, minutes, 385 * seconds and nanoseconds between two times. 386 * <p> 387 * The start time is included, but the end time is not. 388 * The period is calculated from the difference between the nano-of-day values 389 * of the two times. For example, from {@code 13:45:00} to {@code 14:50:30.123456789} 390 * is {@code P1H5M30.123456789S}. 391 * <p> 392 * The result of this method can be a negative period if the end is before the start. 393 * 394 * @param startTime the start time, inclusive, not null 395 * @param endTime the end time, exclusive, not null 396 * @return the period between the times, not null 397 * @throws ArithmeticException if numeric overflow occurs 398 */ 399 public static Period betweenISO(LocalTime startTime, LocalTime endTime) { 400 return create(0, 0, 0, endTime.toNanoOfDay() - startTime.toNanoOfDay()); 401 } 402 403 //----------------------------------------------------------------------- 404 /** 405 * Obtains a {@code Period} from a text string such as {@code PnYnMnDTnHnMn.nS}. 406 * <p> 407 * This will parse the string produced by {@code toString()} which is 408 * a subset of the ISO-8601 period format {@code PnYnMnDTnHnMn.nS}. 409 * <p> 410 * The string consists of a series of numbers with a suffix identifying their meaning. 411 * The values, and suffixes, must be in the sequence year, month, day, hour, minute, second. 412 * Any of the number/suffix pairs may be omitted providing at least one is present. 413 * If the period is zero, the value is normally represented as {@code PT0S}. 414 * The numbers must consist of ASCII digits. 415 * Any of the numbers may be negative. Negative zero is not accepted. 416 * The number of nanoseconds is expressed as an optional fraction of the seconds. 417 * There must be at least one digit before any decimal point. 418 * There must be between 1 and 9 inclusive digits after any decimal point. 419 * The letters will all be accepted in upper or lower case. 420 * The decimal point may be either a dot or a comma. 421 * 422 * @param text the text to parse, not null 423 * @return the parsed period, not null 424 * @throws DateTimeParseException if the text cannot be parsed to a period 425 */ 426 public static Period parse(final CharSequence text) { 427 Objects.requireNonNull(text, "text"); 428 return new PeriodParser(text).parse(); 429 } 430 431 //----------------------------------------------------------------------- 432 /** 433 * Creates an instance. 434 * 435 * @param years the amount 436 * @param months the amount 437 * @param days the amount 438 * @param nanos the amount 439 */ 440 private static Period create(int years, int months, int days, long nanos) { 441 if ((years | months | days | nanos) == 0) { 442 return ZERO; 443 } 444 return new Period(years, months, days, nanos); 445 } 446 447 /** 448 * Constructor. 449 * 450 * @param years the amount 451 * @param months the amount 452 * @param days the amount 453 * @param nanos the amount 454 */ 455 private Period(int years, int months, int days, long nanos) { 456 this.years = years; 457 this.months = months; 458 this.days = days; 459 this.nanos = nanos; 460 } 461 462 /** 463 * Resolves singletons. 464 * 465 * @return the resolved instance 466 */ 467 private Object readResolve() { 468 if ((years | months | days | nanos) == 0) { 469 return ZERO; 470 } 471 return this; 472 } 473 474 //----------------------------------------------------------------------- 475 /** 476 * Checks if this period is zero-length. 477 * 478 * @return true if this period is zero-length 479 */ 480 public boolean isZero() { 481 return (this == ZERO); 482 } 483 484 /** 485 * Checks if this period is fully positive, excluding zero. 486 * <p> 487 * This checks whether all the amounts in the period are positive, 488 * defined as greater than zero. 489 * 490 * @return true if this period is fully positive excluding zero 491 */ 492 public boolean isPositive() { 493 return ((years | months | days | nanos) > 0); 494 } 495 496 //----------------------------------------------------------------------- 497 /** 498 * Gets the amount of years of this period. 499 * 500 * @return the amount of years of this period 501 */ 502 public int getYears() { 503 return years; 504 } 505 506 /** 507 * Gets the amount of months of this period. 508 * 509 * @return the amount of months of this period 510 */ 511 public int getMonths() { 512 return months; 513 } 514 515 /** 516 * Gets the amount of days of this period. 517 * 518 * @return the amount of days of this period 519 */ 520 public int getDays() { 521 return days; 522 } 523 524 /** 525 * Gets the amount of hours of this period. 526 * <p> 527 * Within a period, the time fields are always normalized. 528 * 529 * @return the amount of hours of this period 530 */ 531 public int getHours() { 532 return (int) (nanos / NANOS_PER_HOUR); 533 } 534 535 /** 536 * Gets the amount of minutes within an hour of this period. 537 * <p> 538 * Within a period, the time fields are always normalized. 539 * 540 * @return the amount of minutes within an hour of this period 541 */ 542 public int getMinutes() { 543 return (int) ((nanos / NANOS_PER_MINUTE) % 60); 544 } 545 546 /** 547 * Gets the amount of seconds within a minute of this period. 548 * <p> 549 * Within a period, the time fields are always normalized. 550 * 551 * @return the amount of seconds within a minute of this period 552 */ 553 public int getSeconds() { 554 return (int) ((nanos / NANOS_PER_SECOND) % 60); 555 } 556 557 /** 558 * Gets the amount of nanoseconds within a second of this period. 559 * <p> 560 * Within a period, the time fields are always normalized. 561 * 562 * @return the amount of nanoseconds within a second of this period 563 */ 564 public int getNanos() { 565 return (int) (nanos % NANOS_PER_SECOND); // safe from overflow 566 } 567 568 /** 569 * Gets the total amount of the time units of this period, measured in nanoseconds. 570 * <p> 571 * Within a period, the time fields are always normalized. 572 * 573 * @return the total amount of time unit nanoseconds of this period 574 */ 575 public long getTimeNanos() { 576 return nanos; 577 } 578 579 //----------------------------------------------------------------------- 580 /** 581 * Returns a copy of this period with the specified amount of years. 582 * <p> 583 * This method will only affect the years field. 584 * All other units are unaffected. 585 * <p> 586 * This instance is immutable and unaffected by this method call. 587 * 588 * @param years the years to represent 589 * @return a {@code Period} based on this period with the requested years, not null 590 */ 591 public Period withYears(int years) { 592 if (years == this.years) { 593 return this; 594 } 595 return create(years, months, days, nanos); 596 } 597 598 /** 599 * Returns a copy of this period with the specified amount of months. 600 * <p> 601 * This method will only affect the months field. 602 * All other units are unaffected. 603 * <p> 604 * This instance is immutable and unaffected by this method call. 605 * 606 * @param months the months to represent 607 * @return a {@code Period} based on this period with the requested months, not null 608 */ 609 public Period withMonths(int months) { 610 if (months == this.months) { 611 return this; 612 } 613 return create(years, months, days, nanos); 614 } 615 616 /** 617 * Returns a copy of this period with the specified amount of days. 618 * <p> 619 * This method will only affect the days field. 620 * All other units are unaffected. 621 * <p> 622 * This instance is immutable and unaffected by this method call. 623 * 624 * @param days the days to represent 625 * @return a {@code Period} based on this period with the requested days, not null 626 */ 627 public Period withDays(int days) { 628 if (days == this.days) { 629 return this; 630 } 631 return create(years, months, days, nanos); 632 } 633 634 /** 635 * Returns a copy of this period with the specified total amount of time units 636 * expressed in nanoseconds. 637 * <p> 638 * Within a period, the time fields are always normalized. 639 * This method will affect all the time units - hours, minutes, seconds and nanos. 640 * The date units are unaffected. 641 * <p> 642 * This instance is immutable and unaffected by this method call. 643 * 644 * @param nanos the nanoseconds to represent 645 * @return a {@code Period} based on this period with the requested nanoseconds, not null 646 */ 647 public Period withTimeNanos(long nanos) { 648 if (nanos == this.nanos) { 649 return this; 650 } 651 return create(years, months, days, nanos); 652 } 653 654 //----------------------------------------------------------------------- 655 /** 656 * Returns a copy of this period with the specified period added. 657 * <p> 658 * This operates separately on the years, months, days and the normalized time. 659 * There is no further normalization beyond the normalized time. 660 * <p> 661 * This instance is immutable and unaffected by this method call. 662 * 663 * @param other the period to add, not null 664 * @return a {@code Period} based on this period with the requested period added, not null 665 * @throws ArithmeticException if numeric overflow occurs 666 */ 667 public Period plus(Period other) { 668 return create( 669 Math.addExact(years, other.years), 670 Math.addExact(months, other.months), 671 Math.addExact(days, other.days), 672 Math.addExact(nanos, other.nanos)); 673 } 674 675 /** 676 * Returns a copy of this period with the specified period added. 677 * <p> 678 * The specified unit must be one of the supported units from {@link ChronoUnit}, 679 * {@code YEARS}, {@code MONTHS} or {@code DAYS} or be a time unit with an 680 * {@linkplain TemporalUnit#isDurationEstimated() exact duration}. 681 * Other units throw an exception. 682 * <p> 683 * This instance is immutable and unaffected by this method call. 684 * 685 * @param amount the amount to add, positive or negative 686 * @param unit the unit that the amount is expressed in, not null 687 * @return a {@code Period} based on this period with the requested amount added, not null 688 * @throws ArithmeticException if numeric overflow occurs 689 */ 690 public Period plus(long amount, TemporalUnit unit) { 691 Objects.requireNonNull(unit, "unit"); 692 if (unit instanceof ChronoUnit) { 693 if (unit == YEARS || unit == MONTHS || unit == DAYS || unit.isDurationEstimated() == false) { 694 if (amount == 0) { 695 return this; 696 } 697 switch((ChronoUnit) unit) { 698 case NANOS: return plusNanos(amount); 699 case MICROS: return plusNanos(Math.multiplyExact(amount, 1000L)); 700 case MILLIS: return plusNanos(Math.multiplyExact(amount, 1000_000L)); 701 case SECONDS: return plusSeconds(amount); 702 case MINUTES: return plusMinutes(amount); 703 case HOURS: return plusHours(amount); 704 case HALF_DAYS: return plusNanos(Math.multiplyExact(amount, 12 * NANOS_PER_HOUR)); 705 case DAYS: return plusDays(amount); 706 case MONTHS: return plusMonths(amount); 707 case YEARS: return plusYears(amount); 708 default: throw new DateTimeException("Unsupported unit: " + unit.getName()); 709 } 710 } 711 } 712 if (unit.isDurationEstimated()) { 713 throw new DateTimeException("Unsupported unit: " + unit.getName()); 714 } 715 return plusNanos(Duration.of(amount, unit).toNanos()); 716 } 717 718 public Period plusYears(long amount) { 719 return create(Math.toIntExact(Math.addExact(years, amount)), months, days, nanos); 720 } 721 722 public Period plusMonths(long amount) { 723 return create(years, Math.toIntExact(Math.addExact(months, amount)), days, nanos); 724 } 725 726 public Period plusDays(long amount) { 727 return create(years, months, Math.toIntExact(Math.addExact(days, amount)), nanos); 728 } 729 730 public Period plusHours(long amount) { 731 return plusNanos(Math.multiplyExact(amount, NANOS_PER_HOUR)); 732 } 733 734 public Period plusMinutes(long amount) { 735 return plusNanos(Math.multiplyExact(amount, NANOS_PER_MINUTE)); 736 } 737 738 public Period plusSeconds(long amount) { 739 return plusNanos(Math.multiplyExact(amount, NANOS_PER_SECOND)); 740 } 741 742 public Period plusNanos(long amount) { 743 return create(years, months, days, Math.addExact(nanos, amount)); 744 } 745 746 //----------------------------------------------------------------------- 747 /** 748 * Returns a copy of this period with the specified period subtracted. 749 * <p> 750 * This operates separately on the years, months, days and the normalized time. 751 * There is no further normalization beyond the normalized time. 752 * <p> 753 * This instance is immutable and unaffected by this method call. 754 * 755 * @param other the period to subtract, not null 756 * @return a {@code Period} based on this period with the requested period subtracted, not null 757 * @throws ArithmeticException if numeric overflow occurs 758 */ 759 public Period minus(Period other) { 760 return create( 761 Math.subtractExact(years, other.years), 762 Math.subtractExact(months, other.months), 763 Math.subtractExact(days, other.days), 764 Math.subtractExact(nanos, other.nanos)); 765 } 766 767 /** 768 * Returns a copy of this period with the specified period subtracted. 769 * <p> 770 * The specified unit must be one of the supported units from {@link ChronoUnit}, 771 * {@code YEARS}, {@code MONTHS} or {@code DAYS} or be a time unit with an 772 * {@linkplain TemporalUnit#isDurationEstimated() exact duration}. 773 * Other units throw an exception. 774 * <p> 775 * This instance is immutable and unaffected by this method call. 776 * 777 * @param amount the amount to subtract, positive or negative 778 * @param unit the unit that the amount is expressed in, not null 779 * @return a {@code Period} based on this period with the requested amount subtracted, not null 780 * @throws ArithmeticException if numeric overflow occurs 781 */ 782 public Period minus(long amount, TemporalUnit unit) { 783 return (amount == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amount, unit)); 784 } 785 786 public Period minusYears(long amount) { 787 return (amount == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-amount)); 788 } 789 790 public Period minusMonths(long amount) { 791 return (amount == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-amount)); 792 } 793 794 public Period minusDays(long amount) { 795 return (amount == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-amount)); 796 } 797 798 public Period minusHours(long amount) { 799 return (amount == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-amount)); 800 } 801 802 public Period minusMinutes(long amount) { 803 return (amount == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-amount)); 804 } 805 806 public Period minusSeconds(long amount) { 807 return (amount == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-amount)); 808 } 809 810 public Period minusNanos(long amount) { 811 return (amount == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-amount)); 812 } 813 814 //----------------------------------------------------------------------- 815 /** 816 * Returns a new instance with each element in this period multiplied 817 * by the specified scalar. 818 * <p> 819 * This simply multiplies each field, years, months, days and normalized time, 820 * by the scalar. No normalization is performed. 821 * 822 * @param scalar the scalar to multiply by, not null 823 * @return a {@code Period} based on this period with the amounts multiplied by the scalar, not null 824 * @throws ArithmeticException if numeric overflow occurs 825 */ 826 public Period multipliedBy(int scalar) { 827 if (this == ZERO || scalar == 1) { 828 return this; 829 } 830 return create( 831 Math.multiplyExact(years, scalar), 832 Math.multiplyExact(months, scalar), 833 Math.multiplyExact(days, scalar), 834 Math.multiplyExact(nanos, scalar)); 835 } 836 837 /** 838 * Returns a new instance with each amount in this period negated. 839 * 840 * @return a {@code Period} based on this period with the amounts negated, not null 841 * @throws ArithmeticException if numeric overflow occurs 842 */ 843 public Period negated() { 844 return multipliedBy(-1); 845 } 846 847 //----------------------------------------------------------------------- 848 /** 849 * Returns a copy of this period with the days and hours normalized using a 24 hour day. 850 * <p> 851 * This normalizes the days and hours units, leaving years and months unchanged. 852 * The hours unit is adjusted to have an absolute value less than 23, 853 * with the days unit being adjusted to compensate. 854 * For example, a period of {@code P1DT27H} will be normalized to {@code P2DT3H}. 855 * <p> 856 * The sign of the days and hours units will be the same after normalization. 857 * For example, a period of {@code P1DT-51H} will be normalized to {@code P-1DT-3H}. 858 * Since all time units are always normalized, if the hours units changes sign then 859 * other time units will also be affected. 860 * <p> 861 * This instance is immutable and unaffected by this method call. 862 * 863 * @return a {@code Period} based on this period with excess hours normalized to days, not null 864 * @throws ArithmeticException if numeric overflow occurs 865 */ 866 public Period normalizedHoursToDays() { 867 // logic uses if statements to normalize signs to avoid unnecessary overflows 868 long totalDays = (nanos / NANOS_PER_DAY) + days; // no overflow 869 long splitNanos = nanos % NANOS_PER_DAY; 870 if (totalDays > 0 && splitNanos < 0) { 871 splitNanos += NANOS_PER_DAY; 872 totalDays--; 873 } else if (totalDays < 0 && splitNanos > 0) { 874 splitNanos -= NANOS_PER_DAY; 875 totalDays++; 876 } 877 if (totalDays == days && splitNanos == nanos) { 878 return this; 879 } 880 return create(years, months, Math.toIntExact(totalDays), splitNanos); 881 } 882 883 /** 884 * Returns a copy of this period with any days converted to hours using a 24 hour day. 885 * <p> 886 * The days unit is reduced to zero, with the hours unit increased by 24 times the 887 * days unit to compensate. Other units are unaffected. 888 * For example, a period of {@code P2DT4H} will be normalized to {@code PT52H}. 889 * <p> 890 * This instance is immutable and unaffected by this method call. 891 * 892 * @return a {@code Period} based on this period with days normalized to hours, not null 893 * @throws ArithmeticException if numeric overflow occurs 894 */ 895 public Period normalizedDaysToHours() { 896 if (days == 0) { 897 return this; 898 } 899 return create(years, months, 0, Math.addExact(Math.multiplyExact(days, NANOS_PER_DAY), nanos)); 900 } 901 902 /** 903 * Returns a copy of this period with the years and months normalized using a 12 month year. 904 * <p> 905 * This normalizes the years and months units, leaving other units unchanged. 906 * The months unit is adjusted to have an absolute value less than 11, 907 * with the years unit being adjusted to compensate. 908 * For example, a period of {@code P1Y15M} will be normalized to {@code P2Y3M}. 909 * <p> 910 * The sign of the years and months units will be the same after normalization. 911 * For example, a period of {@code P1Y-25M} will be normalized to {@code P-1Y-1M}. 912 * <p> 913 * This normalization uses a 12 month year it is not valid for all calendar systems. 914 * <p> 915 * This instance is immutable and unaffected by this method call. 916 * 917 * @return a {@code Period} based on this period with years and months normalized, not null 918 * @throws ArithmeticException if numeric overflow occurs 919 */ 920 public Period normalizedMonthsISO() { 921 long totalMonths = years * 12L + months; // no overflow 922 long splitYears = totalMonths / 12; 923 int splitMonths = (int) (totalMonths % 12); // no overflow 924 if (splitYears == years && splitMonths == months) { 925 return this; 926 } 927 return create(Math.toIntExact(splitYears), splitMonths, days, nanos); 928 } 929 930 //------------------------------------------------------------------------- 931 /** 932 * Converts this period to one that only has date units. 933 * <p> 934 * The resulting period will have the same years, months and days as this period 935 * but the time units will all be zero. No normalization occurs in the calculation. 936 * For example, a period of {@code P1Y3MT12H} will be converted to {@code P1Y3M}. 937 * <p> 938 * This instance is immutable and unaffected by this method call. 939 * 940 * @return a {@code Period} based on this period with the time units set to zero, not null 941 */ 942 public Period toDateOnly() { 943 if (nanos == 0) { 944 return this; 945 } 946 return create(years, months, days, 0); 947 } 948 949 //------------------------------------------------------------------------- 950 /** 951 * Adds this period to the specified temporal object. 952 * <p> 953 * This returns a temporal object of the same observable type as the input 954 * with this period added. 955 * <p> 956 * In most cases, it is clearer to reverse the calling pattern by using 957 * {@link Temporal#plus(TemporalAdder)}. 958 * <pre> 959 * // these two lines are equivalent, but the second approach is recommended 960 * dateTime = thisPeriod.addTo(dateTime); 961 * dateTime = dateTime.plus(thisPeriod); 962 * </pre> 963 * <p> 964 * The calculation will add the years, then months, then days, then nanos. 965 * Only non-zero amounts will be added. 966 * If the date-time has a calendar system with a fixed number of months in a 967 * year, then the years and months will be combined before being added. 968 * <p> 969 * This instance is immutable and unaffected by this method call. 970 * 971 * @param temporal the temporal object to adjust, not null 972 * @return an object of the same type with the adjustment made, not null 973 * @throws DateTimeException if unable to add 974 * @throws ArithmeticException if numeric overflow occurs 975 */ 976 @Override 977 public Temporal addTo(Temporal temporal) { 978 Objects.requireNonNull(temporal, "temporal"); 979 if ((years | months) != 0) { 980 ValueRange startRange = Chrono.from(temporal).range(MONTH_OF_YEAR); 981 if (startRange.isFixed() && startRange.isIntValue()) { 982 long monthCount = startRange.getMaximum() - startRange.getMinimum() + 1; 983 temporal = temporal.plus(years * monthCount + months, MONTHS); 984 } else { 985 if (years != 0) { 986 temporal = temporal.plus(years, YEARS); 987 } 988 if (months != 0) { 989 temporal = temporal.plus(months, MONTHS); 990 } 991 } 992 } 993 if (days != 0) { 994 temporal = temporal.plus(days, DAYS); 995 } 996 if (nanos != 0) { 997 temporal = temporal.plus(nanos, NANOS); 998 } 999 return temporal; 1000 } 1001 1002 /** 1003 * Subtracts this period from the specified temporal object. 1004 * <p> 1005 * This returns a temporal object of the same observable type as the input 1006 * with this period subtracted. 1007 * <p> 1008 * In most cases, it is clearer to reverse the calling pattern by using 1009 * {@link Temporal#minus(TemporalSubtractor)}. 1010 * <pre> 1011 * // these two lines are equivalent, but the second approach is recommended 1012 * dateTime = thisPeriod.subtractFrom(dateTime); 1013 * dateTime = dateTime.minus(thisPeriod); 1014 * </pre> 1015 * <p> 1016 * The calculation will subtract the years, then months, then days, then nanos. 1017 * Only non-zero amounts will be subtracted. 1018 * If the date-time has a calendar system with a fixed number of months in a 1019 * year, then the years and months will be combined before being subtracted. 1020 * <p> 1021 * This instance is immutable and unaffected by this method call. 1022 * 1023 * @param temporal the temporal object to adjust, not null 1024 * @return an object of the same type with the adjustment made, not null 1025 * @throws DateTimeException if unable to subtract 1026 * @throws ArithmeticException if numeric overflow occurs 1027 */ 1028 @Override 1029 public Temporal subtractFrom(Temporal temporal) { 1030 Objects.requireNonNull(temporal, "temporal"); 1031 if ((years | months) != 0) { 1032 ValueRange startRange = Chrono.from(temporal).range(MONTH_OF_YEAR); 1033 if (startRange.isFixed() && startRange.isIntValue()) { 1034 long monthCount = startRange.getMaximum() - startRange.getMinimum() + 1; 1035 temporal = temporal.minus(years * monthCount + months, MONTHS); 1036 } else { 1037 if (years != 0) { 1038 temporal = temporal.minus(years, YEARS); 1039 } 1040 if (months != 0) { 1041 temporal = temporal.minus(months, MONTHS); 1042 } 1043 } 1044 } 1045 if (days != 0) { 1046 temporal = temporal.minus(days, DAYS); 1047 } 1048 if (nanos != 0) { 1049 temporal = temporal.minus(nanos, NANOS); 1050 } 1051 return temporal; 1052 } 1053 1054 //----------------------------------------------------------------------- 1055 /** 1056 * Converts this period to one that only has time units. 1057 * <p> 1058 * The resulting period will have the same time units as this period 1059 * but the date units will all be zero. No normalization occurs in the calculation. 1060 * For example, a period of {@code P1Y3MT12H} will be converted to {@code PT12H}. 1061 * <p> 1062 * This instance is immutable and unaffected by this method call. 1063 * 1064 * @return a {@code Period} based on this period with the date units set to zero, not null 1065 */ 1066 public Period toTimeOnly() { 1067 if ((years | months | days) == 0) { 1068 return this; 1069 } 1070 return create(0, 0, 0, nanos); 1071 } 1072 1073 //------------------------------------------------------------------------- 1074 /** 1075 * Calculates the duration of this period. 1076 * <p> 1077 * The calculation uses the hours, minutes, seconds and nanoseconds fields. 1078 * If years, months or days are present an exception is thrown. 1079 * See {@link #toTimeOnly()} for a way to remove the date units and 1080 * {@link #normalizedDaysToHours()} for a way to convert days to hours. 1081 * 1082 * @return a {@code Duration} equivalent to this period, not null 1083 * @throws DateTimeException if the period cannot be converted as it contains years, months or days 1084 */ 1085 public Duration toDuration() { 1086 if ((years | months | days) != 0) { 1087 throw new DateTimeException("Unable to convert period to duration as years/months/days are present: " + this); 1088 } 1089 return Duration.ofNanos(nanos); 1090 } 1091 1092 //----------------------------------------------------------------------- 1093 /** 1094 * Checks if this period is equal to another period. 1095 * <p> 1096 * The comparison is based on the amounts held in the period. 1097 * To be equal, the years, months, days and normalized time fields must be equal. 1098 * 1099 * @param obj the object to check, null returns false 1100 * @return true if this is equal to the other period 1101 */ 1102 @Override 1103 public boolean equals(Object obj) { 1104 if (this == obj) { 1105 return true; 1106 } 1107 if (obj instanceof Period) { 1108 Period other = (Period) obj; 1109 return years == other.years && months == other.months && 1110 days == other.days && nanos == other.nanos; 1111 } 1112 return false; 1113 } 1114 1115 /** 1116 * A hash code for this period. 1117 * 1118 * @return a suitable hash code 1119 */ 1120 @Override 1121 public int hashCode() { 1122 // ordered such that overflow from one field doesn't immediately affect the next field 1123 return ((years << 27) | (years >>> 5)) ^ 1124 ((days << 21) | (days >>> 11)) ^ 1125 ((months << 17) | (months >>> 15)) ^ 1126 ((int) (nanos ^ (nanos >>> 32))); 1127 } 1128 1129 //----------------------------------------------------------------------- 1130 /** 1131 * Outputs this period as a {@code String}, such as {@code P6Y3M1DT12H}. 1132 * <p> 1133 * The output will be in the ISO-8601 period format. 1134 * 1135 * @return a string representation of this period, not null 1136 */ 1137 @Override 1138 public String toString() { 1139 if (this == ZERO) { 1140 return "PT0S"; 1141 } else { 1142 StringBuilder buf = new StringBuilder(); 1143 buf.append('P'); 1144 if (years != 0) { 1145 buf.append(years).append('Y'); 1146 } 1147 if (months != 0) { 1148 buf.append(months).append('M'); 1149 } 1150 if (days != 0) { 1151 buf.append(days).append('D'); 1152 } 1153 if (nanos != 0) { 1154 buf.append('T'); 1155 if (getHours() != 0) { 1156 buf.append(getHours()).append('H'); 1157 } 1158 if (getMinutes() != 0) { 1159 buf.append(getMinutes()).append('M'); 1160 } 1161 int secondPart = getSeconds(); 1162 int nanoPart = getNanos(); 1163 int secsNanosOr = secondPart | nanoPart; 1164 if (secsNanosOr != 0) { // if either non-zero 1165 if ((secsNanosOr & Integer.MIN_VALUE) != 0) { // if either less than zero 1166 buf.append('-'); 1167 secondPart = Math.abs(secondPart); 1168 nanoPart = Math.abs(nanoPart); 1169 } 1170 buf.append(secondPart); 1171 if (nanoPart != 0) { 1172 int dotPos = buf.length(); 1173 nanoPart += 1000_000_000; 1174 while (nanoPart % 10 == 0) { 1175 nanoPart /= 10; 1176 } 1177 buf.append(nanoPart); 1178 buf.setCharAt(dotPos, '.'); 1179 } 1180 buf.append('S'); 1181 } 1182 } 1183 return buf.toString(); 1184 } 1185 } 1186 1187 }