Class AbstractDuration<U extends ChronoUnit>

java.lang.Object
net.time4j.engine.AbstractDuration<U>
All Implemented Interfaces:
TimeSpan<U>
Direct Known Subclasses:
Duration

public abstract class AbstractDuration<U extends ChronoUnit> extends Object implements TimeSpan<U>

Defines a timespan using the default algorithm of Time4J.

Dependent on the sign of the duration there are three cases:

  1. Empty duration => The method addTo() just yields a given time point unaffected.
  2. Positive duration => All contained time units will be added in the order from largest to smallest units. Convertible units will be consolidated in one step. The new time point is relative to given time point argument in the future.
  3. Negative duration => All contained time units will be subtracted in the reversed order from the smallest to the largest units. Convertible units will be consolidated in one step. The new time point is relative to given time point argument in the past.

Usually possible element overflows will be truncated such that the last valid time point will be determined. The rest of the discussion is about the gregorian calendar system and the addition of months and days, but is also applicable on other calendar systems. Examples in pseudo-code:

  • [2011-05-31] + [P4D] = [2011-06-04]
  • [2011-05-31] + [P9M] = [2012-02-29]
  • [2011-05-31] + [-P1M] = [2011-04-30]
  • [2011-05-30] + [P1M1D] = [2011-07-01]
  • [2011-05-31] + [P1M1D] = [2011-07-01]
  • [2011-07-01] + [-P1M1D] = [2011-05-30]
  • [2011-05-31] + [-P1Y1M1D] = [2010-04-30]

If the smallest existing time unit is used then following invariants hold for the addition of a duration and the delta between two time points. Let t1 and t2 be two time points with t1 <= t2:

  • FIRST INVARIANCE: t1.plus(t1.until(t2)).equals(t2) == true
  • SECOND INVARIANCE: t2.until(t1).equals(t1.until(t2).inverse()) == true

Following condition only holds if either the day-of-month of any involved date is smaller than 28 or if a reversible metric is used:

  • THIRD INVARIANCE: t2.minus(t1.until(t2)).equals(t1) == true

Note: Usually the third invariance is NOT valid. A counter example is given with following dates: {t1, t2} = {[2011-03-31], [2011-07-01]} => P3M1D (using the standard metric) because of [2011-07-01] - P3M1D = [2011-03-30]. Example for using a reversible metric:

     PlainDate d1 = PlainDate.of(2011, 3, 31);
     PlainDate d2 = PlainDate.of(2011, 7, 1);

     TimeMetric<CalendarUnit, Duration<CalendarUnit>> metric =
        Duration.inYearsMonthsDays().reversible();
     Duration<CalendarUnit> duration =
        metric.between(d1, d2); // P2M31D
     Duration<CalendarUnit> invDur =
        metric.between(d2, d1); // -P2M31D

     boolean firstInvariance = d1.plus(duration).equals(d2); // true
     boolean secondInvariance = invDur.equals(duration.inverse()); // true
     boolean thirdInvariance = d2.minus(duration).equals(d1); // true
 

About the mathematical background of specified algorithm: Note that the addition is not commutative, hence the order of addition steps will impact the result. For example a two-step-addition looks like:

  • [2011-05-30] + [P1M] + [P2D] = [2011-07-02]
  • [2011-05-30] + [P2D] + [P1M] = [2011-07-01]

