src/share/classes/java/time/Period.java

Print this page




  62 package java.time;
  63 
  64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  65 import static java.time.temporal.ChronoUnit.DAYS;
  66 import static java.time.temporal.ChronoUnit.MONTHS;
  67 import static java.time.temporal.ChronoUnit.YEARS;
  68 
  69 import java.io.DataInput;
  70 import java.io.DataOutput;
  71 import java.io.IOException;
  72 import java.io.InvalidObjectException;
  73 import java.io.ObjectStreamException;
  74 import java.io.Serializable;
  75 import java.time.chrono.ChronoLocalDate;
  76 import java.time.chrono.Chronology;
  77 import java.time.format.DateTimeParseException;
  78 import java.time.temporal.ChronoUnit;
  79 import java.time.temporal.Temporal;
  80 import java.time.temporal.TemporalAmount;
  81 import java.time.temporal.TemporalUnit;

  82 import java.time.temporal.ValueRange;
  83 import java.util.Arrays;
  84 import java.util.Collections;
  85 import java.util.List;
  86 import java.util.Objects;
  87 import java.util.regex.Matcher;
  88 import java.util.regex.Pattern;
  89 
  90 /**
  91  * A date-based amount of time, such as '2 years, 3 months and 4 days'.
  92  * <p>
  93  * This class models a quantity or amount of time in terms of years, months and days.
  94  * See {@link Duration} for the time-based equivalent to this class.
  95  * <p>
  96  * Durations and period differ in their treatment of daylight savings time
  97  * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact
  98  * number of seconds, thus a duration of one day is always exactly 24 hours.
  99  * By contrast, a {@code Period} will add a conceptual day, trying to maintain
 100  * the local time.
 101  * <p>


 198         return create(0, 0, days);
 199     }
 200 
 201     //-----------------------------------------------------------------------
 202     /**
 203      * Obtains a {@code Period} representing a number of years, months and days.
 204      * <p>
 205      * This creates an instance based on years, months and days.
 206      *
 207      * @param years  the amount of years, may be negative
 208      * @param months  the amount of months, may be negative
 209      * @param days  the amount of days, may be negative
 210      * @return the period of years, months and days, not null
 211      */
 212     public static Period of(int years, int months, int days) {
 213         return create(years, months, days);
 214     }
 215 
 216     //-----------------------------------------------------------------------
 217     /**
 218      * Obtains a {@code Period} consisting of the number of years, months,
 219      * and days between two dates.
 220      * <p>
 221      * The start date is included, but the end date is not.
 222      * The period is calculated by removing complete months, then calculating
 223      * the remaining number of days, adjusting to ensure that both have the same sign.
 224      * The number of months is then split into years and months based on a 12 month year.
 225      * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
 226      * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
 227      * <p>
 228      * The result of this method can be a negative period if the end is before the start.
 229      * The negative sign will be the same in each of year, month and day.
 230      *
 231      * @param startDate  the start date, inclusive, not null
 232      * @param endDate  the end date, exclusive, not null
 233      * @return the period between this date and the end date, not null
 234      * @see ChronoLocalDate#periodUntil(ChronoLocalDate)
 235      */
 236     public static Period between(LocalDate startDate, LocalDate endDate) {
 237         return startDate.periodUntil(endDate);















 238     }
 239 
 240     //-----------------------------------------------------------------------
 241     /**
 242      * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
 243      * <p>
 244      * This will parse the string produced by {@code toString()} which is
 245      * based on the ISO-8601 period format {@code PnYnMnD}.
 246      * <p>
 247      * The string starts with an optional sign, denoted by the ASCII negative
 248      * or positive symbol. If negative, the whole period is negated.
 249      * The ASCII letter "P" is next in upper or lower case.
 250      * There are then three sections, each consisting of a number and a suffix.
 251      * At least one of the three sections must be present.
 252      * The sections have suffixes in ASCII of "Y", "M" and "D" for
 253      * years, months and days, accepted in upper or lower case.
 254      * The suffixes must occur in order.
 255      * The number part of each section must consist of ASCII digits.
 256      * The number may be prefixed by the ASCII negative or positive symbol.
 257      * The number must parse to an {@code int}.


 281                 }
 282             }
 283         }
 284         throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
 285     }
 286 
 287     private static int parseNumber(CharSequence text, String str, int negate) {
 288         if (str == null) {
 289             return 0;
 290         }
 291         int val = Integer.parseInt(str);
 292         try {
 293             return Math.multiplyExact(val, negate);
 294         } catch (ArithmeticException ex) {
 295             throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
 296         }
 297     }
 298 
 299     //-----------------------------------------------------------------------
 300     /**
























 301      * Creates an instance.
 302      *
 303      * @param years  the amount
 304      * @param months  the amount
 305      * @param days  the amount
 306      */
 307     private static Period create(int years, int months, int days) {
 308         if ((years | months | days) == 0) {
 309             return ZERO;
 310         }
 311         return new Period(years, months, days);
 312     }
 313 
 314     /**
 315      * Constructor.
 316      *
 317      * @param years  the amount
 318      * @param months  the amount
 319      * @param days  the amount
 320      */
 321     private Period(int years, int months, int days) {
 322         this.years = years;
 323         this.months = months;
 324         this.days = days;
 325     }
 326 
 327     //-----------------------------------------------------------------------
 328     /**
 329      * Gets the value of the requested unit.
 330      * <p>
 331      * This returns a value for each of the three supported units,
 332      * {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and
 333      * {@link ChronoUnit#DAYS DAYS}.
 334      * All other units throw an exception.
 335      *
 336      * @param unit the {@code TemporalUnit} for which to return the value
 337      * @return the long value of the unit
 338      * @throws DateTimeException if the unit is not supported

 339      */
 340     @Override
 341     public long get(TemporalUnit unit) {
 342         if (unit == ChronoUnit.YEARS) {
 343             return getYears();
 344         } else if (unit == ChronoUnit.MONTHS) {
 345             return getMonths();
 346         } else if (unit == ChronoUnit.DAYS) {
 347             return getDays();
 348         } else {
 349             throw new DateTimeException("Unsupported unit: " + unit.getName());
 350         }
 351     }
 352 
 353     /**
 354      * Gets the set of units supported by this period.
 355      * <p>
 356      * The supported units are {@link ChronoUnit#YEARS YEARS},
 357      * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
 358      * They are returned in the order years, months, days.
 359      * <p>
 360      * This set can be used in conjunction with {@link #get(TemporalUnit)}
 361      * to access the entire state of the period.
 362      *
 363      * @return a list containing the years, months and days units, not null
 364      */
 365     @Override
 366     public List<TemporalUnit> getUnits() {
 367         return SUPPORTED_UNITS;
 368     }
 369 


 482      * <p>
 483      * This sets the amount of the days unit in a copy of this period.
 484      * The years and months units are unaffected.
 485      * <p>
 486      * This instance is immutable and unaffected by this method call.
 487      *
 488      * @param days  the days to represent, may be negative
 489      * @return a {@code Period} based on this period with the requested days, not null
 490      */
 491     public Period withDays(int days) {
 492         if (days == this.days) {
 493             return this;
 494         }
 495         return create(years, months, days);
 496     }
 497 
 498     //-----------------------------------------------------------------------
 499     /**
 500      * Returns a copy of this period with the specified period added.
 501      * <p>
 502      * This operates separately on the years, months, days and the normalized time.
 503      * There is no further normalization beyond the normalized time.
 504      * <p>
 505      * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"
 506      * returns "3 years, 8 months and 5 days".
 507      * <p>
 508      * This instance is immutable and unaffected by this method call.
 509      *
 510      * @param amountToAdd  the period to add, not null
 511      * @return a {@code Period} based on this period with the requested period added, not null
 512      * @throws ArithmeticException if numeric overflow occurs
 513      */
 514     public Period plus(Period amountToAdd) {
 515         return create(
 516                 Math.addExact(years, amountToAdd.years),
 517                 Math.addExact(months, amountToAdd.months),
 518                 Math.addExact(days, amountToAdd.days));
 519     }
 520 
 521     /**
 522      * Returns a copy of this period with the specified years added.
 523      * <p>


 565      * The years and months units are unaffected.
 566      * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
 567      * <p>
 568      * This instance is immutable and unaffected by this method call.
 569      *
 570      * @param daysToAdd  the days to add, positive or negative
 571      * @return a {@code Period} based on this period with the specified days added, not null
 572      * @throws ArithmeticException if numeric overflow occurs
 573      */
 574     public Period plusDays(long daysToAdd) {
 575         if (daysToAdd == 0) {
 576             return this;
 577         }
 578         return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
 579     }
 580 
 581     //-----------------------------------------------------------------------
 582     /**
 583      * Returns a copy of this period with the specified period subtracted.
 584      * <p>
 585      * This operates separately on the years, months, days and the normalized time.
 586      * There is no further normalization beyond the normalized time.
 587      * <p>
 588      * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"
 589      * returns "-1 years, 4 months and 1 day".
 590      * <p>
 591      * This instance is immutable and unaffected by this method call.
 592      *
 593      * @param amountToSubtract  the period to subtract, not null
 594      * @return a {@code Period} based on this period with the requested period subtracted, not null
 595      * @throws ArithmeticException if numeric overflow occurs
 596      */
 597     public Period minus(Period amountToSubtract) {
 598         return create(
 599                 Math.subtractExact(years, amountToSubtract.years),
 600                 Math.subtractExact(months, amountToSubtract.months),
 601                 Math.subtractExact(days, amountToSubtract.days));
 602     }
 603 
 604     /**
 605      * Returns a copy of this period with the specified years subtracted.
 606      * <p>


 828                     temporal = temporal.minus(years, YEARS);
 829                 }
 830                 if (months != 0) {
 831                     temporal = temporal.minus(months, MONTHS);
 832                 }
 833             }
 834         }
 835         if (days != 0) {
 836             temporal = temporal.minus(days, DAYS);
 837         }
 838         return temporal;
 839     }
 840 
 841     /**
 842      * Calculates the range of months based on the temporal.
 843      *
 844      * @param temporal  the temporal, not null
 845      * @return the month range, negative if not fixed range
 846      */
 847     private long monthRange(Temporal temporal) {

 848         ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR);
 849         if (startRange.isFixed() && startRange.isIntValue()) {
 850             return startRange.getMaximum() - startRange.getMinimum() + 1;
 851         }

 852         return -1;
 853     }
 854 
 855     //-----------------------------------------------------------------------
 856     /**
 857      * Checks if this period is equal to another period.
 858      * <p>
 859      * The comparison is based on the amounts held in the period.
 860      * To be equal, the years, months and days units must be individually equal.
 861      * Note that this means that a period of "15 Months" is not equal to a period
 862      * of "1 Year and 3 Months".
 863      *
 864      * @param obj  the object to check, null returns false
 865      * @return true if this is equal to the other period
 866      */
 867     @Override
 868     public boolean equals(Object obj) {
 869         if (this == obj) {
 870             return true;
 871         }




  62 package java.time;
  63 
  64 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  65 import static java.time.temporal.ChronoUnit.DAYS;
  66 import static java.time.temporal.ChronoUnit.MONTHS;
  67 import static java.time.temporal.ChronoUnit.YEARS;
  68 
  69 import java.io.DataInput;
  70 import java.io.DataOutput;
  71 import java.io.IOException;
  72 import java.io.InvalidObjectException;
  73 import java.io.ObjectStreamException;
  74 import java.io.Serializable;
  75 import java.time.chrono.ChronoLocalDate;
  76 import java.time.chrono.Chronology;
  77 import java.time.format.DateTimeParseException;
  78 import java.time.temporal.ChronoUnit;
  79 import java.time.temporal.Temporal;
  80 import java.time.temporal.TemporalAmount;
  81 import java.time.temporal.TemporalUnit;
  82 import java.time.temporal.UnsupportedTemporalTypeException;
  83 import java.time.temporal.ValueRange;
  84 import java.util.Arrays;
  85 import java.util.Collections;
  86 import java.util.List;
  87 import java.util.Objects;
  88 import java.util.regex.Matcher;
  89 import java.util.regex.Pattern;
  90 
  91 /**
  92  * A date-based amount of time, such as '2 years, 3 months and 4 days'.
  93  * <p>
  94  * This class models a quantity or amount of time in terms of years, months and days.
  95  * See {@link Duration} for the time-based equivalent to this class.
  96  * <p>
  97  * Durations and period differ in their treatment of daylight savings time
  98  * when added to {@link ZonedDateTime}. A {@code Duration} will add an exact
  99  * number of seconds, thus a duration of one day is always exactly 24 hours.
 100  * By contrast, a {@code Period} will add a conceptual day, trying to maintain
 101  * the local time.
 102  * <p>


 199         return create(0, 0, days);
 200     }
 201 
 202     //-----------------------------------------------------------------------
 203     /**
 204      * Obtains a {@code Period} representing a number of years, months and days.
 205      * <p>
 206      * This creates an instance based on years, months and days.
 207      *
 208      * @param years  the amount of years, may be negative
 209      * @param months  the amount of months, may be negative
 210      * @param days  the amount of days, may be negative
 211      * @return the period of years, months and days, not null
 212      */
 213     public static Period of(int years, int months, int days) {
 214         return create(years, months, days);
 215     }
 216 
 217     //-----------------------------------------------------------------------
 218     /**
 219      * Obtains an instance of {@code Period} from a temporal amount.

 220      * <p>
 221      * This obtains a period based on the specified amount.
 222      * A {@code TemporalAmount} represents an  amount of time, which may be
 223      * date-based or time-based, which this factory extracts to a period.
 224      * <p>
 225      * The conversion loops around the set of units from the amount and uses
 226      * the {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS}
 227      * and {@link ChronoUnit#DAYS DAYS} units to create a period.
 228      * If any other units are found then an exception is thrown.
 229      *
 230      * @param amount  the temporal amount to convert, not null
 231      * @return the equivalent period, not null
 232      * @throws DateTimeException if unable to convert to a {@code Period}
 233      * @throws ArithmeticException if the amount of years, months or days exceeds an int
 234      */
 235     public static Period from(TemporalAmount amount) {
 236         Objects.requireNonNull(amount, "amount");
 237         int years = 0;
 238         int months = 0;
 239         int days = 0;
 240         for (TemporalUnit unit : amount.getUnits()) {
 241             long unitAmount = amount.get(unit);
 242             if (unit == ChronoUnit.YEARS) {
 243                 years = Math.toIntExact(unitAmount);
 244             } else if (unit == ChronoUnit.MONTHS) {
 245                 months = Math.toIntExact(unitAmount);
 246             } else if (unit == ChronoUnit.DAYS) {
 247                 days = Math.toIntExact(unitAmount);
 248             } else {
 249                 throw new DateTimeException("Unit must be Years, Months or Days, but was " + unit);
 250             }
 251         }
 252         return create(years, months, days);
 253     }
 254 
 255     //-----------------------------------------------------------------------
 256     /**
 257      * Obtains a {@code Period} from a text string such as {@code PnYnMnD}.
 258      * <p>
 259      * This will parse the string produced by {@code toString()} which is
 260      * based on the ISO-8601 period format {@code PnYnMnD}.
 261      * <p>
 262      * The string starts with an optional sign, denoted by the ASCII negative
 263      * or positive symbol. If negative, the whole period is negated.
 264      * The ASCII letter "P" is next in upper or lower case.
 265      * There are then three sections, each consisting of a number and a suffix.
 266      * At least one of the three sections must be present.
 267      * The sections have suffixes in ASCII of "Y", "M" and "D" for
 268      * years, months and days, accepted in upper or lower case.
 269      * The suffixes must occur in order.
 270      * The number part of each section must consist of ASCII digits.
 271      * The number may be prefixed by the ASCII negative or positive symbol.
 272      * The number must parse to an {@code int}.


 296                 }
 297             }
 298         }
 299         throw new DateTimeParseException("Text cannot be parsed to a Period", text, 0);
 300     }
 301 
 302     private static int parseNumber(CharSequence text, String str, int negate) {
 303         if (str == null) {
 304             return 0;
 305         }
 306         int val = Integer.parseInt(str);
 307         try {
 308             return Math.multiplyExact(val, negate);
 309         } catch (ArithmeticException ex) {
 310             throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Period", text, 0).initCause(ex);
 311         }
 312     }
 313 
 314     //-----------------------------------------------------------------------
 315     /**
 316      * Obtains a {@code Period} consisting of the number of years, months,
 317      * and days between two dates.
 318      * <p>
 319      * The start date is included, but the end date is not.
 320      * The period is calculated by removing complete months, then calculating
 321      * the remaining number of days, adjusting to ensure that both have the same sign.
 322      * The number of months is then split into years and months based on a 12 month year.
 323      * A month is considered if the end day-of-month is greater than or equal to the start day-of-month.
 324      * For example, from {@code 2010-01-15} to {@code 2011-03-18} is one year, two months and three days.
 325      * <p>
 326      * The result of this method can be a negative period if the end is before the start.
 327      * The negative sign will be the same in each of year, month and day.
 328      *
 329      * @param startDate  the start date, inclusive, not null
 330      * @param endDate  the end date, exclusive, not null
 331      * @return the period between this date and the end date, not null
 332      * @see ChronoLocalDate#periodUntil(ChronoLocalDate)
 333      */
 334     public static Period between(LocalDate startDate, LocalDate endDate) {
 335         return startDate.periodUntil(endDate);
 336     }
 337 
 338     //-----------------------------------------------------------------------
 339     /**
 340      * Creates an instance.
 341      *
 342      * @param years  the amount
 343      * @param months  the amount
 344      * @param days  the amount
 345      */
 346     private static Period create(int years, int months, int days) {
 347         if ((years | months | days) == 0) {
 348             return ZERO;
 349         }
 350         return new Period(years, months, days);
 351     }
 352 
 353     /**
 354      * Constructor.
 355      *
 356      * @param years  the amount
 357      * @param months  the amount
 358      * @param days  the amount
 359      */
 360     private Period(int years, int months, int days) {
 361         this.years = years;
 362         this.months = months;
 363         this.days = days;
 364     }
 365 
 366     //-----------------------------------------------------------------------
 367     /**
 368      * Gets the value of the requested unit.
 369      * <p>
 370      * This returns a value for each of the three supported units,
 371      * {@link ChronoUnit#YEARS YEARS}, {@link ChronoUnit#MONTHS MONTHS} and
 372      * {@link ChronoUnit#DAYS DAYS}.
 373      * All other units throw an exception.
 374      *
 375      * @param unit the {@code TemporalUnit} for which to return the value
 376      * @return the long value of the unit
 377      * @throws DateTimeException if the unit is not supported
 378      * @throws UnsupportedTemporalTypeException if the unit is not supported
 379      */
 380     @Override
 381     public long get(TemporalUnit unit) {
 382         if (unit == ChronoUnit.YEARS) {
 383             return getYears();
 384         } else if (unit == ChronoUnit.MONTHS) {
 385             return getMonths();
 386         } else if (unit == ChronoUnit.DAYS) {
 387             return getDays();
 388         } else {
 389             throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName());
 390         }
 391     }
 392 
 393     /**
 394      * Gets the set of units supported by this period.
 395      * <p>
 396      * The supported units are {@link ChronoUnit#YEARS YEARS},
 397      * {@link ChronoUnit#MONTHS MONTHS} and {@link ChronoUnit#DAYS DAYS}.
 398      * They are returned in the order years, months, days.
 399      * <p>
 400      * This set can be used in conjunction with {@link #get(TemporalUnit)}
 401      * to access the entire state of the period.
 402      *
 403      * @return a list containing the years, months and days units, not null
 404      */
 405     @Override
 406     public List<TemporalUnit> getUnits() {
 407         return SUPPORTED_UNITS;
 408     }
 409 


 522      * <p>
 523      * This sets the amount of the days unit in a copy of this period.
 524      * The years and months units are unaffected.
 525      * <p>
 526      * This instance is immutable and unaffected by this method call.
 527      *
 528      * @param days  the days to represent, may be negative
 529      * @return a {@code Period} based on this period with the requested days, not null
 530      */
 531     public Period withDays(int days) {
 532         if (days == this.days) {
 533             return this;
 534         }
 535         return create(years, months, days);
 536     }
 537 
 538     //-----------------------------------------------------------------------
 539     /**
 540      * Returns a copy of this period with the specified period added.
 541      * <p>
 542      * This operates separately on the years, months and days.

 543      * <p>
 544      * For example, "1 year, 6 months and 3 days" plus "2 years, 2 months and 2 days"
 545      * returns "3 years, 8 months and 5 days".
 546      * <p>
 547      * This instance is immutable and unaffected by this method call.
 548      *
 549      * @param amountToAdd  the period to add, not null
 550      * @return a {@code Period} based on this period with the requested period added, not null
 551      * @throws ArithmeticException if numeric overflow occurs
 552      */
 553     public Period plus(Period amountToAdd) {
 554         return create(
 555                 Math.addExact(years, amountToAdd.years),
 556                 Math.addExact(months, amountToAdd.months),
 557                 Math.addExact(days, amountToAdd.days));
 558     }
 559 
 560     /**
 561      * Returns a copy of this period with the specified years added.
 562      * <p>


 604      * The years and months units are unaffected.
 605      * For example, "1 year, 6 months and 3 days" plus 2 days returns "1 year, 6 months and 5 days".
 606      * <p>
 607      * This instance is immutable and unaffected by this method call.
 608      *
 609      * @param daysToAdd  the days to add, positive or negative
 610      * @return a {@code Period} based on this period with the specified days added, not null
 611      * @throws ArithmeticException if numeric overflow occurs
 612      */
 613     public Period plusDays(long daysToAdd) {
 614         if (daysToAdd == 0) {
 615             return this;
 616         }
 617         return create(years, months, Math.toIntExact(Math.addExact(days, daysToAdd)));
 618     }
 619 
 620     //-----------------------------------------------------------------------
 621     /**
 622      * Returns a copy of this period with the specified period subtracted.
 623      * <p>
 624      * This operates separately on the years, months and days.

 625      * <p>
 626      * For example, "1 year, 6 months and 3 days" minus "2 years, 2 months and 2 days"
 627      * returns "-1 years, 4 months and 1 day".
 628      * <p>
 629      * This instance is immutable and unaffected by this method call.
 630      *
 631      * @param amountToSubtract  the period to subtract, not null
 632      * @return a {@code Period} based on this period with the requested period subtracted, not null
 633      * @throws ArithmeticException if numeric overflow occurs
 634      */
 635     public Period minus(Period amountToSubtract) {
 636         return create(
 637                 Math.subtractExact(years, amountToSubtract.years),
 638                 Math.subtractExact(months, amountToSubtract.months),
 639                 Math.subtractExact(days, amountToSubtract.days));
 640     }
 641 
 642     /**
 643      * Returns a copy of this period with the specified years subtracted.
 644      * <p>


 866                     temporal = temporal.minus(years, YEARS);
 867                 }
 868                 if (months != 0) {
 869                     temporal = temporal.minus(months, MONTHS);
 870                 }
 871             }
 872         }
 873         if (days != 0) {
 874             temporal = temporal.minus(days, DAYS);
 875         }
 876         return temporal;
 877     }
 878 
 879     /**
 880      * Calculates the range of months based on the temporal.
 881      *
 882      * @param temporal  the temporal, not null
 883      * @return the month range, negative if not fixed range
 884      */
 885     private long monthRange(Temporal temporal) {
 886         if (temporal.isSupported(MONTH_OF_YEAR)) {
 887             ValueRange startRange = Chronology.from(temporal).range(MONTH_OF_YEAR);
 888             if (startRange.isFixed() && startRange.isIntValue()) {
 889                 return startRange.getMaximum() - startRange.getMinimum() + 1;
 890             }
 891         }
 892         return -1;
 893     }
 894 
 895     //-----------------------------------------------------------------------
 896     /**
 897      * Checks if this period is equal to another period.
 898      * <p>
 899      * The comparison is based on the amounts held in the period.
 900      * To be equal, the years, months and days units must be individually equal.
 901      * Note that this means that a period of "15 Months" is not equal to a period
 902      * of "1 Year and 3 Months".
 903      *
 904      * @param obj  the object to check, null returns false
 905      * @return true if this is equal to the other period
 906      */
 907     @Override
 908     public boolean equals(Object obj) {
 909         if (this == obj) {
 910             return true;
 911         }