Example #1
0
 /**
  * Returns a copy of this period with the amounts normalized to the specified units.
  *
  * <p>This will normalize the period around the specified units. The calculation examines each
  * pair of units that have a fixed conversion factor. Each pair is adjusted so that the amount in
  * the smaller unit does not exceed the amount of the fixed conversion factor. At least one unit
  * must be specified for this method to have any effect.
  *
  * <p>For example, a period of '2 Decades, 2 Years, 17 Months' normalized using 'Years' and
  * 'Months' will return '23 Years, 5 Months'.
  *
  * <p>Any part of this period that cannot be converted to one of the specified units will be
  * unaffected in the result.
  *
  * <p>The result will always contain all the specified units, even if they are zero. The result
  * will be equivalent to this period.
  *
  * @param units the unit array to normalize to, not altered, not null, no nulls
  * @return a period equivalent to this period with the amounts normalized, not null
  * @throws ArithmeticException if the calculation overflows
  */
 public PeriodFields normalizedTo(PeriodUnit... units) {
   checkNotNull(units, "PeriodUnit array must not be null");
   PeriodFields result = this;
   TreeSet<PeriodUnit> targetUnits = new TreeSet<PeriodUnit>(Collections.reverseOrder());
   targetUnits.addAll(Arrays.asList(units));
   // normalize any fields in this period that have a unit greater than the
   // largest unit in the target set that can be normalized
   // eg. normalize Years-Months when the target set only contains Months
   for (PeriodUnit loopUnit : unitFieldMap.keySet()) {
     for (PeriodUnit targetUnit : targetUnits) {
       if (targetUnits.contains(loopUnit) == false) {
         PeriodField conversion = loopUnit.getEquivalentPeriod(targetUnit);
         if (conversion != null) {
           long amount = result.getAmount(loopUnit);
           result = result.plus(conversion.multipliedBy(amount)).without(loopUnit);
           break;
         }
       }
     }
   }
   // algorithm works by finding pairs to check
   // the first rule is to avoid numeric overflow wherever possible, such as when
   // Seconds and Minutes are both MAX_VALUE -
   // eg. the Hour-Minute and Hour-Second pair must be processed before the Minute-Second pair
   // the second rule is to handle the case where processing two pairs causes a knock on
   // effect on a pair that has already been processed according to the first rule -
   // eg. when the Hour-Minute pair is 59 and the Minute-Second pair is 61
   // this is achieved by restarting the whole algorithm (the process loop)
   for (boolean process = true; process; ) {
     process = false;
     for (PeriodUnit targetUnit : targetUnits) {
       for (PeriodUnit loopUnit : result.unitFieldMap.keySet()) {
         if (targetUnit.equals(loopUnit) == false) {
           PeriodField conversion = targetUnit.getEquivalentPeriod(loopUnit);
           if (conversion != null) {
             long convertAmount = conversion.getAmount();
             long amount = result.getAmount(loopUnit);
             if (amount >= convertAmount || amount <= -convertAmount) {
               result =
                   result
                       .with(amount % convertAmount, loopUnit)
                       .plus(amount / convertAmount, targetUnit);
               process = (units.length > 2); // need to re-check from start
             }
           }
         }
       }
       result = result.plus(0, targetUnit); // ensure unit is in the result
     }
   }
   return result;
 }
Example #2
0
 /**
  * Calculates the accurate duration of this period.
  *
  * <p>The conversion is based on the {@code ISOChronology} definition of the seconds and
  * nanoseconds units. If all the fields in this period can be converted to either seconds or
  * nanoseconds then the conversion will succeed, subject to calculation overflow. If any field
  * cannot be converted to these fields above then an exception is thrown.
  *
  * @return the duration of this period based on {@code ISOChronology} fields, not null
  * @throws CalendricalException if this period cannot be converted to an exact duration
  * @throws ArithmeticException if the calculation overflows
  */
 public Duration toDuration() {
   PeriodFields period = toEquivalent(SECONDS, NANOS);
   return Duration.ofSeconds(period.getAmount(SECONDS), period.getAmount(NANOS));
 }