In this context it is understandable that the order of addition steps is dependent on the sign of the duration. If the addition of a negative duration is interpreted as the reversal of the addition of a positive duration then following equivalent relation holds (paying attention to non-commutativity and given the side conditions to compute the duration without remainder completely and to consider a minus-operation as equalizing a plus-operation:

  • [t1] - [months] - [days] = [t2]
  • => [t1] - [months] - [days] + [days] = [t2] + [days]
  • => [t1] - [months] = [t2] + [days]
  • => [t1] - [months] + [months] = [t2] + [days] + [months]
  • => [t1] = [t2] + [days] + [months] // day-of-month <= 28

The permutation of addition steps is obvious. If Time4J had tried the alternative to first add the months and then the days even in case of a negative duration then we would have with

  • t1 = [2013-02-01]
  • t2 = [2013-03-31]
  • duration = t1.until(t2) = [P1M30D]

the situation that the mentioned third invariance would be violated even if the day of month is the first day of month: t2.minus(P1M30D) would not yield t1 but [2013-01-29]. Surely, the sign-dependent execution of addition steps cannot completely guarantee the third invariance in case of factory-created durations but it can guarantee it at least for all days in original date until the 28th of month.

Furthermore the specified algorithm ensures the second invariance Duration([t1, t2]) = -Duration([t2, t1]) which expresses a physical property of any duration as a directed temporal amount. The second invariance means that the sign of a duration can only qualify if the first time point is before the second time point or other way around. The sign must not qualify the always positive length of a duration itself however.

Author:
Meno Hochschild
See Also:
AbstractMetric, addTo(TimePoint), subtractFrom(TimePoint)
  • Constructor Details

    • AbstractDuration

      public AbstractDuration()
  • Method Details

    • contains

      public boolean contains(U unit)
      Description copied from interface: TimeSpan

      Queries if given time unit is part of this time span.

      By default the implementation uses following expression:

        for (Item<?> item : getTotalLength()) {
            if (item.getUnit().equals(unit)) {
                return (item.getAmount() > 0);
            }
        }
        return false;
       
      Specified by:
      contains in interface TimeSpan<U extends ChronoUnit>
      Parameters:
      unit - time unit to be asked (optional)
      Returns:
      true if exists else false
      See Also:
      getPartialAmount(U)
    • getPartialAmount

      public long getPartialAmount(U unit)
      Description copied from interface: TimeSpan

      Yields the partial amount associated with given time unit.

      The method returns 0 if this time span does not contain given time unit. In order to get the total length/amount of this time span users have to evaluate the method TimeSpan.getTotalLength() instead.

      Specified by:
      getPartialAmount in interface TimeSpan<U extends ChronoUnit>
      Parameters:
      unit - time unit (optional)
      Returns:
      amount as part of time span (>= 0)
    • inverse

      public abstract AbstractDuration<U> inverse()

      Creates a copy of this duration with the same amounts and units but the inversed sign.

      Returns:
      inverted duration
    • isPositive

      public boolean isPositive()
      Description copied from interface: TimeSpan

      Queries if this time span is positive.

      A time span is positive if it is neither empty nor negative.

      Specified by:
      isPositive in interface TimeSpan<U extends ChronoUnit>
      Returns:
      true if positive and not empty else false
      See Also:
      TimeSpan.isEmpty(), TimeSpan.isNegative()
    • isEmpty

      public boolean isEmpty()
      Description copied from interface: TimeSpan

      Queries if this time span is empty.

      Per definition an empty time span has no items with a partial amount different from 0.

      Specified by:
      isEmpty in interface TimeSpan<U extends ChronoUnit>
      Returns:
      true if empty else false
    • toString

      public String toString()

      Yields a canonical representation which optionally starts with the sign then continues with the letter "P" followed by a comma-separated sequence of duration items.

      Overrides:
      toString in class Object
      Returns:
      String
    • addTo

      public final <T extends TimePoint<? super U,​ T>> T addTo(T time)

      Adds this duration to given time point using the default algorithm.

      Specified by:
      addTo in interface TimeSpan<U extends ChronoUnit>
      Type Parameters:
      T - generic type of time point
      Parameters:
      time - reference time point to add this time span to
      Returns:
      new time point as result of addition
      See Also:
      TimeSpan.subtractFrom(TimePoint)
    • subtractFrom

      public final <T extends TimePoint<? super U,​ T>> T subtractFrom(T time)

      Subtracts this duration from given time point using the default algorithm.

      Specified by:
      subtractFrom in interface TimeSpan<U extends ChronoUnit>
      Type Parameters:
      T - generic type of time point
      Parameters:
      time - reference time point to subtract this time span from
      Returns:
      new time point as result of subtraction
      See Also:
      TimeSpan.addTo(TimePoint)