Пример #1
0
 /**
  * Obtains a {@code PeriodFields} from an array of single-unit periods.
  *
  * <p>The period fields must all have different units.
  *
  * @param periods the array of single-unit periods, not null
  * @return the {@code PeriodFields} instance, not null
  * @throws IllegalArgumentException if the same period unit occurs twice
  */
 public static PeriodFields of(PeriodField... periods) {
   checkNotNull(periods, "PeriodField array must not be null");
   TreeMap<PeriodUnit, PeriodField> internalMap = createMap();
   for (PeriodField period : periods) {
     checkNotNull(period, "PeriodField array must not contain null");
     if (internalMap.put(period.getUnit(), period) != null) {
       throw new IllegalArgumentException("PeriodField array contains the same unit twice");
     }
   }
   return create(internalMap);
 }
Пример #2
0
 /**
  * Returns a copy of this period with only those units that can be converted to the specified
  * units.
  *
  * <p>This method will return a new period where every field can be converted to one of the
  * specified units. In the result, each of the retained periods will have the same amount as they
  * do in this period - no conversion or normalization occurs.
  *
  * <p>For example, if this period is '2 Days, 5 Hours, 7 Minutes' and the specified unit array
  * contains 'Seconds' then the output will be '5 Hours, 7 Minutes'. The 'Days' unit is not
  * retained as it cannot be converted to 'Seconds'.
  *
  * <p>This instance is immutable and unaffected by this method call.
  *
  * @param units the units to retain, not altered, not null, no nulls
  * @return a {@code PeriodFields} based on this period with the specified units retained, not null
  */
 public PeriodFields retainConvertible(PeriodUnit... units) {
   checkNotNull(units, "PeriodUnit array must not be null");
   TreeMap<PeriodUnit, PeriodField> copy = clonedMap();
   outer:
   for (Iterator<PeriodUnit> it = copy.keySet().iterator(); it.hasNext(); ) {
     PeriodUnit loopUnit = it.next();
     for (PeriodUnit unit : units) {
       checkNotNull(unit, "PeriodUnit array must not contain null");
       if (loopUnit.isConvertibleTo(unit)) {
         continue outer;
       }
     }
     it.remove();
   }
   return create(copy);
 }
Пример #3
0
 /**
  * Obtains a {@code PeriodFields} from a {@code Duration} based on the standard durations of
  * seconds and nanoseconds.
  *
  * <p>The conversion will create an instance with two units - the {@code ISOChronology} seconds
  * and nanoseconds units. This matches the {@link #toDuration()} method.
  *
  * @param duration the duration to create from, not null
  * @return the {@code PeriodFields} instance, not null
  */
 public static PeriodFields of(Duration duration) {
   checkNotNull(duration, "Duration must not be null");
   TreeMap<PeriodUnit, PeriodField> internalMap = createMap();
   internalMap.put(SECONDS, PeriodField.of(duration.getSeconds(), SECONDS));
   internalMap.put(NANOS, PeriodField.of(duration.getNanoOfSecond(), NANOS));
   return create(internalMap);
 }
Пример #4
0
 /**
  * Obtains a {@code PeriodFields} from an amount and unit, by extending any fractional remainder
  * onto smaller units.
  *
  * <p>The parameters represent the two parts of a phrase like 'one-and-a-half Hours'. The
  * fractional parts will be distributed into the smaller units, and rounded down when no smaller
  * units exist. If the {@code fractionalAmount} is negative, the amount of the biggest unit will
  * be negative, the rest will be positive.
  *
  * @param fractionalAmount the amount of create with, positive or negative
  * @param unit the period unit, not null
  * @return the {@code PeriodFields} instance, not null
  */
 public static PeriodFields of(double fractionalAmount, PeriodUnit unit) {
   checkNotNull(unit, "PeriodUnit must not be null");
   PeriodUnit currentUnit = unit;
   double fudge = 0.000000000000001d;
   TreeMap<PeriodUnit, PeriodField> internalMap = createMap();
   do {
     long floor = (long) Math.floor(fractionalAmount + fudge);
     if (floor != 0) {
       internalMap.put(currentUnit, PeriodField.of(floor, currentUnit));
     }
     double remainder = fractionalAmount - floor; // will be positive
     PeriodField nextEquivalent = currentUnit.getNextEquivalentPeriod();
     if (nextEquivalent != null) {
       currentUnit = nextEquivalent.getUnit();
       fractionalAmount = remainder * nextEquivalent.getAmount();
       fudge *= nextEquivalent.getAmount();
     } else {
       // No smaller units exist, we're done
       currentUnit = null;
     }
   } while (currentUnit != null && Math.abs(fractionalAmount) > fudge);
   if (internalMap.isEmpty()) {
     return of(0L, unit);
   } else {
     return create(internalMap);
   }
 }
