/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (MachineTime.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.SI;
import net.time4j.base.UnixTime;
import net.time4j.engine.RealTime;
import net.time4j.engine.TimeMetric;
import net.time4j.engine.TimePoint;
import net.time4j.scale.TimeScale;
import net.time4j.scale.UniversalTime;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static net.time4j.scale.TimeScale.POSIX;
import static net.time4j.scale.TimeScale.UTC;
/**
* <p>Represents a duration for machine times in decimal seconds with
* nanosecond precision. </p>
*
* <p>Note: Other time units are NOT contained but can be used in construction
* of a machine time. Example: </p>
*
* <pre>
* MachineTime<TimeUnit> mt = MachineTime.of(1, TimeUnit.HOURS);
* System.out.println(mt.contains(TimeUnit.HOURS)); // false
* System.out.println(mt.getSeconds); // 3600L
* </pre>
*
* @param <U> either {@code TimeUnit} or {@code SI}
* @author Meno Hochschild
* @since 3.0
* @see TimeUnit#SECONDS
* @see TimeUnit#NANOSECONDS
* @see SI#SECONDS
* @see SI#NANOSECONDS
* @doctags.concurrency {immutable}
*/
/*[deutsch]
* <p>Repräsentiert eine Dauer für maschinelle Zeiten in dezimalen
* Sekunden mit Nanosekundengenauigkeit. </p>
*
* <p>Hinweis: Andere Zeiteinheiten sind NICHT enthalten, können aber in
* der Konstruktuion einer maschinellen Dauer verwendet werden. Beispiel: </p>
*
* <pre>
* MachineTime<TimeUnit> mt = MachineTime.of(1, TimeUnit.HOURS);
* System.out.println(mt.contains(TimeUnit.HOURS)); // false
* System.out.println(mt.getSeconds); // 3600L
* </pre>
*
* @param <U> either {@code TimeUnit} or {@code SI}
* @author Meno Hochschild
* @since 3.0
* @see TimeUnit#SECONDS
* @see TimeUnit#NANOSECONDS
* @see SI#SECONDS
* @see SI#NANOSECONDS
* @doctags.concurrency {immutable}
*/
public final class MachineTime<U>
implements RealTime<U>, Comparable<MachineTime<U>>, Serializable {
//~ Statische Felder/Initialisierungen --------------------------------
private static final int MRD = 1000000000;
private static final MachineTime<TimeUnit> POSIX_ZERO = new MachineTime<>(0, 0, POSIX);
private static final MachineTime<SI> UTC_ZERO = new MachineTime<>(0, 0, UTC);
/**
* Metric on the POSIX scale (without leap seconds).
*
* @since 2.0
*/
/*[deutsch]
* Metrik auf der POSIX-Skala (ohne Schaltsekunden).
*
* @since 2.0
*/
public static final TimeMetric<TimeUnit, MachineTime<TimeUnit>> ON_POSIX_SCALE = new Metric<>(POSIX);
/**
* <p>Metric on the UTC scale (inclusive leap seconds). </p>
*
* <p>Time points before 1972 are not supported. </p>
*
* @since 2.0
*/
/*[deutsch]
* <p>Metrik auf der UTC-Skala (inklusive Schaltsekunden). </p>
*
* <p>Zeitpunkte vor 1972 werden nicht unterstützt. </p>
*
* @since 2.0
*/
public static final TimeMetric<TimeUnit, MachineTime<SI>> ON_UTC_SCALE = new Metric<>(UTC);
private static final long serialVersionUID = -4150291820807606229L;
//~ Instanzvariablen --------------------------------------------------
private transient final long seconds;
private transient final int nanos;
private transient final TimeScale scale;
//~ Konstruktoren -----------------------------------------------------
private MachineTime(
long secs,
int fraction,
TimeScale scale
) {
super();
while (fraction < 0) {
fraction += MRD;
secs = Math.subtractExact(secs, 1);
}
while (fraction >= MRD) {
fraction -= MRD;
secs = Math.addExact(secs, 1);
}
if ((secs < 0) && (fraction > 0)) {
secs++;
fraction -= MRD;
}
this.seconds = secs;
this.nanos = fraction;
this.scale = scale;
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Creates a machine time duration on the POSIX scale. </p>
*
* @param amount of units
* @param unit helps to interprete given amount
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der POSIX-Skala. </p>
*
* @param amount of units
* @param unit helps to interprete given amount
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public static MachineTime<TimeUnit> of(
long amount,
TimeUnit unit
) {
if (unit.compareTo(TimeUnit.SECONDS) >= 0) {
long secs =
Math.multiplyExact(
amount,
TimeUnit.SECONDS.convert(1, unit));
return ofPosixUnits(secs, 0);
}
long total =
Math.multiplyExact(
amount,
TimeUnit.NANOSECONDS.convert(1, unit));
long secs = Math.floorDiv(total, MRD);
int fraction = (int) Math.floorMod(total, MRD);
return ofPosixUnits(secs, fraction);
}
/**
* <p>Creates a machine time duration on the UTC scale. </p>
*
* @param amount of units
* @param unit helps to interprete given amount
* @return new machine time duration
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der UTC-Skala. </p>
*
* @param amount of units
* @param unit helps to interprete given amount
* @return new machine time duration
* @since 2.0
*/
public static MachineTime<SI> of(
long amount,
SI unit
) {
switch (unit) {
case SECONDS:
return ofSIUnits(amount, 0);
case NANOSECONDS:
long secs = Math.floorDiv(amount, MRD);
int fraction = (int) Math.floorMod(amount, MRD);
return ofSIUnits(secs, fraction);
default:
throw new UnsupportedOperationException(unit.name());
}
}
/**
* <p>Creates a machine time duration on the POSIX scale. </p>
*
* @param seconds POSIX-seconds
* @param fraction nanosecond part
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der POSIX-Skala. </p>
*
* @param seconds POSIX-seconds
* @param fraction nanosecond part
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public static MachineTime<TimeUnit> ofPosixUnits(
long seconds,
int fraction
) {
if ((seconds == 0) && (fraction == 0)) {
return POSIX_ZERO;
}
return new MachineTime<>(seconds, fraction, POSIX);
}
/**
* <p>Creates a machine time duration on the UTC scale. </p>
*
* @param seconds SI-seconds
* @param fraction nanosecond part
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der UTC-Skala. </p>
*
* @param seconds SI-seconds
* @param fraction nanosecond part
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public static MachineTime<SI> ofSIUnits(
long seconds,
int fraction
) {
if ((seconds == 0) && (fraction == 0)) {
return UTC_ZERO;
}
return new MachineTime<>(seconds, fraction, UTC);
}
/**
* <p>Creates a machine time duration on the POSIX scale. </p>
*
* @param seconds decimal POSIX-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @throws IllegalArgumentException if the argument is infinite or NaN
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der POSIX-Skala. </p>
*
* @param seconds decimal POSIX-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @throws IllegalArgumentException if the argument is infinite or NaN
* @since 2.0
*/
public static MachineTime<TimeUnit> ofPosixSeconds(double seconds) {
if (Double.isInfinite(seconds) || Double.isNaN(seconds)) {
throw new IllegalArgumentException("Invalid value: " + seconds);
}
long secs = (long) Math.floor(seconds);
int fraction = (int) ((seconds - secs) * MRD);
return ofPosixUnits(secs, fraction);
}
/**
* <p>Creates a machine time duration on the POSIX scale. </p>
*
* @param seconds decimal POSIX-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der POSIX-Skala. </p>
*
* @param seconds decimal POSIX-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public static MachineTime<TimeUnit> ofPosixSeconds(BigDecimal seconds) {
BigDecimal secs = seconds.setScale(0, RoundingMode.FLOOR);
int fraction =
seconds.subtract(secs)
.multiply(BigDecimal.valueOf(MRD))
.setScale(0, RoundingMode.DOWN)
.intValueExact();
return ofPosixUnits(secs.longValueExact(), fraction);
}
/**
* <p>Creates a machine time duration on the UTC scale. </p>
*
* @param seconds decimal SI-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @throws IllegalArgumentException if the argument is infinite or NaN
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der UTC-Skala. </p>
*
* @param seconds decimal SI-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @throws IllegalArgumentException if the argument is infinite or NaN
* @since 2.0
*/
public static MachineTime<SI> ofSISeconds(double seconds) {
if (Double.isInfinite(seconds) || Double.isNaN(seconds)) {
throw new IllegalArgumentException("Invalid value: " + seconds);
}
long secs = (long) Math.floor(seconds);
int fraction = (int) ((seconds - secs) * MRD);
return ofSIUnits(secs, fraction);
}
/**
* <p>Creates a machine time duration on the UTC scale. </p>
*
* @param seconds decimal SI-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Erzeugt eine Dauer als Maschinenzeit auf der UTC-Skala. </p>
*
* @param seconds decimal SI-seconds
* @return new machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public static MachineTime<SI> ofSISeconds(BigDecimal seconds) {
BigDecimal secs = seconds.setScale(0, RoundingMode.FLOOR);
int fraction =
seconds.subtract(secs)
.multiply(BigDecimal.valueOf(MRD))
.setScale(0, RoundingMode.DOWN)
.intValueExact();
return ofSIUnits(secs.longValueExact(), fraction);
}
@Override
public long getSeconds() {
long secs = this.seconds;
if (this.nanos < 0) {
secs--;
}
return secs;
}
@Override
public int getFraction() {
int n = this.nanos;
if (n < 0) {
n += MRD;
}
return n;
}
/**
* <p>Yields the related time scale. </p>
*
* @return either {@code TimeScale.POSIX} or {@code TimeScale.UTC}
* @since 2.0
*/
/*[deutsch]
* <p>Liefert die zugehörige Zeitskala. </p>
*
* @return either {@code TimeScale.POSIX} or {@code TimeScale.UTC}
* @since 2.0
*/
public TimeScale getScale() {
return this.scale;
}
@Override
public List<Item<U>> getTotalLength() {
List<Item<U>> tmp = new ArrayList<>(2);
if (this.seconds != 0) {
Object u = ((this.scale == UTC) ? SI.SECONDS : TimeUnit.SECONDS);
U unit = cast(u);
tmp.add(Item.of(Math.abs(this.seconds), unit));
}
if (this.nanos != 0) {
Object u =
((this.scale == UTC) ? SI.NANOSECONDS : TimeUnit.NANOSECONDS);
U unit = cast(u);
tmp.add(Item.of(Math.abs(this.nanos), unit));
}
return Collections.unmodifiableList(tmp);
}
@Override
public boolean contains(Object unit) {
if (
((this.scale == POSIX) && TimeUnit.SECONDS.equals(unit))
|| ((this.scale == UTC) && SI.SECONDS.equals(unit))
) {
return (this.seconds != 0);
} else if (
((this.scale == POSIX) && TimeUnit.NANOSECONDS.equals(unit))
|| ((this.scale == UTC) && SI.NANOSECONDS.equals(unit))
) {
return (this.nanos != 0);
}
return false;
}
@Override
public long getPartialAmount(Object unit) {
if (
((this.scale == POSIX) && TimeUnit.SECONDS.equals(unit))
|| ((this.scale == UTC) && SI.SECONDS.equals(unit))
) {
return Math.abs(this.seconds);
} else if (
((this.scale == POSIX) && TimeUnit.NANOSECONDS.equals(unit))
|| ((this.scale == UTC) && SI.NANOSECONDS.equals(unit))
) {
return Math.abs(this.nanos);
}
return 0;
}
@Override
public boolean isNegative() {
return ((this.seconds < 0) || (this.nanos < 0));
}
@Override
public boolean isPositive() {
return ((this.seconds > 0) || (this.nanos > 0));
}
@Override
public boolean isEmpty() {
return (this.seconds == 0) && (this.nanos == 0);
}
/**
* <p>Add given temporal amount to this machine time. </p>
*
* @param amount the amount to be added
* @param unit the related time unit
* @return result of addition
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Addiert den angegebenen Zeitbetrag zu dieser maschinellen Dauer. </p>
*
* @param amount the amount to be added
* @param unit the related time unit
* @return result of addition
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> plus(
long amount,
U unit
) {
long s = this.seconds;
int f = this.nanos;
if (this.scale == POSIX) {
TimeUnit u = TimeUnit.class.cast(unit);
if (u.compareTo(TimeUnit.SECONDS) >= 0) {
s =
Math.addExact(
s,
Math.multiplyExact(
amount,
TimeUnit.SECONDS.convert(1, u))
);
} else {
long total =
Math.addExact(
f,
Math.multiplyExact(
amount,
TimeUnit.NANOSECONDS.convert(1, u))
);
s = Math.addExact(s, Math.floorDiv(total, MRD));
f = (int) Math.floorMod(total, MRD);
}
} else {
switch (SI.class.cast(unit)) {
case SECONDS:
s = Math.addExact(s, amount);
break;
case NANOSECONDS:
long total = Math.addExact(f, amount);
s = Math.addExact(s, Math.floorDiv(total, MRD));
f = (int) Math.floorMod(total, MRD);
break;
default:
throw new UnsupportedOperationException(unit.toString());
}
}
return new MachineTime<>(s, f, this.scale);
}
/**
* <p>Add given temporal amount to this machine time. </p>
*
* @param duration other machine time to be added
* @return result of addition
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Addiert den angegebenen Zeitbetrag zu dieser maschinellen Dauer. </p>
*
* @param duration other machine time to be added
* @return result of addition
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> plus(MachineTime<U> duration) {
if (duration.isEmpty()) {
return this;
} else if (this.isEmpty()) {
return duration;
}
long s = Math.addExact(this.seconds, duration.seconds);
int f = this.nanos + duration.nanos;
return new MachineTime<>(s, f, this.scale);
}
/**
* <p>Subtracts given temporal amount from this machine time. </p>
*
* @param amount the amount to be subtracted
* @param unit the related time unit
* @return difference result
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Subtrahiert den angegebenen Zeitbetrag von dieser maschinellen
* Dauer. </p>
*
* @param amount the amount to be subtracted
* @param unit the related time unit
* @return difference result
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> minus(
long amount,
U unit
) {
return this.plus(Math.negateExact(amount), unit);
}
/**
* <p>Subtracts given temporal amount from this machine time. </p>
*
* @param duration other machine time to be subtracted
* @return difference result
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Subtrahiert den angegebenen Zeitbetrag von dieser maschinellen
* Dauer. </p>
*
* @param duration other machine time to be subtracted
* @return difference result
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> minus(MachineTime<U> duration) {
if (duration.isEmpty()) {
return this;
} else if (this.isEmpty()) {
return duration.inverse();
}
long s = Math.subtractExact(this.seconds, duration.seconds);
int f = this.nanos - duration.nanos;
return new MachineTime<>(s, f, this.scale);
}
/**
* <p>Converts this machine duration to its absolute amount. </p>
*
* @return absolute machine time duration, always non-negative
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Wandelt eine maschinelle Dauer in ihren Absolutbetrag um. </p>
*
* @return absolute machine time duration, always non-negative
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> abs() {
if (this.isNegative()) {
return new MachineTime<>(
Math.negateExact(this.seconds), -this.nanos, this.scale);
} else {
return this;
}
}
/**
* <p>Creates a copy with inversed sign. </p>
*
* @return negated machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
/*[deutsch]
* <p>Wandelt eine maschinelle Dauer in ihr negatives Äquivalent
* um. </p>
*
* @return negated machine time duration
* @throws ArithmeticException in case of numerical overflow
* @since 2.0
*/
public MachineTime<U> inverse() {
if (this.isEmpty()) {
return this;
}
return new MachineTime<>(
Math.negateExact(this.seconds), -this.nanos, this.scale);
}
/**
* <p>Multiplies this duration with given factor. </p>
*
* @param factor multiplicand
* @return changed copy of this duration
*/
/*[deutsch]
* <p>Multipliziert diese Dauer mit dem angegebenen Faktor. </p>
*
* @param factor multiplicand
* @return changed copy of this duration
*/
public MachineTime<U> multipliedBy(long factor) {
if (factor == 1) {
return this;
}
BigDecimal value =
this.toBigDecimal().multiply(BigDecimal.valueOf(factor));
MachineTime<?> mt;
if (this.scale == POSIX) {
mt = MachineTime.ofPosixSeconds(value);
} else {
mt = MachineTime.ofSISeconds(value);
}
return cast(mt);
}
/**
* <p>Divides this duration by given divisor using rounding
* mode {@code HALF_UP}. </p>
*
* @param divisor divisor
* @return changed copy of this duration
* @see RoundingMode#HALF_UP
* @deprecated Use {@link #dividedBy(long, RoundingMode) dividedBy(long, RoundingMode.HALF_UP} instead
*/
/*[deutsch]
* <p>Dividiert diese Dauer durch den angegebenen Teiler und
* benutzt die kaufmännische Rundung. </p>
*
* @param divisor Teiler
* @return geänderte Kopie dieser Dauer
* @see RoundingMode#HALF_UP
* @deprecated Use {@link #dividedBy(long, RoundingMode) dividedBy(long, RoundingMode.HALF_UP} instead
*/
@Deprecated
public MachineTime<U> dividedBy(long divisor) {
return this.dividedBy(divisor, RoundingMode.HALF_UP);
}
/**
* <p>Divides this duration by given divisor using given rounding mode. </p>
*
* @param divisor divisor
* @param roundingMode rounding mode to be used in division
* @return changed copy of this duration
* @since 3.23/4.19
*/
/*[deutsch]
* <p>Dividiert diese Dauer durch den angegebenen Teiler und benutzt die angegebene Rundung. </p>
*
* @param divisor divisor
* @param roundingMode rounding mode to be used in division
* @return changed copy of this duration
* @since 3.23/4.19
*/
public MachineTime<U> dividedBy(
long divisor,
RoundingMode roundingMode
) {
if (divisor == 1) {
return this;
}
BigDecimal value =
this.toBigDecimal().setScale(9, RoundingMode.FLOOR).divide(new BigDecimal(divisor), roundingMode);
MachineTime<?> mt;
if (this.scale == POSIX) {
mt = MachineTime.ofPosixSeconds(value);
} else {
mt = MachineTime.ofSISeconds(value);
}
return cast(mt);
}
@Override
public <T extends TimePoint<? super U, T>> T addTo(T time) {
U s, f;
if (this.scale == POSIX) {
s = cast(TimeUnit.SECONDS);
f = cast(TimeUnit.NANOSECONDS);
} else {
s = cast(SI.SECONDS);
f = cast(SI.NANOSECONDS);
}
return time.plus(this.seconds, s).plus(this.nanos, f);
}
@Override
public <T extends TimePoint<? super U, T>> T subtractFrom(T time) {
U s, f;
if (this.scale == POSIX) {
s = cast(TimeUnit.SECONDS);
f = cast(TimeUnit.NANOSECONDS);
} else {
s = cast(SI.SECONDS);
f = cast(SI.NANOSECONDS);
}
return time.minus(this.seconds, s).minus(this.nanos, f);
}
/**
* <p>Compares the absolute lengths and is equivalent to {@code abs().compareTo(other.abs()) < 0}. </p>
*
* @param other another machine time to be compared with
* @return boolean
* @see #compareTo(MachineTime)
* @see #isLongerThan(MachineTime)
* @since 3.20/4.16
*/
/*[deutsch]
* <p>Vergleicht die absoluten Längen und ist äquivalent zu {@code abs().compareTo(other.abs()) < 0}. </p>
*
* @param other another machine time to be compared with
* @return boolean
* @see #compareTo(MachineTime)
* @see #isLongerThan(MachineTime)
* @since 3.20/4.16
*/
public boolean isShorterThan(MachineTime<U> other) {
return (this.abs().compareTo(other.abs()) < 0);
}
/**
* <p>Compares the absolute lengths and is equivalent to {@code abs().compareTo(other.abs()) > 0}. </p>
*
* @param other another machine time to be compared with
* @return boolean
* @see #compareTo(MachineTime)
* @see #isShorterThan(MachineTime)
* @since 3.20/4.16
*/
/*[deutsch]
* <p>Vergleicht die absoluten Längen und ist äquivalent zu {@code abs().compareTo(other.abs()) > 0}. </p>
*
* @param other another machine time to be compared with
* @return boolean
* @see #compareTo(MachineTime)
* @see #isShorterThan(MachineTime)
* @since 3.20/4.16
*/
public boolean isLongerThan(MachineTime<U> other) {
return (this.abs().compareTo(other.abs()) > 0);
}
/**
* <p>Method of the {@code Comparable}-interface. </p>
*
* @param other another machine time to be compared with
* @return negative, zero or positive integer if this instance is shorter, equal or longer than other one
* @throws ClassCastException if this and the other machine time have different time scales
* @see #isShorterThan(MachineTime)
* @see #isLongerThan(MachineTime)
* @since 3.20/4.16
*/
/*[deutsch]
* <p>Methode des {@code Comparable}-Interface. </p>
*
* @param other another machine time to be compared with
* @return negative, zero or positive integer if this instance is shorter, equal or longer than other one
* @throws ClassCastException if this and the other machine time have different time scales
* @see #isShorterThan(MachineTime)
* @see #isLongerThan(MachineTime)
* @since 3.20/4.16
*/
@Override
public int compareTo(MachineTime<U> other) {
if (this.scale == other.scale) {
if (this.seconds < other.seconds) {
return -1;
} else if (this.seconds > other.seconds) {
return 1;
} else {
return (this.nanos - other.nanos);
}
} else {
throw new ClassCastException("Different time scales.");
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof MachineTime) {
MachineTime<?> that = (MachineTime<?>) obj;
return (
(this.seconds == that.seconds)
&& (this.nanos == that.nanos)
&& (this.scale == that.scale));
} else {
return false;
}
}
@Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + (int) (this.seconds ^ (this.seconds >>> 32));
hash = 23 * hash + this.nanos;
hash = 23 * hash + this.scale.hashCode();
return hash;
}
/**
* <p>Returns a format in technical notation including the name of the underlying time scale. </p>
*
* @return String like "-5s [POSIX]" or "4.123456789s [UTC]"
*/
/*[deutsch]
* <p>Returns a format in technical notation including the name of the underlying time scale. </p>
*
* @return String like "-5s [POSIX]" or "4.123456789s [UTC]"
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
this.createNumber(sb);
sb.append("s [");
sb.append(this.scale.name());
sb.append(']');
return sb.toString();
}
/**
* <p>Converts this machine time duration into a decimal number of seconds. </p>
*
* @return BigDecimal
*/
/*[deutsch]
* <p>Wandelt diese maschinelle Dauer in einen dezimalen Sekundenbetrag um. </p>
*
* @return BigDecimal
*/
public BigDecimal toBigDecimal() {
StringBuilder sb = new StringBuilder();
this.createNumber(sb);
return new BigDecimal(sb.toString());
}
private void createNumber(StringBuilder sb) {
if (this.isNegative()) {
sb.append('-');
sb.append(Math.abs(this.seconds));
} else {
sb.append(this.seconds);
}
if (this.nanos != 0) {
sb.append('.');
String fraction = String.valueOf(Math.abs(this.nanos));
for (int i = 9 - fraction.length(); i > 0; i--) {
sb.append('0');
}
sb.append(fraction);
}
}
@SuppressWarnings("unchecked")
private static <U> U cast(Object unit) {
return (U) unit;
}
/**
* @serialData Uses <a href="../../../serialized-form.html#net.time4j.range.SPX">
* a dedicated serialization form</a> as proxy. The layout
* is bit-compressed. The first byte contains within the
* six most significant bits the type id {@code 7} and as
* least significant bit the value 1 if this instance uses
* the UTC-scale. Then the bytes for the seconds and fraction
* follow. The fraction bytes are only written if the fraction
* is not zero. In that case, the second least significant bit
* of the header is set, too.
*
* Schematic algorithm:
*
* <pre>
* byte header = (7 << 2);
* if (scale == TimeScale.UTC) header |= 1;
* if (this.getFraction() > 0) header |= 2;
* out.writeByte(header);
* out.writeLong(getSeconds());
* if (this.getFraction() > 0) {
* out.writeInt(getFraction());
* }
* </pre>
*
* @return replacement object in serialization graph
*/
private Object writeReplace() {
return new SPX(this, SPX.MACHINE_TIME_TYPE);
}
/**
* @serialData Blocks because a serialization proxy is required.
* @param in object input stream
* @throws InvalidObjectException (always)
*/
private void readObject(ObjectInputStream in)
throws IOException {
throw new InvalidObjectException("Serialization proxy required.");
}
//~ Innere Klassen ----------------------------------------------------
private static class Metric<U>
implements TimeMetric<TimeUnit, MachineTime<U>> {
//~ Instanzvariablen ----------------------------------------------
private final TimeScale scale;
//~ Konstruktoren -------------------------------------------------
private Metric(TimeScale scale) {
super();
this.scale = scale;
}
//~ Methoden ------------------------------------------------------
@Override
public
<T extends TimePoint<? super TimeUnit, T>> MachineTime<U> between(
T start,
T end
) {
long secs;
int nanos;
if (
(this.scale == UTC)
&& (start instanceof UniversalTime)
) {
UniversalTime t1 = (UniversalTime) start;
UniversalTime t2 = (UniversalTime) end;
long utc2 = t2.getElapsedTime(UTC);
long utc1 = t1.getElapsedTime(UTC);
if (utc2 < 0 || utc1 < 0) {
throw new UnsupportedOperationException(
"Cannot calculate SI-duration before 1972-01-01.");
}
secs = utc2 - utc1;
nanos = t2.getNanosecond(UTC) - t1.getNanosecond(UTC);
} else if (start instanceof UnixTime) {
UnixTime t1 = (UnixTime) start;
UnixTime t2 = (UnixTime) end;
secs = t2.getPosixTime() - t1.getPosixTime();
nanos = t2.getNanosecond() - t1.getNanosecond();
} else {
throw new UnsupportedOperationException(
"Machine time requires objects of type 'UnixTime'.");
}
return new MachineTime<>(secs, nanos, this.scale);
}
}
}