net.time4j.engine.TimePoint Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2015 Meno Hochschild,
* -----------------------------------------------------------------------
* This file (TimePoint.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see .
* -----------------------------------------------------------------------
*/
package net.time4j.engine;
import net.time4j.base.MathUtils;
import java.io.Serializable;
/**
* Represents an immutable time point along a time axis which is directed
* into the future.
*
* Display and change chronological element values
*
* The time point consists of chronological elements. This base class
* delegates the time arithmetic to the associated time axis respective to
* the underlying rules of elements and units. However, any concrete subclass
* is required to define the state and reflect it in all {@code get()}-methods
* and also to specify the serialization behaviour.
*
* Element values can only be changed by creating a new immutable copy
* of the original instance. This is done via all {@code with()}-methods.
*
* Time axis
*
* If the referenced time axis is the UTC-timeline (that is a time point
* is defined relative to the start of UTC epoch - see
* package summary) then any implementation
* must also implement the interface {@link net.time4j.scale.UniversalTime}.
* In every other case we have a local time axis. All time units are to be
* defined referencing the time axis. For example, second units are interpreted
* as local UT1-seconds on a local timestamp but on a {@code UniversalTime}
* before 1972 as global UT1-seconds and after 1972 as atomic SI-seconds.
* Hence Time4J has even defined different second units in the main package.
* Applications should therefore take much care if they transform a duration
* from one time axis to another one.
*
* Sorting
*
* Unless explicitly stated otherwise sorting of time points is always
* in strict temporal order and consistent with {@code equals()}. In case
* of doubt the documentation of the subclass is leading. Alternatively,
* subclasses are free to implement the interface {@code Temporal} to
* enable a temporal order.
*
* Addition (or subtraction) of a time span to a time point
*
* These operations are performed by all {@code plus()}- and {@code minus()}-
* methods. A time span can either be a single time unit, or it consists of
* several time units.
*
* If given time unit does not have a fixed length (for example months)
* then the result of an addition can deviate from the expected element
* value to be considered. In case of multiple additions care is required.
* In case of doubt the original value should be saved for a later addition.
* Example with additions of months in one or two steps (pseudo-code):
*
*
* - [2011-08-31] + [P1M] = [2011-09-30]
* - [2011-09-30] + [P1M] = [2011-10-30]
* - [2011-08-31] + [P2M] = [2011-10-31]
*
*
* Difference of time points
*
* The difference of time points results in a time span. The result can
* either be expressed in one time unit only, or in multiple units which
* represent the base unit of associated chronological element. In latter
* case users have to define a metric, too.
*
* Implementation notes
*
*
* - All subclasses must be final und immutable.
* - Documentation of supported and registered elements is required.
* - For a suitable choice of the type U of time units it should be
* noted that the time units must correspond to the internal state of
* a time point because otherwise the calculation of a time span between
* two time points cannot be complete. For example it would be a mistake
* to allow milliseconds for a time point which even has nanoseconds.
* - The natural order should be consistent with {@code equals()}.
*
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @author Meno Hochschild
* @serial exclude
* @see Chronology
* @see TimeAxis
* @see Temporal
*/
/*[deutsch]
* Repräsentiert einen unveränderlichen Zeitpunkt auf einer in
* die Zukunft gerichteten Zeitachse.
*
* Chronologische Elementwerte anzeigen und ändern
*
* Der Zeitwert setzt sich aus chronologischen Elementen zusammen. Diese
* abstrakte Basisklasse delegiert die Zeitrechnung immer an die zugehörige
* Zeitachse bzw. genauer an die ihr zugeordneten Regeln der Elemente und
* Zeiteinheiten, muß aber selbst den Zustand definieren, in den
* {@code get()}-Methoden den Zustand reflektieren und auch das
* Serialisierungsverhalten festlegen.
*
* Da alle konkreten Implementierungen immutable sind und sein
* müssen, sind Elementwerte nur dadurch änderbar, daß jeweils
* eine neue Instanz mit geänderten Elementwerten erzeugt wird. Das wird
* unter anderem von allen {@code with()}-Methoden geleistet.
*
* Zeitstrahl
*
* Ist die referenzierte Zeitachse die UTC-Weltzeitlinie, wenn also
* ein Zeitpunkt dann relativ zum Beginn der UTC-Epoche liegt (siehe
* Paketbeschreibung), dann muß eine
* Implementierung auch das Interface {@link net.time4j.scale.UniversalTime}
* implementieren. Sonst liegt eine lokale Zeitachse vor. Alle Zeiteinheiten
* sind bezüglich der Zeitachse zu interpretieren. Zum Beispiel gelten
* Sekundenzeiteinheiten auf einem lokalen Zeitstempel als lokale UT1-Sekunden
* mit externem Zeitzonenkontext, auf einem {@code UniversalTime}-Objekt
* vor 1972 als globale UT1-Sekunden und nach 1972 als atomare SI-Sekunden.
* Deshalb sollten Anwendungen besondere Vorsicht walten lassen, wenn eine
* Dauer von einer Zeitachse auf eine andere Zeitachse übertragen wird.
*
* Sortierung
*
* Wenn nicht ausdrücklich anders dokumentiert, wird die Sortierung
* von Zeitpunkten immer rein zeitlich definiert und konsistent mit
* {@code equals()} sein. Im Zweifelsfall ist die Dokumentation der
* konkreten Subklasse maßgeblich. Alternativ können Subklassen
* auch das Interface {@code Temporal} implementieren, um eine rein
* zeitliche Ordnung zu ermöglichen.
*
* Addition (oder Subtraktion) einer Zeitspanne zu einem Zeitpunkt
*
* Diese Operationen werden von allen {@code plus()}- und {@code minus()}-
* Methoden geleistet. Eine Zeitspanne kann entweder nur eine einzelne
* Zeiteinheit sein oder ist aus mehreren Zeiteinheiten zusammengesetzt.
*
* Wenn die angegebene Zeiteinheit bzw. Genauigkeit keine konstante
* Länge definiert (zum Beispiel haben Monate in Tagen ausgedrückt
* im allgemeinen unterschiedliche Längen), dann kann das Ergebnis
* einer Addition im Hinblick auf den erwarteten Elementwert abweichen. Bei
* mehrfachen Additionen hintereinander ist Vorsicht angebracht, im
* Zweifelsfall sollte der Originalwert für eine spätere Addition
* gespeichert werden. Beispiel mit Additionen von Monaten in einem oder in
* zwei Schritten (Pseudo-Code):
*
*
* - [2011-08-31] + [P1M] = [2011-09-30]
* - [2011-09-30] + [P1M] = [2011-10-30]
* - [2011-08-31] + [P2M] = [2011-10-31]
*
*
* Differenz von Zeitpunkten
*
* Die Differenz von Zeitpunkten resultiert jeweils in einer Zeitspanne.
* Das Ergebnis kann entweder in nur einer Zeiteinheit ausgedrückt werden,
* oder in mehreren Zeiteinheiten, die jede die Basiseinheit des entsprechenden
* chronologischen Elements repräsentieren. In letzterem Fall ist auch
* eine Metrik anzugeben.
*
* Implementierungshinweise
*
*
* - Alle Subklassen müssen final und immutable
* sein.
* - Es muß dokumentiert werden, welche chronologischen Elemente
* unterstützt werden bzw. registriert sind.
* - Zur Wahl des Zeiteinheitstyps U ist zu beachten, daß die
* Zeiteinheiten mit dem internen Zeitwertzustand zusammenpassen müssen,
* weil sonst die Berechnung der Zeitspanne zwischen zwei Zeitpunkten nicht
* vollständig möglich sein kann. Zum Beispiel wäre es ein
* Fehler, wenn die Zeiteinheiten maximal in Millisekunden genau sind, aber
* die konkreten Zeitpunkte sogar Nanosekunden enthalten.
* - Die natürliche Ordnung sollte konsistent mit {@code equals()}
* sein.
*
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @author Meno Hochschild
* @serial exclude
* @see Chronology
* @see TimeAxis
* @see Temporal
*/
public abstract class TimePoint>
extends ChronoEntity
implements Comparable, Serializable {
//~ Methoden ----------------------------------------------------------
/**
* Compares two time points preferably by their temporal positions
* on the common time axis.
*
* Implementation note: In order to make the natural order consistent
* with {@code equals()} the whole state must be taken into account,
* with preference for those attributes which define the temporal
* position on the time axis.
*
* @see #equals(Object)
*/
/*[deutsch]
* Vergleicht zwei Zeitpunkte bevorzugt nach ihrer Position auf der
* gemeinsamen Zeitachse.
*
* Implementierungshinweis: Damit die natürliche Ordnung konsistent
* mit {@code equals()} ist, müssen zum Vergleich alle internen
* Zustandsattribute herangezogen werden, bevorzugt aber die Attribute,
* die die zeitliche Position festlegen.
*
* @see #equals(Object)
*/
@Override
public abstract int compareTo(T timePoint);
/**
* Adds the given time span to this time point and yields
* the result of the addition.
*
* Delegates to {@link TimeSpan#addTo(TimePoint)}.
*
* @param timeSpan time span to be added to this instance
* @return result of addition as changed copy, this
* instance remains unaffected
* @throws RuleNotFoundException if any time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #minus(TimeSpan)
*/
/*[deutsch]
* Addiert die angegebene Zeitspanne zur Bezugszeit und liefert das
* Additionsergebnis zurück.
*
* Delegiert an {@link TimeSpan#addTo(TimePoint)}.
*
* @param timeSpan time span to be added to this instance
* @return result of addition as changed copy, this
* instance remains unaffected
* @throws RuleNotFoundException if any time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #minus(TimeSpan)
*/
public T plus(TimeSpan extends U> timeSpan) {
try {
return timeSpan.addTo(this.getContext());
} catch (IllegalArgumentException iae) {
ArithmeticException ex =
new ArithmeticException(
"Result beyond boundaries of time axis.");
ex.initCause(iae);
throw ex;
}
}
/**
* Subtracts given time span from this time point and yields
* the result of subtraction.
*
* Delegiert an {@link TimeSpan#subtractFrom(TimePoint)}.
*
* @param timeSpan time span to be subtracted from this instance
* @return result of subtraction as changed copy, this
* instance remains unaffected
* @throws RuleNotFoundException if any time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(TimeSpan)
*/
/*[deutsch]
* Subtrahiert die angegebene Zeitspanne von der Bezugszeit und
* liefert das Subtraktionsergebnis zurück.
*
* Delegiert an {@link TimeSpan#subtractFrom(TimePoint)}.
*
* @param timeSpan time span to be subtracted from this instance
* @return result of subtraction as changed copy, this
* instance remains unaffected
* @throws RuleNotFoundException if any time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(TimeSpan)
*/
public T minus(TimeSpan extends U> timeSpan) {
try {
return timeSpan.subtractFrom(this.getContext());
} catch (IllegalArgumentException iae) {
ArithmeticException ex =
new ArithmeticException(
"Result beyond boundaries of time axis.");
ex.initCause(iae);
throw ex;
}
}
/**
* Adds given amount in units to this time point and yields the
* result of addition.
*
* Similar to {@link #plus(TimeSpan)} but with the difference
* that the timespan is only given in one single time unit. Example
* in pseudo-code:
*
*
* - [2011-05-31].plus(1, <MONTHS>) = [2011-06-30]
* - [2011-05-31].plus(4, <DAYS>) = [2011-06-04]
* - [2011-06-04].plus(-4, <DAYS>) = [2011-05-31]
* - [2010-04-29].plus(397, <DAYS>) = [2011-05-31]
* - [2010-04-29].plus(13, <MONTHS>) = [2011-05-29]
* - [2010-04-29].plus(-2, <MONTHS>) = [2010-02-28]
* - [2010-04-29].plus(1, <YEARS>) = [2011-04-29]
*
*
* @param amount amount to be added (maybe negative)
* @param unit time unit
* @return result of addition as changed copy, this instance
* remains unaffected
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(TimeSpan)
*/
/*[deutsch]
* Addiert den angegebenen Betrag der entsprechenden Zeiteinheit
* zu dieser Bezugszeit und liefert das Additionsergebnis zurück.
*
* Ähnlich wie {@link #plus(TimeSpan)}, aber mit dem Unterschied,
* daß die Zeitspanne in nur einer Zeiteinheit angegeben wird.
* Beispiel in Pseudo-Code:
*
*
* - [2011-05-31].plus(1, <MONTHS>) = [2011-06-30]
* - [2011-05-31].plus(4, <DAYS>) = [2011-06-04]
* - [2011-06-04].plus(-4, <DAYS>) = [2011-05-31]
* - [2010-04-29].plus(397, <DAYS>) = [2011-05-31]
* - [2010-04-29].plus(13, <MONTHS>) = [2011-05-29]
* - [2010-04-29].plus(-2, <MONTHS>) = [2010-02-28]
* - [2010-04-29].plus(1, <YEARS>) = [2011-04-29]
*
*
* @param amount amount to be added (maybe negative)
* @param unit time unit
* @return result of addition as changed copy, this instance
* remains unaffected
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(TimeSpan)
*/
public T plus(
long amount,
U unit
) {
if (amount == 0) {
return this.getContext();
}
try {
return this.getRule(unit).addTo(this.getContext(), amount);
} catch (IllegalArgumentException iae) {
ArithmeticException ex =
new ArithmeticException(
"Result beyond boundaries of time axis.");
ex.initCause(iae);
throw ex;
}
}
/**
* Subtracts given amount in units from this time point and
* yields the result of subtraction.
*
* @param amount amount to be subtracted (maybe negative)
* @param unit time unit
* @return result of subtraction as changed copy, this instance
* remains unaffected
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, U)
*/
/*[deutsch]
* Subtrahiert den angegebenen Betrag der entsprechenden Zeiteinheit
* von dieser Bezugszeit und liefert das Ergebnis zurück.
*
* @param amount amount to be subtracted (maybe negative)
* @param unit time unit
* @return result of subtraction as changed copy, this instance
* remains unaffected
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, U)
*/
public T minus(
long amount,
U unit
) {
return this.plus(MathUtils.safeNegate(amount), unit);
}
/**
* Calculates the (most normalized) time span between this time point
* and given end time point using the given metric.
*
* @param generic type of time span result
* @param end end time point
* @param metric temporal distance metric
* @return difference between this and given end time point
* expressed as time span
* @throws ArithmeticException in case of numerical overflow
*/
/*[deutsch]
*
Ermittelt die (meist normalisierte) Zeitspanne zwischen dieser
* Bezugszeit und dem Endzeitpunkt in der angegebenen Metrik.
*
* @param generic type of time span result
* @param end end time point
* @param metric temporal distance metric
* @return difference between this and given end time point
* expressed as time span
* @throws ArithmeticException in case of numerical overflow
*/
public
P until(
T end,
TimeMetric extends U, P> metric
) {
return metric.between(this.getContext(), end);
}
/**
*
Calculates the temporal distance between this time point and
* given end time point in only one time unit.
*
* Similar to {@link #until(TimePoint, TimeMetric)} but with the
* difference that the time span is onyl calculated in one time unit
* as long-primitive. In many cases a remainder of subtraction will
* be left if given unit is not the smallest possible unit. Time points
* whose element values differ less than one base unit will be
* considered as equal. Examples in pseudo-code:
*
*
* - [2011-05-31].until([2011-06-04], <MONTHS>) = 0
* - [2011-05-31].until([2011-06-04], <DAYS>) = 4
* - [2011-06-04].until([2011-05-31], <DAYS>) = -4
* - [2010-04-29].until([2011-05-31], <DAYS>) = 397
* - [2010-04-29].until([2011-05-31], <MONTHS>) = 13
* - [2010-04-29].until([2011-05-31], <YEARS>) = 1
* - [2010-05-31].until([2011-05-31], <YEARS>) = 1
* - [2010-06-01].until([2011-05-31], <YEARS>) = 0
*
*
* @param end end time point
* @param unit time unit
* @return difference between this and given end time point
* as count of given time unit
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #until(TimePoint, TimeMetric)
*/
/*[deutsch]
* Ermittelt den zeitlichen Abstand zwischen dieser Bezugszeit und dem
* angegebenen Zeitpunkt nur in einer Zeiteinheit zurück.
*
* Ähnlich wie {@link #until(TimePoint, TimeMetric)}, aber mit
* dem Unterschied, daß die Zeitspanne nur in einer Zeiteinheit als
* long-Primitive ermittelt wird. Es wird meist ein Subtraktionsrest
* verbleiben, wenn es sich nicht um die kleinstmögliche bzw. genaueste
* Zeiteinheit handelt. Zeitpunkte, deren Elementwerte sich um weniger als
* eine Einheit unterscheiden, gelten als gleich. Beispiele in
* Pseudo-Code:
*
*
* - [2011-05-31].until([2011-06-04], <MONTHS>) = 0
* - [2011-05-31].until([2011-06-04], <DAYS>) = 4
* - [2011-06-04].until([2011-05-31], <DAYS>) = -4
* - [2010-04-29].until([2011-05-31], <DAYS>) = 397
* - [2010-04-29].until([2011-05-31], <MONTHS>) = 13
* - [2010-04-29].until([2011-05-31], <YEARS>) = 1
* - [2010-05-31].until([2011-05-31], <YEARS>) = 1
* - [2010-06-01].until([2011-05-31], <YEARS>) = 0
*
*
* @param end end time point
* @param unit time unit
* @return difference between this and given end time point
* as count of given time unit
* @throws RuleNotFoundException if given time unit is not registered
* and does also not implement {@link BasicUnit} to yield
* a suitable unit rule for the underlying time axis
* @throws ArithmeticException in case of numerical overflow
* @see #until(TimePoint, TimeMetric)
*/
public long until(
T end,
U unit
) {
return this.getRule(unit).between(this.getContext(), end);
}
/**
* Determines the minimum of both time points.
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @param t1 first time point
* @param t2 second time point
* @return minimum of t1 and t2
*/
/*[deutsch]
* Bestimmt das Minimum der beiden Zeitpunkte.
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @param t1 first time point
* @param t2 second time point
* @return minimum of t1 and t2
*/
public static > T min(
T t1,
T t2
) {
return (t1.compareTo(t2) > 0) ? t2 : t1;
}
/**
* Determines the maximum of both time points.
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @param t1 first time point
* @param t2 second time point
* @return maximum of t1 and t2
*/
/*[deutsch]
* Bestimmt das Maximum der beiden Zeitpunkte.
*
* @param generic type of time units compatible to {@link ChronoUnit}
* @param generic type of self reference
* @param t1 first time point
* @param t2 second time point
* @return maximum of t1 and t2
*/
public static > T max(
T t1,
T t2
) {
return (t1.compareTo(t2) > 0) ? t1 : t2;
}
/**
* Compares the whole state of this instance with given object.
*
* Implementations will usually define their state only
* based on the temporal position on the time axis because this
* is the most intuitive approach. Exceptions from this rule should
* be explicitly documented and reasoned.
*
* @see #compareTo(TimePoint)
*/
/*[deutsch]
* Vergleicht den gesamten Zustand dieser Instanz mit dem des
* angegebenen Objekts.
*
* Implementierungen werden üblicherweise ihren Zustand nur
* auf Basis der zeitlichen Position definieren, da dies am ehesten
* der Erwartungshaltung der Anwender entspricht. Ausnahmen sind
* explizit zu dokumentieren und zu begründen.
*
* @see #compareTo(TimePoint)
*/
@Override
public abstract boolean equals(Object obj);
/**
* Subclasses must redefine this method corresponding to the
* behaviour of {@code equals()}.
*/
/*[deutsch]
* Subklassen müssen diese Methode passend zum Verhalten
* von {@code equals()} redefinieren.
*/
@Override
public abstract int hashCode();
/**
* Provides a complete textual representation of the state of
* this time point.
*
* The textual description often follows the conventions of ISO-8601.
* Usually the description starts with the chronological informations
* which are coarse-grained and ends with those ones which are
* fine-grained (for example the ISO-notation YYYY-MM-DD).
*/
/*[deutsch]
* Liefert eine vollständige Beschreibung des Zustands dieses
* Zeitpunkts.
*
* Die textuelle Beschreibung folgt oft den Konventionen des
* ISO-8601-Standards, indem zuerst die groben und dann die feineren
* chronologischen Informationen mit höherer Detailgenauigkeit folgen
* (zum Beispiel die ISO-Notation YYYY-MM-DD).
*/
@Override
public abstract String toString();
/**
* Returns the assigned time axis which contains all necessary
* chronological rules.
*
* Concrete subclasses must create in a static initializer a
* time axis by help of {@code TimeAxis.Builder}, keep it as static
* constant and make it available here. Using the procedure guarantees
* that a basic set of registered elements, units and rules will be
* installed.
*
* @return chronological system as time axis (never {@code null})
* @see TimeAxis.Builder
*/
/*[deutsch]
* Liefert die zugehörige Zeitachse, die alle notwendigen
* chronologischen Regeln enthält.
*
* Konkrete Subklassen müssen in einem static initializer
* mit Hilfe von {@code TimeAxis.Builder} eine Zeitachse bauen, in
* einer eigenen Konstanten halten und hier verfügbar machen.
* Über dieses Verfahren wird zugleich ein Basissatz von Elementen,
* Zeiteinheiten und chronologischen Regeln vorinstalliert.
*
* @return chronological system as time axis (never {@code null})
* @see TimeAxis.Builder
*/
@Override
protected abstract TimeAxis getChronology();
// Einheitsregel
private UnitRule getRule(U unit) {
return this.getChronology().getRule(unit);
}
}