Пример #5
0
 /**
  * Totals this period in terms of a single unit.
  *
  * <p>This will take each of the stored {@code PeriodField} instances and convert them to the
  * specified unit. The result will be the total of these converted periods.
  *
  * <p>For example, '3 Hours, 34 Minutes' can be totalled to minutes resulting in '214 Minutes'.
  *
  * @param unit the unit to total in, not null
  * @return a period equivalent to the total of this period in a single unit, not null
  * @throws CalendricalException if this period cannot be converted to the unit
  * @throws ArithmeticException if the calculation overflows
  */
 public PeriodField toTotal(PeriodUnit unit) {
   checkNotNull(unit, "PeriodUnit must not be null");
   PeriodField result = null;
   for (PeriodField period : unitFieldMap.values()) {
     period = period.toEquivalent(unit);
     result = (result != null ? result.plus(period) : period);
   }
   return result;
 }
Пример #6
0
 /**
  * Returns a copy of this period with the specified unit removed.
  *
  * <p>If this period already contains an amount for the unit then the amount is removed.
  * Otherwise, no action occurs.
  *
  * <p>This instance is immutable and unaffected by this method call.
  *
  * @param unit the unit to remove, not null
  * @return a {@code PeriodFields} based on this period with the specified unit removed, not null
  */
 public PeriodFields without(PeriodUnit unit) {
   checkNotNull(unit, "PeriodUnit must not be null");
   if (unitFieldMap.containsKey(unit) == false) {
     return this;
   }
   TreeMap<PeriodUnit, PeriodField> copy = clonedMap();
   copy.remove(unit);
   return create(copy);
 }
Пример #7
0
 /**
  * Returns a copy of this period with the specified units retained.
  *
  * <p>This method will return a new period that only has the specified units. All units not
  * present in the input will not be present in the result. In most cases, the result will not be
  * equivalent to this period.
  *
  * <p>This instance is immutable and unaffected by this method call.
  *
  * @param units the units to retain, not altered, not null, no nulls
  * @return a {@code PeriodFields} based on this period with the specified units retained, not null
  */
 public PeriodFields retain(PeriodUnit... units) {
   checkNotNull(units, "PeriodUnit array must not be null");
   TreeMap<PeriodUnit, PeriodField> copy = clonedMap();
   List<PeriodUnit> unitList = Arrays.asList(units);
   if (unitList.contains(null)) {
     throw new NullPointerException("PeriodUnit array must not contain null");
   }
   copy.keySet().retainAll(unitList);
   return create(copy);
 }
Пример #8
0
 /**
  * Returns a copy of this period with the specified period subtracted.
  *
  * <p>The result will contain the units and amounts from this period minus the specified unit and
  * amount. The specified unit will always be in the result even if the amount is zero.
  *
  * <p>This instance is immutable and unaffected by this method call.
  *
  * @param amount the amount to subtract, measured in the specified unit, positive or negative
  * @param unit the unit defining the amount, not null
  * @return a {@code PeriodFields} based on this period with the specified period subtracted, not
  *     null
  * @throws ArithmeticException if the calculation overflows
  */
 public PeriodFields minus(long amount, PeriodUnit unit) {
   checkNotNull(unit, "PeiodRule must not be null");
   if (amount == 0 && contains(unit)) {
     return this;
   }
   TreeMap<PeriodUnit, PeriodField> copy = clonedMap();
   PeriodField old = copy.get(unit);
   copy.put(unit, old != null ? old.minus(amount) : PeriodField.of(amount, unit).negated());
   return create(copy);
 }
Пример #9
0
 /**
  * Converts this period to one containing only the units specified.
  *
  * <p>This converts this period to one measured in the specified units. It operates by looping
  * through the individual parts of this period, converting each in turn to one of the specified
  * units. These converted periods are then combined to form the result.
  *
  * <p>No normalization is performed on the result. This means that an amount in a smaller unit
  * cannot be converted to an amount in a larger unit. If you need to do this, call {@link
  * #normalized()} before calling this method.
  *
  * <p>This method uses {@link PeriodField#toEquivalent(PeriodUnit...)} and as such, it is
  * recommended to specify the units from largest to smallest.
  *
  * <p>For example, '3 Hours' can normally be converted to both minutes and seconds. If the units
  * array contains both 'Minutes' and 'Seconds', then the result will be measured in whichever is
  * first in the array.
  *
  * @param units the required unit array, not altered, not null, no nulls
  * @return a period equivalent to this period, not null
  * @throws CalendricalException if this period cannot be converted to any of the units
  * @throws ArithmeticException if the calculation overflows
  */
 public PeriodFields toEquivalent(PeriodUnit... units) {
   checkNotNull(units, "PeriodUnit array must not be null");
   TreeMap<PeriodUnit, PeriodField> map = createMap();
   for (PeriodField period : unitFieldMap.values()) {
     period = period.toEquivalent(units);
     PeriodField old = map.get(period.getUnit());
     period = (old != null ? old.plus(period) : period);
     map.put(period.getUnit(), period);
   }
   return (map.equals(unitFieldMap) ? this : create(map));
 }
Пример #10
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;
 }
