/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (SimpleInterval.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 <http://www.gnu.org/licenses/>.
* -----------------------------------------------------------------------
*/
package net.time4j.range;
import net.time4j.engine.AttributeQuery;
import net.time4j.engine.TimeLine;
import net.time4j.format.FormatPatternProvider;
import net.time4j.format.expert.ChronoParser;
import net.time4j.format.expert.ChronoPrinter;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
import java.util.Optional;
/**
* <p>Generic interval class suitable for any type of timepoints on a timeline. </p>
*
* <p>Represents an interval with following simplified features: </p>
*
* <ul>
* <li>Always half-open with inclusive start and exclusive end. </li>
* <li>Supports infinite boundaries (the only exception to half-open-state). </li>
* <li>Can be adapted to any foreign type as long as a timeline can be implemented. </li>
* <li>Can be used in conjunction with {@code IntervalCollection} and {@code IntervalTree}. </li>
* </ul>
*
* <p>This class is mainly intended to adapt foreign types like {@code java.util.Date}
* or {@code java.time.Instant}. It is less suitable for calendrical types. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
* @author Meno Hochschild
* @since 3.25/4.21
*/
/*[deutsch]
* <p>Allgemeine Intervallklasse geeignet für generische Zeitpunkte auf einem beliebigen Zeitstrahl. </p>
*
* <p>Repräsentiert ein Intervall mit folgenden vereinfachten Eigenschaften: </p>
*
* <ul>
* <li>Immer halb-offen mit dem Start inklusive und dem Ende exklusive. </li>
* <li>Unterstützt unendliche Grenzen (die einzige Ausnahme vom halb-offenen Zustand). </li>
* <li>Kann an irgendeinen Fremdtyp angepasst werden, solange ein Zeitstrahl passend konstruiert wird. </li>
* <li>Kann in Verbindung mit {@code IntervalCollection} und {@code IntervalTree} genutzt werden. </li>
* </ul>
*
* <p>Diese Klasse dient hauptsächlich zur Verwendung mit Fremdtypen wie {@code java.util.Date}
* oder {@code java.time.Instant}. Sie ist für rein kalendarische Typen weniger geeignet. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
* @author Meno Hochschild
* @since 3.25/4.21
*/
public final class SimpleInterval<T>
implements ChronoInterval<T>, Serializable {
//~ Statische Felder/Initialisierungen --------------------------------
private static final Factory<Date> OLD_DATE_FACTORY = new Factory<>(new TraditionalTimeLine());
private static final Factory<Instant> INSTANT_FACTORY = new Factory<>(new InstantTimeLine());
private static final long serialVersionUID = -3508139527445140226L;
//~ Instanzvariablen --------------------------------------------------
/**
* @serial start boundary
*/
private final Boundary<T> start;
/**
* @serial end boundary
*/
private final Boundary<T> end;
/**
* @serial the underlying timeline
*/
private final TimeLine<T> timeLine;
//~ Konstruktoren -----------------------------------------------------
SimpleInterval(
T start,
T end,
TimeLine<T> timeLine
) {
super();
if (timeLine == null) {
throw new NullPointerException();
}
if ((start != null) && (end != null) && timeLine.compare(start, end) > 0) {
throw new IllegalArgumentException("Start after end: " + start + "/" + end);
}
this.start = ((start == null) ? Boundary.<T>infinitePast() : Boundary.ofClosed(start));
this.end = ((end == null) ? Boundary.<T>infiniteFuture() : Boundary.ofOpen(end));
this.timeLine = timeLine;
}
SimpleInterval(
Boundary<T> start,
Boundary<T> end,
TimeLine<T> timeLine
) {
super();
this.start = start;
this.end = end;
this.timeLine = timeLine;
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Creates a new interval between given boundaries. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall mit den angegebenen Grenzen. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
*/
public static SimpleInterval<Date> between(
Date start,
Date end
) {
return OLD_DATE_FACTORY.between(start, end);
}
/**
* <p>Creates a new interval between given boundaries. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
* @see MomentInterval#between(Instant, Instant)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall mit den angegebenen Grenzen. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
* @see MomentInterval#between(Instant, Instant)
*/
public static SimpleInterval<Instant> between(
Instant start,
Instant end
) {
return INSTANT_FACTORY.between(start, end);
}
/**
* <p>Creates a new interval since given start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall seit dem angegebenen Start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
*/
public static SimpleInterval<Date> since(Date start) {
return OLD_DATE_FACTORY.since(start);
}
/**
* <p>Creates a new interval since given start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
* @see MomentInterval#since(Instant)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall seit dem angegebenen Start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
* @see MomentInterval#since(Instant)
*/
public static SimpleInterval<Instant> since(Instant start) {
return INSTANT_FACTORY.since(start);
}
/**
* <p>Creates a new interval until given end. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall bis zum angegebenen Ende. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
*/
public static SimpleInterval<Date> until(Date end) {
return OLD_DATE_FACTORY.until(end);
}
/**
* <p>Creates a new interval until given end. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
* @see MomentInterval#until(Instant)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall bis zum angegebenen Ende. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
* @see MomentInterval#until(Instant)
*/
public static SimpleInterval<Instant> until(Instant end) {
return INSTANT_FACTORY.until(end);
}
/**
* <p>Defines a timeline on which new intervals for the type {@code java.util.Date} can be created. </p>
*
* @return singleton interval factory
*/
/*[deutsch]
* <p>Definiert einen Zeitstrahl, auf dem neue Intervalle für den Typ {@code java.util.Date}
* erzeugt werden können. </p>
*
* @return singleton interval factory
*/
public static Factory<Date> onTraditionalTimeLine() {
return OLD_DATE_FACTORY;
}
/**
* <p>Defines a timeline on which new intervals for the type {@code java.time.Instant} can be created. </p>
*
* @return singleton interval factory
*/
/*[deutsch]
* <p>Definiert einen Zeitstrahl, auf dem neue Intervalle für den Typ {@code java.time.Instant}
* erzeugt werden können. </p>
*
* @return singleton interval factory
*/
public static Factory<Instant> onInstantTimeLine() {
return INSTANT_FACTORY;
}
/**
* <p>Defines a timeline on which new generic intervals can be created. </p>
*
* <p>Note that the given timeline must be serializable to make the produced simple intervals
* serializable, too. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
* @param timeLine the timeline definition
* @return new interval factory
*/
/*[deutsch]
* <p>Definiert einen Zeitstrahl, auf dem neue generische Intervalle erzeugt werden können. </p>
*
* <p>Hinweis: Der angegebene Zeitstrahl muß serialisierbar sein, damit alle darauf erzeugten
* Intervalle serialisierbar sind. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
* @param timeLine the timeline definition
* @return new interval factory
*/
public static <T> Factory<T> onTimeLine(TimeLine<T> timeLine) {
return new Factory<>(timeLine);
}
@Override
public Boundary<T> getStart() {
return this.start;
}
@Override
public Boundary<T> getEnd() {
return this.end;
}
@Override
public boolean isEmpty() {
if (!this.isFinite()) {
return false;
}
return (this.timeLine.compare(this.start.getTemporal(), this.end.getTemporal()) == 0);
}
@Override
public boolean contains(T temporal) {
if (temporal == null) {
throw new NullPointerException();
} else if (!this.start.isInfinite() && (this.timeLine.compare(temporal, this.start.getTemporal()) < 0)) {
return false;
}
if (!this.end.isInfinite()) {
return (this.timeLine.compare(this.end.getTemporal(), temporal) > 0);
}
return true;
}
@Override
public boolean contains(ChronoInterval<T> other) {
if (!other.isFinite()) {
return false;
}
T startA = this.start.getTemporal();
T startB = other.getStart().getTemporal();
if (other.getStart().isOpen()) {
startB = this.timeLine.stepForward(startB);
}
if ((startB == null) || ((startA != null) && (this.timeLine.compare(startA, startB) > 0))) {
return false;
}
T endA = this.end.getTemporal();
if (endA == null) {
return true;
}
T endB = other.getEnd().getTemporal();
if (
other.getEnd().isOpen()
&& (this.timeLine.compare(startB, endB) == 0)
) {
if ((endA == null) || (this.timeLine.compare(startB, endA) >= 0)) {
return false;
}
} else {
if (other.getEnd().isClosed()) {
endB = this.timeLine.stepForward(endB);
if (endB == null) {
return false;
}
}
return (this.timeLine.compare(endA, endB) >= 0);
}
return true;
}
@Override
public boolean isAfter(T temporal) {
if (temporal == null) {
throw new NullPointerException();
} else if (this.start.isInfinite()) {
return false;
}
return (this.timeLine.compare(this.start.getTemporal(), temporal) > 0);
}
@Override
public boolean isBefore(T temporal) {
if (temporal == null) {
throw new NullPointerException();
} else if (this.end.isInfinite()) {
return false;
}
return (this.timeLine.compare(this.end.getTemporal(), temporal) <= 0);
}
@Override
public boolean isBefore(ChronoInterval<T> other) {
if (other.getStart().isInfinite() || this.end.isInfinite()) {
return false;
}
T endA = this.end.getTemporal();
T startB = other.getStart().getTemporal();
if (other.getStart().isOpen()) {
startB = this.timeLine.stepForward(startB);
}
if (startB == null) { // exotic case: start in infinite future
return true;
} else {
return (this.timeLine.compare(endA, startB) <= 0);
}
}
@Override
public boolean abuts(ChronoInterval<T> other) {
if (this.isEmpty() || other.isEmpty()) {
return false;
}
T startA = this.start.getTemporal();
T startB = other.getStart().getTemporal();
if ((startB != null) && other.getStart().isOpen()) {
startB = this.timeLine.stepForward(startB);
}
T endA = this.end.getTemporal();
T endB = other.getEnd().getTemporal();
if ((endB != null) && other.getEnd().isClosed()) {
endB = this.timeLine.stepForward(endB);
}
if ((endA == null) || (startB == null)) {
return ((startA != null) && (endB != null) && (this.timeLine.compare(startA, endB) == 0));
} else if ((startA == null) || (endB == null)) {
return (this.timeLine.compare(endA, startB) == 0);
}
return (this.timeLine.compare(endA, startB) == 0) ^ (this.timeLine.compare(startA, endB) == 0);
}
/**
* <p>Obtains the intersection of this interval and other one if present. </p>
*
* <p>Note that the return type of the method is for the older version line v3.25 or later
* just {@code SimpleInterval<T>}, possibly returning {@code null}. </p>
*
* @param other another interval which might have an intersection with this interval
* @return a wrapper around the found intersection or an empty wrapper
* @see Optional#isPresent()
* @see #intersects(ChronoInterval)
* @since 4.21
*/
/*[deutsch]
* <p>Ermittelt die Schnittmenge dieses Intervalls mit dem angegebenen anderen Intervall, falls vorhanden. </p>
*
* <p>Zu beachten: In der älteren Versionslinie v3.25 oder später ist der return-type einfach nur
* {@code SimpleInterval<T>}, wobei {@code null} ein möglicher Rückgabewert ist. </p>
*
* @param other another interval which might have an intersection with this interval
* @return a wrapper around the found intersection or an empty wrapper
* @see Optional#isPresent()
* @see #intersects(ChronoInterval)
* @since 4.21
*/
public Optional<SimpleInterval<T>> findIntersection(ChronoInterval<T> other) {
if (this.isEmpty() || other.isEmpty()) {
return Optional.empty();
}
Boundary<T> s;
Boundary<T> e;
if (this.start.isInfinite()) {
s = other.getStart();
} else if (other.getStart().isInfinite()) {
s = this.start;
} else {
T t1 = this.start.getTemporal();
T t2 = other.getStart().getTemporal();
if (other.getStart().isOpen()) {
t2 = this.timeLine.stepForward(t2);
}
if ((t1 == null) || (t2 == null)) {
return Optional.empty();
}
s = ((this.timeLine.compare(t1, t2) < 0) ? Boundary.ofClosed(t2) : Boundary.ofClosed(t1));
}
if (this.end.isInfinite()) {
e = other.getEnd();
} else if (other.getEnd().isInfinite()) {
e = this.end;
} else {
T t1 = this.end.getTemporal();
T t2 = other.getEnd().getTemporal();
if (other.getEnd().isClosed()) {
t2 = this.timeLine.stepForward(t2);
}
if (t1 == null) {
e = ((t2 == null) ? Boundary.infiniteFuture() : Boundary.ofOpen(t2));
} else if (t2 == null) {
e = ((t1 == null) ? Boundary.infiniteFuture() : Boundary.ofOpen(t1));
} else {
e = ((this.timeLine.compare(t1, t2) < 0) ? Boundary.ofOpen(t1) : Boundary.ofOpen(t2));
}
}
if (isAfter(s, e)) {
return Optional.empty();
} else {
SimpleInterval<T> intersection = new SimpleInterval<>(s, e, this.timeLine);
return (intersection.isEmpty() ? Optional.empty() : Optional.of(intersection));
}
}
/**
* <p>Prints this interval using a localized interval pattern. </p>
*
* <p>If given printer does not contain a reference to a locale then the interval pattern
* "{0}/{1}" will be used. </p>
*
* @param printer format object for printing start and end
* @return localized formatted string
* @see #print(ChronoPrinter, String)
* @see FormatPatternProvider#getIntervalPattern(Locale)
*/
/*[deutsch]
* <p>Formatiert dieses Intervall mit Hilfe eines lokalisierten Intervallmusters. </p>
*
* <p>Falls der angegebene Formatierer keine Referenz zu einer Sprach- und Ländereinstellung hat, wird
* das Intervallmuster "{0}/{1}" verwendet. </p>
*
* @param printer format object for printing start and end
* @return localized formatted string
* @see #print(ChronoPrinter, String)
* @see FormatPatternProvider#getIntervalPattern(Locale)
*/
public String print(ChronoPrinter<T> printer) {
return this.print(printer, IsoInterval.getIntervalPattern(printer));
}
/**
* <p>Prints this interval in a custom format. </p>
*
* @param printer format object for printing start and end components
* @param intervalPattern interval pattern containing placeholders {0} and {1} (for start and end)
* @return formatted string in given pattern format
*/
/*[deutsch]
* <p>Formatiert dieses Intervall in einem benutzerdefinierten Format. </p>
*
* @param printer format object for printing start and end components
* @param intervalPattern interval pattern containing placeholders {0} and {1} (for start and end)
* @return formatted string in given pattern format
*/
public String print(
ChronoPrinter<T> printer,
String intervalPattern
) {
AttributeQuery attrs = printer.getAttributes();
StringBuilder sb = new StringBuilder(32);
int i = 0;
int n = intervalPattern.length();
while (i < n) {
char c = intervalPattern.charAt(i);
if ((c == '{') && (i + 2 < n) && (intervalPattern.charAt(i + 2) == '}')) {
char next = intervalPattern.charAt(i + 1);
if (next == '0') {
if (this.start.isInfinite()) {
sb.append("-\u221E");
} else {
printer.print(this.start.getTemporal(), sb, attrs);
}
i += 3;
continue;
} else if (next == '1') {
if (this.end.isInfinite()) {
sb.append("+\u221E");
} else {
printer.print(this.end.getTemporal(), sb, attrs);
}
i += 3;
continue;
}
}
sb.append(c);
i++;
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof SimpleInterval) {
SimpleInterval<?> that = (SimpleInterval<?>) obj;
return this.start.equals(that.start) && this.end.equals(that.end) && this.timeLine.equals(that.timeLine);
} else {
return false;
}
}
@Override
public int hashCode() {
return this.start.hashCode() ^ this.end.hashCode();
}
/**
* <p>Returns a string in technical notation (suitable for debugging purposes). </p>
*
* @return String
*/
/*[deutsch]
* <p>Liefert eine technische Beschreibung, die vor allem zum Debugging geeignet ist. </p>
*
* @return String
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.start.isInfinite()) {
sb.append("(-\u221E");
} else {
sb.append('[');
sb.append(this.start.getTemporal());
}
sb.append('/');
if (this.end.isInfinite()) {
sb.append("+\u221E)");
} else {
sb.append(this.end.getTemporal());
sb.append(')');
}
return sb.toString();
}
private boolean isAfter(
Boundary<T> start,
Boundary<T> end
) {
if (start.isInfinite()|| end.isInfinite()) {
return false;
} else {
return (this.timeLine.compare(start.getTemporal(), end.getTemporal()) > 0);
}
}
//~ Innere Klassen ----------------------------------------------------
/**
* <p>Serves for the creation of generic simple intervals on a timeline. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
*/
/*[deutsch]
* <p>Dient der Erzeugung von allgemeinen einfachen Intervallen auf einem Zeitstrahl. </p>
*
* @param <T> generic type of timepoints on the underlying timeline
*/
public static class Factory<T> {
//~ Instanzvariablen ----------------------------------------------
private final TimeLine<T> timeLine;
//~ Konstruktoren -------------------------------------------------
private Factory(TimeLine<T> timeLine) {
super();
if (timeLine == null) {
throw new NullPointerException("Missing timeline.");
}
this.timeLine = timeLine;
}
//~ Methoden ------------------------------------------------------
/**
* <p>Creates a new interval between given boundaries. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall mit den angegebenen Grenzen. </p>
*
* @param start the start of interval (inclusive)
* @param end the end of interval (exclusive)
* @return new interval (half-open)
*/
public SimpleInterval<T> between(
T start,
T end
) {
if (start == null) {
throw new NullPointerException("Missing start.");
} else if (end == null) {
throw new NullPointerException("Missing end.");
}
return new SimpleInterval<>(start, end, this.timeLine);
}
/**
* <p>Creates a new interval since given start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall seit dem angegebenen Start. </p>
*
* @param start the start of interval (inclusive)
* @return new interval (half-open and infinite)
*/
public SimpleInterval<T> since(T start) {
if (start == null) {
throw new NullPointerException("Missing start.");
}
return new SimpleInterval<>(start, null, this.timeLine);
}
/**
* <p>Creates a new interval until given end. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
*/
/*[deutsch]
* <p>Erzeugt ein neues Intervall bis zum angegebenen Ende. </p>
*
* @param end the end of interval (exclusive)
* @return new interval (open and infinite)
*/
public SimpleInterval<T> until(T end) {
if (end == null) {
throw new NullPointerException("Missing end.");
}
return new SimpleInterval<>(null, end, this.timeLine);
}
/**
* <p>Interpretes given text as interval using a localized interval pattern. </p>
*
* <p>If given parser does not contain a reference to a locale then the interval pattern
* "{0}/{1}" will be used. </p>
*
* @param text text to be parsed
* @param parser format object for parsing start and end components
* @return parsed interval
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
* @see #parse(CharSequence, ChronoParser, String)
*/
/*[deutsch]
* <p>Interpretiert den angegebenen Text als Intervall mit Hilfe eines lokalisierten
* Intervallmusters. </p>
*
* <p>Falls der angegebene Formatierer keine Referenz zu einer Sprach- und Ländereinstellung hat, wird
* das Intervallmuster "{0}/{1}" verwendet. </p>
*
* @param text text to be parsed
* @param parser format object for parsing start and end components
* @return parsed interval
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
* @see #parse(CharSequence, ChronoParser, String)
*/
public SimpleInterval<T> parse(
CharSequence text,
ChronoParser<T> parser
) throws ParseException {
return parse(text, parser, IsoInterval.getIntervalPattern(parser));
}
/**
* <p>Interpretes given text as interval using given interval pattern. </p>
*
* <p>For version v4.21 or later, it is also possible to use an or-pattern logic. Example
* see {@link DateInterval#parse(String, ChronoParser, String)}. </p>
*
* @param text text to be parsed
* @param parser format object for parsing start and end components
* @param intervalPattern interval pattern containing placeholders {0} and {1} (for start and end)
* @return parsed interval
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
*/
/*[deutsch]
* <p>Interpretiert den angegebenen Text als Intervall mit Hilfe des angegebenen
* Intervallmusters. </p>
*
* <p>Für die Version v4.21 oder später ist es auch möglich, eine Oder-Logik im Muster
* zu verwenden. Beispiel siehe {@link DateInterval#parse(String, ChronoParser, String)}. </p>
*
* @param text text to be parsed
* @param parser format object for parsing start and end components
* @param intervalPattern interval pattern containing placeholders {0} and {1} (for start and end)
* @return parsed interval
* @throws IndexOutOfBoundsException if given text is empty
* @throws ParseException if the text is not parseable
*/
public SimpleInterval<T> parse(
CharSequence text,
ChronoParser<T> parser,
String intervalPattern
) throws ParseException {
IntervalCreator<T, SimpleInterval<T>> icreator = (start, end) -> new SimpleInterval<>(start, end, timeLine);
return IntervalParser.parsePattern(
text,
icreator,
parser,
intervalPattern
);
}
// package-private
TimeLine<T> getTimeLine() {
return this.timeLine;
}
}
private static class TraditionalTimeLine
implements TimeLine<Date>, Serializable {
//~ Methoden ------------------------------------------------------
@Override
public Date stepForward(Date timepoint) {
if (timepoint.getTime() == Long.MAX_VALUE) {
return null;
}
return new Date(timepoint.getTime() + 1);
}
@Override
public Date stepBackwards(Date timepoint) {
if (timepoint.getTime() == Long.MIN_VALUE) {
return null;
}
return new Date(timepoint.getTime() - 1);
}
@Override
public int compare(Date o1, Date o2) {
return o1.compareTo(o2);
}
@Override
public Date getMinimum() {
return new Date(Long.MIN_VALUE);
}
@Override
public Date getMaximum() {
return new Date(Long.MAX_VALUE);
}
private Object readResolve() throws ObjectStreamException {
return OLD_DATE_FACTORY.getTimeLine();
}
}
private static class InstantTimeLine
implements TimeLine<Instant>, Serializable {
//~ Methoden ------------------------------------------------------
@Override
public Instant stepForward(Instant timepoint) {
if (timepoint.equals(Instant.MAX)) {
return null;
}
return timepoint.plus(1, ChronoUnit.NANOS);
}
@Override
public Instant stepBackwards(Instant timepoint) {
if (timepoint.equals(Instant.MIN)) {
return null;
}
return timepoint.minus(1, ChronoUnit.NANOS);
}
@Override
public int compare(Instant o1, Instant o2) {
return o1.compareTo(o2);
}
@Override
public Instant getMinimum() {
return Instant.MIN;
}
@Override
public Instant getMaximum() {
return Instant.MAX;
}
private Object readResolve() throws ObjectStreamException {
return INSTANT_FACTORY.getTimeLine();
}
}
}