/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (TemporalType.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;
import net.time4j.base.MathUtils;
import net.time4j.base.TimeSource;
import net.time4j.engine.ChronoException;
import net.time4j.engine.Converter;
import net.time4j.scale.TimeScale;
import net.time4j.tz.Timezone;
import net.time4j.tz.ZonalOffset;
import java.time.Clock;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
/**
* <p>Serves as bridge to temporal types of JDK or other date and time
* libraries.</p>
*
* <p>All singleton instances are defined as static constants and are
* <i>immutable</i>.</p>
*
* @param <S> source type in other library
* @param <T> target type in Time4J
* @author Meno Hochschild
* @since 2.0
*/
/*[deutsch]
* <p>Dient als Brücke zu Datums- und Zeittypen aus dem JDK oder anderen
* Bibliotheken. </p>
*
* <p>Alle Singleton-Instanzen sind als statische Konstanten definiert und
* unveränderlich (<i>immutable</i>). </p>
*
* @param <S> source type in other library
* @param <T> target type in Time4J
* @author Meno Hochschild
* @since 2.0
*/
public abstract class TemporalType<S, T>
implements Converter<S, T> {
//~ Statische Felder/Initialisierungen --------------------------------
private static final int MIO = 1000_000;
/**
* <p>Bridge between a traditional Java timestamp of type
* {@code java.util.Date} and the class {@code Moment}.</p>
*
* <p>The conversion does not take into account any UTC-leapseconds. The
* supported value range is smaller than in the class {@code Moment}.
* Example: </p>
*
* <pre>
* java.util.Date instant = new java.util.Date(86401 * 1000);
* Moment ut = TemporalType.JAVA_UTIL_DATE.translate(instant);
* System.out.println(ut);
* // output: 1970-01-02T00:00:01Z
* </pre>
*
* @since 2.0
*/
/*[deutsch]
* <p>Brücke zwischen einem traditionellen Java-Zeitstempel des Typs
* {@code java.util.Date} und der Klasse {@code Moment}. </p>
*
* <p>Die Konversion berücksichtigt KEINE UTC-Schaltsekunden.
* Der unterstützte Wertbereich ist kleiner als in der Klasse
* {@code Moment}. Beispiel: </p>
*
* <pre>
* java.util.Date instant = new java.util.Date(86401 * 1000);
* Moment ut = TemporalType.JAVA_UTIL_DATE.translate(instant);
* System.out.println(ut);
* // Ausgabe: 1970-01-02T00:00:01Z
* </pre>
*
* @since 2.0
*/
public static final TemporalType<java.util.Date, Moment> JAVA_UTIL_DATE = new JavaUtilDateRule();
/**
* <p>Bridge between a traditional Java timestamp as count of milliseconds
* since UNIX-epoch and the class {@code Moment}.</p>
*
* <p>The conversion does not take into account any UTC-leapseconds.
* The supported value range is smaller than in the class {@code Moment}.
* Example: </p>
*
* <pre>
* long instant = 86401 * 1000L;
* Moment ut = TemporalType.MILLIS_SINCE_UNIX.translate(instant);
* System.out.println(ut);
* // output: 1970-01-02T00:00:01Z
* </pre>
*
* @since 2.0
*/
/*[deutsch]
* <p>Brücke zwischen einem traditionellen Java-Zeitstempel als
* Anzahl der Millisekunden seit der UNIX-Epoche und der Klasse
* {@code Moment}. </p>
*
* <p>Die Konversion berücksichtigt KEINE UTC-Schaltsekunden. Der
* unterstützte Wertbereich ist etwas kleiner als in der Klasse
* {@code Moment}. Beispiel: </p>
*
* <pre>
* long instant = 86401 * 1000L;
* Moment ut = TemporalType.MILLIS_SINCE_UNIX.translate(instant);
* System.out.println(ut);
* // Ausgabe: 1970-01-02T00:00:01Z
* </pre>
*
* @since 2.0
*/
public static final TemporalType<Long, Moment> MILLIS_SINCE_UNIX = new MillisSinceUnixRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.LocalDate} and
* the class {@code PlainDate}. </p>
*
* <p>The conversion is always exact. Example: </p>
*
* <pre>
* PlainDate date = TemporalType.LOCAL_DATE.translate(LocalDate.of(2015, 4, 30));
* System.out.println(date);
* // output: 2015-04-30
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.LocalDate} und
* der Klasse {@code PlainDate}. </p>
*
* <p>Die Konversion ist immer exakt. Beispiel: </p>
*
* <pre>
* PlainDate date = TemporalType.LOCAL_DATE.translate(LocalDate.of(2015, 4, 30));
* System.out.println(date);
* // Ausgabe: 2015-04-30
* </pre>
*
* @since 4.0
*/
public static final TemporalType<LocalDate, PlainDate> LOCAL_DATE = new LocalDateRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.LocalTime} and
* the class {@code PlainTime}. </p>
*
* <p>The conversion is exact with the exception of midnight at end of day (T24:00). The
* special time T24:00 will be mapped to 00:00 in class {@code LocalTime}. Example: </p>
*
* <pre>
* PlainTime time = TemporalType.LOCAL_TIME.translate(LocalTime.of(17, 45));
* System.out.println(time);
* // output: T17:45
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.LocalTime} und
* der Klasse {@code PlainTime}. </p>
*
* <p>Die Konversion ist mit Ausnahme von Mitternacht am Ende des Tages (T24:00) exakt.
* Die spezielle Zeit T24:00 wird auf 00:00 in {@code LocalTime} abgebildet. Beispiel: </p>
*
* <pre>
* PlainTime time = TemporalType.LOCAL_TIME.translate(LocalTime.of(17, 45));
* System.out.println(time);
* // Ausgabe: T17:45
* </pre>
*
* @since 4.0
*/
public static final TemporalType<LocalTime, PlainTime> LOCAL_TIME = new LocalTimeRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.LocalDateTime} and
* the class {@code PlainTimestamp}. </p>
*
* <p>The conversion is always exact. Example: </p>
*
* <pre>
* PlainTimestamp tsp = TemporalType.LOCAL_DATE_TIME.translate(LocalDateTime.of(2015, 4, 30, 17, 45));
* System.out.println(tsp);
* // output: 2015-04-30T17:45
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.LocalDateTime} und
* der Klasse {@code PlainTimestamp}. </p>
*
* <p>Die Konversion ist immer exakt. Beispiel: </p>
*
* <pre>
* PlainTimestamp tsp = TemporalType.LOCAL_DATE_TIME.translate(LocalDateTime.of(2015, 4, 30, 17, 45));
* System.out.println(tsp);
* // Ausgabe: 2015-04-30T17:45
* </pre>
*
* @since 4.0
*/
public static final TemporalType<LocalDateTime, PlainTimestamp> LOCAL_DATE_TIME = new LocalDateTimeRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.Instant} and
* the class {@code Moment}. </p>
*
* <p>The conversion is usually exact. However, leap seconds will always be ignored. The
* outer value range limits of the class {@code Moment} are a tiny bit smaller. Example: </p>
*
* <pre>
* Moment moment = TemporalType.INSTANT.translate(Instant.ofEpochSecond(86401, 450_000_000));
* System.out.println(moment);
* // output: 1970-01-02T00:00:01,450000000Z
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.Instant} und
* der Klasse {@code Moment}. </p>
*
* <p>Die Konversion ist normalerweise exakt, aber Schaltsekunden werden immer ignoriert.
* Die äßeren Wertgrenzen der Klasse {@code Moment} sind geringfügig kleiner.
* Beispiel: </p>
*
* <pre>
* Moment moment = TemporalType.INSTANT.translate(Instant.ofEpochSecond(86401, 450_000_000));
* System.out.println(moment);
* // Ausgabe: 1970-01-02T00:00:01,450000000Z
* </pre>
*
* @since 4.0
*/
public static final TemporalType<Instant, Moment> INSTANT = new InstantRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.ZonedDateTime} and
* the class {@code ZonalDateTime}. </p>
*
* <p>The conversion is usually exact. However, leap seconds will always be ignored. The
* outer value range limits of the class {@code ZonalDateTime} are a tiny bit different. Example: </p>
*
* <pre>
* Moment moment = TemporalType.ZONED_DATE_TIME.translate(Instant.ofEpochSecond(86401, 450_000_000));
* System.out.println(moment);
* // Ausgabe: 1970-01-02T00:00:01,450000000Z
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.ZonedDateTime} und
* der Klasse {@code ZonalDateTime}. </p>
*
* <p>Die Konversion ist normalerweise exakt, ignoriert aber Schaltsekunden.
* Die äßeren Wertgrenzen der Klasse {@code ZonalDateTime} sind geringfügig anders.
* Beispiel: </p>
*
* <pre>
* Moment moment = TemporalType.ZONED_DATE_TIME.translate(Instant.ofEpochSecond(86401, 450_000_000));
* System.out.println(moment);
* // Ausgabe: 1970-01-02T00:00:01,450000000Z
* </pre>
*
* @since 4.0
*/
public static final TemporalType<ZonedDateTime, ZonalDateTime> ZONED_DATE_TIME = new ZonedDateTimeRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.Duration} and
* the class {@code net.time4j.Duration}. </p>
*
* <p>The conversion is usually exact but will always perform a normalization on the side
* of Time4J. Example: </p>
*
* <pre>
* Duration<ClockUnit> duration = TemporalType.THREETEN_DURATION.translate(java.time.Duration.ofSeconds(65));
* System.out.println(duration);
* // output: PT1M5S
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.Duration} und
* der Klasse {@code net.time4j.Duration}. </p>
*
* <p>Die Konversion ist normalerweise exakt, führt aber auf der Seite von Time4J immer
* eine Normalisierung durch. Beispiel: </p>
*
* <pre>
* Duration<ClockUnit> duration = TemporalType.THREETEN_DURATION.translate(java.time.Duration.ofSeconds(65));
* System.out.println(duration);
* // Ausgabe: PT1M5S
* </pre>
*
* @since 4.0
*/
public static final TemporalType<java.time.Duration, Duration<ClockUnit>> THREETEN_DURATION = new DurationRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.Period} and
* the class {@code net.time4j.Duration}. </p>
*
* <p>Note that mixed signs in original period like "P1M-30D" will be rejected by Time4J.
* This is a normalizing conversion. Example for a correct input: </p>
*
* <pre>
* Duration<CalendarUnit> duration = TemporalType.THREETEN_PERIOD.translate(Period.of(3, 13, 45));
* System.out.println(duration);
* // output: P4Y1M45D
* </pre>
*
* <p>Note: The algorithm to apply a negative duration is slightly different. Example: </p>
*
* <pre>
* System.out.println(
* LocalDate.of(2015, 7, 1).minus(Period.of(0, 1, 1))); // 2015-05-31
* System.out.println(
* PlainDate.of(2015, 7, 1).minus(Duration.ofCalendarUnits(0, 1, 1))); // 2015-05-30
* </pre>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.Period} und
* der Klasse {@code net.time4j.Duration}. </p>
*
* <p>Man beachte, daß gemischte Vorzeichen in der Original-Periode wie "P1M-30D"
* von Time4J verworfen werden. Die Konversion normalisiert immer. Beispiel für eine korrekte Eingabe: </p>
*
* <pre>
* Duration<CalendarUnit> duration = TemporalType.THREETEN_PERIOD.translate(Period.of(3, 13, 45));
* System.out.println(duration);
* // output: P4Y1M45D
* </pre>
*
* <p>Zu beachten: Der Algorithmus zur Anwendung einer negativen Dauer ist etwas verschieden. Beispiel: </p>
*
* <pre>
* System.out.println(
* LocalDate.of(2015, 7, 1).minus(Period.of(0, 1, 1))); // 2015-05-31
* System.out.println(
* PlainDate.of(2015, 7, 1).minus(Duration.ofCalendarUnits(0, 1, 1))); // 2015-05-30
* </pre>
*
* @since 4.0
*/
public static final TemporalType<Period, Duration<CalendarUnit>> THREETEN_PERIOD = new PeriodRule();
/**
* <p>Bridge between the JSR-310-class {@code java.time.Clock} and
* the interface {@code net.time4j.base.TimeSource}. </p>
*
* <p>The conversion will always ignore leap seconds and initially use {@code ZoneId.systemDefault()}. </p>
*
* @since 4.0
*/
/*[deutsch]
* <p>Brücke zwischen der JSR-310-Klasse {@code java.time.Clock} und
* dem Interface {@code net.time4j.base.TimeSource}. </p>
*
* <p>Die Konversion wird Schaltsekunden immer ignorieren und initial {@code ZoneId.systemDefault()}
* verwenden. </p>
*
* @since 4.0
*/
public static final TemporalType<Clock, TimeSource<?>> CLOCK = new ClockRule();
//~ Konstruktoren -----------------------------------------------------
/**
* <p>For subclasses only. </p>
*
* <p>Subclasses should never make the constructor <i>public</i>
* but are encouraged to assign an instance to a static constant. </p>
*/
/*[deutsch]
* <p>Nur für Subklassen. </p>
*
* <p>Subklassen sollten nie den Konstruktor <i>public</i> machen,
* sondern werden eine Instanz einer statischen Konstanten
* zuweisen. </p>
*/
protected TemporalType() {
super();
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Converts the external type to a type in Time4J. </p>
*
* @param source external object
* @return translated Time4J-object
* @throws ArithmeticException in case of numerical overflow
* @throws ChronoException if conversion fails
* @since 2.0
*/
/*[deutsch]
* <p>Konvertiert den externen Typ nach Time4J. </p>
*
* @param source external object
* @return translated Time4J-object
* @throws ArithmeticException in case of numerical overflow
* @throws ChronoException if conversion fails
* @since 2.0
*/
public abstract T translate(S source);
/**
* <p>Converts the Time4J-type to an external type.</p>
*
* @param time4j Time4J-object
* @return translated object of external type
* @throws ArithmeticException in case of numerical overflow
* @throws ChronoException if conversion fails
* @since 2.0
*/
/*[deutsch]
* <p>Konvertiert den Time4J-Typ zu einem externen Typ.</p>
*
* @param time4j Time4J-object
* @return translated object of external type
* @throws ArithmeticException in case of numerical overflow
* @throws ChronoException if conversion fails
* @since 2.0
*/
public abstract S from(T time4j);
//~ Innere Klassen ----------------------------------------------------
private static class JavaUtilDateRule
extends TemporalType<java.util.Date, Moment> {
//~ Methoden ------------------------------------------------------
@Override
public Moment translate(java.util.Date source) {
long millis = source.getTime();
long seconds = MathUtils.floorDivide(millis, 1000);
int nanos = MathUtils.floorModulo(millis, 1000) * MIO;
return Moment.of(seconds, nanos, TimeScale.POSIX);
}
@Override
public java.util.Date from(Moment target) {
long posixTime = target.getPosixTime();
int fraction = target.getNanosecond();
long millis =
MathUtils.safeAdd(
MathUtils.safeMultiply(posixTime, 1000),
fraction / MIO);
return new java.util.Date(millis);
}
@Override
public Class<java.util.Date> getSourceType() {
return java.util.Date.class;
}
}
private static class MillisSinceUnixRule
extends TemporalType<Long, Moment> {
//~ Methoden ------------------------------------------------------
@Override
public Moment translate(Long source) {
long millis = source.longValue();
long seconds = MathUtils.floorDivide(millis, 1000);
int nanos = MathUtils.floorModulo(millis, 1000) * MIO;
return Moment.of(seconds, nanos, TimeScale.POSIX);
}
@Override
public Long from(Moment moment) {
long posixTime = moment.getPosixTime();
int fraction = moment.getNanosecond();
return Long.valueOf(
MathUtils.safeAdd(
MathUtils.safeMultiply(posixTime, 1000),
fraction / MIO));
}
@Override
public Class<Long> getSourceType() {
return Long.class;
}
}
private static class LocalDateRule
extends TemporalType<LocalDate, PlainDate> {
//~ Methoden ------------------------------------------------------
@Override
public PlainDate translate(LocalDate source) {
return PlainDate.of(source.getYear(), source.getMonthValue(), source.getDayOfMonth());
}
@Override
public LocalDate from(PlainDate date) {
return LocalDate.of(date.getYear(), date.getMonth(), date.getDayOfMonth());
}
@Override
public Class<LocalDate> getSourceType() {
return LocalDate.class;
}
}
private static class LocalTimeRule
extends TemporalType<LocalTime, PlainTime> {
//~ Methoden ------------------------------------------------------
@Override
public PlainTime translate(LocalTime source) {
return PlainTime.of(source.getHour(), source.getMinute(), source.getSecond(), source.getNano());
}
@Override
public LocalTime from(PlainTime time) {
if (time.getHour() == 24) {
return LocalTime.MIDNIGHT;
}
return LocalTime.of(time.getHour(), time.getMinute(), time.getSecond(), time.getNanosecond());
}
@Override
public Class<LocalTime> getSourceType() {
return LocalTime.class;
}
}
private static class LocalDateTimeRule
extends TemporalType<LocalDateTime, PlainTimestamp> {
//~ Methoden ------------------------------------------------------
@Override
public PlainTimestamp translate(LocalDateTime source) {
return PlainTimestamp.of(
PlainDate.of(source.getYear(), source.getMonthValue(), source.getDayOfMonth()),
PlainTime.of(source.getHour(), source.getMinute(), source.getSecond(), source.getNano())
);
}
@Override
public LocalDateTime from(PlainTimestamp tsp) {
return LocalDateTime.of(
tsp.getYear(),
tsp.getMonth(),
tsp.getDayOfMonth(),
tsp.getHour(),
tsp.getMinute(),
tsp.getSecond(),
tsp.getNanosecond()
);
}
@Override
public Class<LocalDateTime> getSourceType() {
return LocalDateTime.class;
}
}
private static class InstantRule
extends TemporalType<Instant, Moment> {
//~ Methoden ------------------------------------------------------
@Override
public Moment translate(Instant source) {
return Moment.of(source.getEpochSecond(), source.getNano(), TimeScale.POSIX);
}
@Override
public Instant from(Moment moment) {
return Instant.ofEpochSecond(moment.getPosixTime(), moment.getNanosecond());
}
@Override
public Class<Instant> getSourceType() {
return Instant.class;
}
}
private static class ZonedDateTimeRule
extends TemporalType<ZonedDateTime, ZonalDateTime> {
//~ Methoden ------------------------------------------------------
@Override
public ZonalDateTime translate(ZonedDateTime source) {
Moment moment = TemporalType.INSTANT.translate(source.toInstant());
return moment.inZonalView(source.getZone().getId());
}
@Override
public ZonedDateTime from(ZonalDateTime zdt) {
Instant instant = TemporalType.INSTANT.from(zdt.toMoment());
ZoneId zone;
try {
zone = ZoneId.of(zdt.getTimezone().canonical());
} catch (DateTimeException ex) {
ZonalOffset zo = Timezone.of(zdt.getTimezone()).getOffset(zdt.toMoment());
zone = ZoneOffset.of(zo.toString());
}
return ZonedDateTime.ofInstant(instant, zone);
}
@Override
public Class<ZonedDateTime> getSourceType() {
return ZonedDateTime.class;
}
}
private static class DurationRule
extends TemporalType<java.time.Duration, Duration<ClockUnit>> {
//~ Methoden ------------------------------------------------------
@Override
public Duration<ClockUnit> translate(java.time.Duration source) {
Duration<ClockUnit> duration =
Duration.of(source.getSeconds(), ClockUnit.SECONDS).plus(source.getNano(), ClockUnit.NANOS);
return duration.with(Duration.STD_CLOCK_PERIOD);
}
@Override
public java.time.Duration from(Duration<ClockUnit> time4j) {
java.time.Duration threetenDuration = java.time.Duration.ZERO;
for (ClockUnit unit : ClockUnit.values()) {
long amount = time4j.getPartialAmount(unit);
TemporalUnit threetenUnit;
switch (unit) {
case HOURS:
threetenUnit = ChronoUnit.HOURS;
break;
case MINUTES:
threetenUnit = ChronoUnit.MINUTES;
break;
case SECONDS:
threetenUnit = ChronoUnit.SECONDS;
break;
case MILLIS:
threetenUnit = ChronoUnit.MILLIS;
break;
case MICROS:
threetenUnit = ChronoUnit.MICROS;
break;
case NANOS:
threetenUnit = ChronoUnit.NANOS;
break;
default:
throw new UnsupportedOperationException(unit.name());
}
threetenDuration = threetenDuration.plus(amount, threetenUnit);
}
if (time4j.isNegative()) {
threetenDuration = threetenDuration.negated();
}
return threetenDuration;
}
@Override
public Class<java.time.Duration> getSourceType() {
return java.time.Duration.class;
}
}
private static class PeriodRule
extends TemporalType<Period, Duration<CalendarUnit>> {
//~ Methoden ------------------------------------------------------
@Override
public Duration<CalendarUnit> translate(Period source) {
try {
Duration<CalendarUnit> duration =
Duration.ofCalendarUnits(source.getYears(), source.getMonths(), source.getDays());
return duration.with(Duration.STD_CALENDAR_PERIOD);
} catch (RuntimeException ex) {
throw new ChronoException("Cannot convert period: " + source, ex);
}
}
@Override
public Period from(Duration<CalendarUnit> time4j) {
Period period = Period.ZERO;
for (CalendarUnit unit : CalendarUnit.values()) {
long amount = time4j.getPartialAmount(unit);
if (amount != 0) {
if (time4j.isNegative()) {
amount = Math.negateExact(amount);
}
switch (unit) {
case MILLENNIA:
period = period.plusYears(Math.multiplyExact(amount, 1000));
break;
case CENTURIES:
period = period.plusYears(Math.multiplyExact(amount, 100));
break;
case DECADES:
period = period.plusYears(Math.multiplyExact(amount, 10));
break;
case YEARS:
period = period.plusYears(amount);
break;
case QUARTERS:
period = period.plusMonths(Math.multiplyExact(amount, 3));
break;
case MONTHS:
period = period.plusMonths(amount);
break;
case WEEKS:
period = period.plusDays(Math.multiplyExact(amount, 7));
break;
case DAYS:
period = period.plusDays(amount);
break;
default:
throw new UnsupportedOperationException(unit.name());
}
}
}
return period;
}
@Override
public Class<Period> getSourceType() {
return Period.class;
}
}
private static class ClockRule
extends TemporalType<Clock, TimeSource<?>> {
//~ Methoden ------------------------------------------------------
@Override
public TimeSource<?> translate(Clock source) {
return () -> TemporalType.INSTANT.translate(source.instant());
}
@Override
public Clock from(TimeSource<?> time4j) {
return new DelegateClock(ZoneId.systemDefault(), time4j);
}
@Override
public Class<Clock> getSourceType() {
return Clock.class;
}
}
private static class DelegateClock
extends Clock {
//~ Instanzvariablen ----------------------------------------------
private final ZoneId zoneId;
private final TimeSource<?> source;
//~ Konstruktoren -------------------------------------------------
private DelegateClock(
ZoneId zoneId,
TimeSource<?> source
) {
super();
if (source == null) {
throw new NullPointerException("Missing time source.");
}
this.zoneId = zoneId;
this.source = source;
}
//~ Methoden ------------------------------------------------------
@Override
public ZoneId getZone() {
return this.zoneId;
}
@Override
public Clock withZone(ZoneId zoneId) {
if (zoneId.equals(this.zoneId)) {
return this;
}
return new DelegateClock(zoneId, this.source);
}
@Override
public Instant instant() {
return TemporalType.INSTANT.from(Moment.from(this.source.currentTime()));
}
}
}