Пример #11
0
 /**
  * Obtains a {@code PeriodFields} by totalling the amounts in a list of {@code PeriodProvider}
  * instances.
  *
  * <p>This method returns a period with all the unit-amount pairs from the providers totalled.
  * Thus a period of '2 Months and 5 Days' combined with a period of '7 Days and 21 Hours' will
  * yield a result of '2 Months, 12 Days and 21 Hours'.
  *
  * @param periodProviders the providers to total, not null
  * @return the {@code PeriodFields} instance, not null
  * @throws NullPointerException if any period provider is null or returns null
  */
 public static PeriodFields ofTotal(PeriodProvider... periodProviders) {
   checkNotNull(periodProviders, "PeriodProvider[] must not be null");
   if (periodProviders.length == 1) {
     return of(periodProviders[0]);
   }
   TreeMap<PeriodUnit, PeriodField> map = createMap();
   for (PeriodProvider periodProvider : periodProviders) {
     PeriodFields periods = of(periodProvider);
     for (PeriodField period : periods.unitFieldMap.values()) {
       PeriodField old = map.get(period.getUnit());
       period = (old != null ? old.plus(period) : period);
       map.put(period.getUnit(), period);
     }
   }
   return create(map);
 }
Пример #12
0
 /**
  * Returns a copy of this period with the modular division remainder of each field calculated with
  * respect to the specified period.
  *
  * <p>This method will return a new period where every field represents a period less than the
  * specified period. If this period contains a period that cannot be converted to the specified
  * unit then an exception is thrown.
  *
  * <p>For example, if this period is '37 Hours, 7 Minutes' and the specified period is '24 Hours'
  * then the output will be '13 Hours, 7 Minutes'.
  *
  * <p>This method requires this period to be convertible to the specified period. To ensure this
  * is true, call {@link #retainConvertible}, with the base unit of the period passed into this
  * method, before calling this method.
  *
  * <p>This instance is immutable and unaffected by this method call.
  *
  * @param period the period to calculate the remainder against, not null
  * @return a {@code PeriodFields} based on this period with the remainder, not null
  * @throws CalendricalException if any field cannot be converted to the unit of the period
  */
 public PeriodFields remainder(PeriodField period) {
   checkNotNull(period, "PeriodField must not be null");
   TreeMap<PeriodUnit, PeriodField> copy = createMap();
   for (PeriodField loopField : unitFieldMap.values()) {
     if (loopField.getUnit().equals(period.getUnit())) {
       copy.put(loopField.getUnit(), loopField.remainder(period.getAmount()));
     } else {
       for (PeriodField equivalent : period.getUnit().getEquivalentPeriods()) {
         if (loopField.getUnit().equals(equivalent.getUnit())) {
           copy.put(loopField.getUnit(), loopField.remainder(equivalent.getAmount()));
         }
       }
     }
   }
   if (copy.size() < size()) {
     throw new CalendricalException(
         "Unable to calculate remainder as some fields cannot be converted");
   }
   return create(copy);
 }
Пример #13
0
 /**
  * Obtains a {@code PeriodFields} from an amount and unit.
  *
  * <p>The parameters represent the two parts of a phrase like '6 Days'.
  *
  * @param amount the amount of create with, positive or negative
  * @param unit the period unit, not null
  * @return the {@code PeriodFields} instance, not null
  */
 public static PeriodFields of(long amount, PeriodUnit unit) {
   checkNotNull(unit, "PeriodUnit must not be null");
   TreeMap<PeriodUnit, PeriodField> internalMap = createMap();
   internalMap.put(unit, PeriodField.of(amount, unit));
   return create(internalMap);
 }
Пример #14
0
 /**
  * Gets the period for the specified unit.
  *
  * <p>This method allows the period to be queried by unit, like a map. If the unit is not found
  * then {@code null} is returned.
  *
  * @param unit the unit to query, not null
  * @return the period, null if no period stored for the unit
  */
 public PeriodField get(PeriodUnit unit) {
   checkNotNull(unit, "PeriodUnit must not be null");
   return unitFieldMap.get(unit);
 }
Пример #15
0
 /**
  * Obtains a {@code PeriodFields} from a {@code PeriodProvider}.
  *
  * <p>This method provides null-checking around {@link PeriodProvider#toPeriodFields()}.
  *
  * @param periodProvider the provider to create from, not null
  * @return the {@code PeriodFields} instance, not null
  * @throws NullPointerException if the period provider is null or returns null
  */
 public static PeriodFields of(PeriodProvider periodProvider) {
   checkNotNull(periodProvider, "PeriodProvider must not be null");
   PeriodFields result = periodProvider.toPeriodFields();
   checkNotNull(result, "PeriodProvider implementation must not return null");
   return result;
 }
Пример #16
0
 /**
  * Obtains a {@code PeriodFields} from a single-unit period.
  *
  * @param period the single-unit period, not null
  * @return the {@code PeriodFields} instance, not null
  */
 public static PeriodFields of(PeriodField period) {
   checkNotNull(period, "PeriodField must not be null");
   TreeMap<PeriodUnit, PeriodField> internalMap = createMap();
   internalMap.put(period.getUnit(), period);
   return create(internalMap);
 }