diff --git a/src/main/java/org/threeten/extra/scale/MispInstant.java b/src/main/java/org/threeten/extra/scale/MispInstant.java new file mode 100644 index 00000000..55feb27c --- /dev/null +++ b/src/main/java/org/threeten/extra/scale/MispInstant.java @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.threeten.extra.scale; + +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; + +/** + * An instantaneous point on the time-line measured in the MISP time-scale. + *
+ * The java.time classes use the Java time-scale for simplicity.
+ * That scale works on the assumption that the time-line is simple, there are no
+ * leap-seconds and there are always 24 * 60 * 60 seconds in a day.
+ * Unfortunately, the Earth's rotation is not straightforward, and a solar day
+ * does not match this definition.
+ *
+ * This class is an alternative representation based on the MISP time-scale. The + * MISP time-scale is a single incrementing count of SI seconds. There are no + * leap seconds or other discontinuities. + *
+ * The duration between two points on the MISP time-scale is calculated solely + * using this class. Do not use the {@code between} method on {@code Duration} + * as that will lose information. Instead use + * {@link #durationUntil(MispInstant)} on this class. + *
+ * It is intended that most applications will use the {@code Instant} class + * which uses the UTC-SLS mapping from UTC to guarantee 86400 seconds per day. + * + *
+ * The scale is explained in Chapter 6 of the MISB Motion Imagery Handbook. The + * Motion Imagery Handbook can be downloaded from the NSG Registry + * (https://nsgreg.nga.mil). The MISP Time System clock counts SI-Seconds since + * the Epoch of 1970-01-01T00:00:00.0Z (UTC). The MISP Time System is locked + * with TAI with a Leap Second offset of 8.000082 seconds. There are no leap + * seconds or other discontinuities. + * + *
+ * This class may be used for instants in the far past and far future. Since + * some instants will be prior to 1970, it is not strictly an implementation of + * the MISP Time System. However it is equivalent to it since 1970. + * + *
+ * This class must be treated as a value type. Do not synchronize, rely on the
+ * identity hash code or use the distinction between equals() and ==.
+ */
+public final class MispInstant
+ implements Comparable
+ * This is the number of seconds between 1958-01-01T00:00:00(TAI) and
+ * 1970-01-01T00:00.00(UTC).
+ */
+ private static final long TAI_OFFSET_SEC = 378691208;
+
+ /**
+ * Nanoseconds part of the difference between TAI and MISP.
+ *
+ *
+ * This is the fractional part of the number of seconds between
+ * 1958-01-01T00:00:00(TAI) and 1970-01-01T00:00.00(UTC).
+ */
+ private static final long TAI_OFFSET_NANOS = 82000;
+
+ /**
+ * The number of seconds from the epoch of 1970-01-01T00:00:00(UTC).
+ */
+ private final long seconds;
+
+ /**
+ * The number of nanoseconds, later along the time-line, from the seconds
+ * field. This is always positive, and never exceeds 999,999,999.
+ */
+ private final int nanos;
+
+ /**
+ * Obtains an instance of {@code MispInstant} from the number of seconds
+ * from the MISP epoch of 1970-01-01T00:00:00(UTC) with a nanosecond
+ * fraction of second.
+ *
+ * This method allows an arbitrary number of nanoseconds to be passed in.
+ * The factory will alter the values of the second and nanosecond in order
+ * to ensure that the stored nanosecond is in the range 0 to 999,999,999.
+ * For example, the following will result in the exactly the same instant:
+ *
+ * Converting a UTC-SLS instant to the MISP Time System requires leap second
+ * rules. This method uses the latest available system rules.
+ *
+ * Conversion from an {@link Instant} will not be completely accurate near a
+ * leap second in accordance with UTC-SLS.
+ *
+ * @param instant the instant to convert, not null
+ * @return the MISP instant, not null
+ * @throws DateTimeException if the range of {@code MispInstant} is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public static MispInstant of(Instant instant) {
+ return UtcRules.system().convertToMisp(instant);
+ }
+
+ /**
+ * Obtains an instance of {@code MispInstant} from a {@code UtcInstant}.
+ *
+ * Converting a UTC instant to MISP requires leap second rules. This method
+ * uses the latest available system rules.
+ *
+ * The {@code MispInstant} will represent exactly the same point on the
+ * time-line as per the available leap-second rules. If the leap-second
+ * rules change then conversion back to UTC may result in a different
+ * instant.
+ *
+ * @param instant the instant to convert, not null
+ * @return the MISP instant, not null
+ * @throws DateTimeException if the range of {@code MispInstant} is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public static MispInstant of(UtcInstant instant) {
+ return UtcRules.system().convertToMisp(instant);
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param mispSeconds the number of MISP seconds from the epoch
+ * @param nanoOfSecond the nanoseconds within the second, from 0 to
+ * 999,999,999
+ */
+ private MispInstant(long mispSeconds, int nanoOfSecond) {
+ super();
+ this.seconds = mispSeconds;
+ this.nanos = nanoOfSecond;
+ }
+
+ /**
+ * Gets the number of seconds from the MISP epoch of 1970-01-01T00:00:00(UTC).
+ *
+ * The MISP second count is a simple incrementing count of seconds where
+ * second 0 is 1970-01-01T00:00:00(UTC). The nanosecond part of the second
+ * is returned by {@link getNano}.
+ *
+ * @return the seconds from the epoch of 1970-01-01T00:00:00(UTC)
+ */
+ public long getMispSeconds() {
+ return seconds;
+ }
+
+ /**
+ * Returns a copy of this {@code MispInstant} with the number of seconds from the MISP epoch of 1970-01-01T00:00:00(UTC).
+ *
+ * The MISP second count is a simple incrementing count of seconds where
+ * second 0 is 1970-01-01T00:00:00(UTC). The nanosecond part of the second
+ * is returned by {@link getNano}.
+ *
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param mispSeconds the number of seconds from the epoch of
+ * 1970-01-01T00:00:00(UTC)
+ * @return a {@code MispInstant} based on this instant with the requested
+ * second, not null
+ */
+ public MispInstant withMispSeconds(long mispSeconds) {
+ return ofMispSeconds(mispSeconds, nanos);
+ }
+
+ /**
+ * Gets the number of nanoseconds, later along the time-line, from the start of the second.
+ *
+ * The nanosecond-of-second value measures the total number of nanoseconds
+ * from the second returned by {@link getMispSeconds()}.
+ *
+ * @return the nanoseconds within the second, from 0 to 999,999,999
+ */
+ public int getNano() {
+ return nanos;
+ }
+
+ /**
+ * Returns a copy of this {@code MispInstant} with the nano-of-second value changed.
+ *
+ * The nanosecond-of-second value measures the total number of nanoseconds
+ * from the second returned by {@link getMispSeconds()}.
+ *
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param nanoOfSecond the nano-of-second, from 0 to 999,999,999
+ * @return a {@code MispInstant} based on this instant with the requested nano-of-second, not null
+ * @throws IllegalArgumentException if nanoOfSecond is out of range
+ */
+ public MispInstant withNano(int nanoOfSecond) {
+ if (nanoOfSecond < 0 || nanoOfSecond >= NANOS_PER_SECOND) {
+ throw new IllegalArgumentException("NanoOfSecond must be from 0 to 999,999,999");
+ }
+ return ofMispSeconds(seconds, nanoOfSecond);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration added.
+ *
+ * The duration is added using simple addition of the seconds and
+ * nanoseconds in the duration to the seconds and nanoseconds of this
+ * instant. As a result, the duration is treated as being measured in SI
+ * seconds for the purpose of this method.
+ *
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param duration the duration to add, not null
+ * @return a {@code MispInstant} based on this instant with the duration added, not null
+ * @throws ArithmeticException if the calculation exceeds the supported
+ * range
+ */
+ public MispInstant plus(Duration duration) {
+ long secsToAdd = duration.getSeconds();
+ int nanosToAdd = duration.getNano();
+ if ((secsToAdd | nanosToAdd) == 0) {
+ return this;
+ }
+ long secs = Math.addExact(seconds, secsToAdd);
+ long nanoAdjustment = ((long) nanos) + nanosToAdd; // safe int+int
+ return ofMispSeconds(secs, nanoAdjustment);
+ }
+
+ /**
+ * Returns a copy of this instant with the specified duration subtracted.
+ *
+ * The duration is subtracted using simple subtraction of the seconds and
+ * nanoseconds in the duration from the seconds and nanoseconds of this
+ * instant. As a result, the duration is treated as being measured in SI
+ * seconds for the purpose of this method.
+ *
+ * This instance is immutable and unaffected by this method call.
+ *
+ * @param duration the duration to subtract, not null
+ * @return a {@code MispInstant} based on this instant with the duration subtracted, not null
+ * @throws ArithmeticException if the calculation exceeds the supported range
+ */
+ public MispInstant minus(Duration duration) {
+ long secsToSubtract = duration.getSeconds();
+ int nanosToSubtract = duration.getNano();
+ if ((secsToSubtract | nanosToSubtract) == 0) {
+ return this;
+ }
+ long secs = Math.subtractExact(seconds, secsToSubtract);
+ long nanoAdjustment = ((long) nanos) - nanosToSubtract; // safe int+int
+ return ofMispSeconds(secs, nanoAdjustment);
+ }
+
+ /**
+ * Returns the duration between this instant and the specified instant.
+ *
+ * This calculates the duration between this instant and another based on
+ * the MISP time scale. Adding the duration to this instant using
+ * {@link #plus} will always result in an instant equal to the specified
+ * instant.
+ *
+ * @param otherInstant the instant to calculate the duration until, not null
+ * @return the duration until the specified instant, may be negative, not null
+ * @throws ArithmeticException if the calculation exceeds the supported range
+ */
+ public Duration durationUntil(MispInstant otherInstant) {
+ long durSecs = Math.subtractExact(otherInstant.seconds, seconds);
+ long durNanos = otherInstant.nanos - nanos;
+ return Duration.ofSeconds(durSecs, durNanos);
+ }
+
+ /**
+ * Converts this instant to an {@code Instant}.
+ *
+ * Converting a MISP instant to UTC-SLS requires leap second rules. This
+ * method uses the latest available system rules. The conversion first maps
+ * from the MISP time system to UTC, then converts to UTC-SLS.
+ *
+ * Conversion to an {@link Instant} will not be completely accurate near a
+ * leap second in accordance with UTC-SLS.
+ *
+ * @return an {@code Instant} representing the best approximation of this instant, not null
+ * @throws DateTimeException if the range of {@code Instant} is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant toInstant() {
+ return UtcRules.system().convertToInstant(this);
+ }
+
+ /**
+ * Converts this instant to a {@code UtcInstant}.
+ *
+ * Converting a MISP instant to UTC requires leap second rules. This method
+ * uses the latest available system rules.
+ *
+ * The {@link UtcInstant} will represent exactly the same point on the
+ * time-line as per the available leap-second rules. If the leap-second
+ * rules change then conversion back to the MISP time system may result in a
+ * different instant.
+ *
+ * @return a {@code UtcInstant} representing the same instant, not null
+ * @throws DateTimeException if the range of {@code UtcInstant} is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public UtcInstant toUtcInstant() {
+ return UtcRules.system().convertToUtc(this);
+ }
+
+ /**
+ * Converts this instant to a {@code TaiInstant}.
+ *
+ * @return a {@code TaiInstant} representing the same instant, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public TaiInstant toTaiInstant() {
+ long secs = seconds + TAI_OFFSET_SEC;
+ long nos = nanos + TAI_OFFSET_NANOS;
+ return TaiInstant.ofTaiSeconds(secs, nos);
+ }
+
+ /**
+ * Compares this instant to another based on the time-line.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return the comparator value, negative if less, positive if greater
+ */
+ @Override
+ public int compareTo(MispInstant otherInstant) {
+ int cmp = Long.compare(seconds, otherInstant.seconds);
+ if (cmp != 0) {
+ return cmp;
+ }
+ return nanos - otherInstant.nanos;
+ }
+
+ /**
+ * Checks if this instant is after the specified instant.
+ *
+ * The comparison is based on the time-line position of the instants.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return true if this instant is after the specified instant
+ * @throws NullPointerException if otherInstant is null
+ */
+ public boolean isAfter(MispInstant otherInstant) {
+ return compareTo(otherInstant) > 0;
+ }
+
+ /**
+ * Checks if this instant is before the specified instant.
+ *
+ * The comparison is based on the time-line position of the instants.
+ *
+ * @param otherInstant the other instant to compare to, not null
+ * @return true if this instant is before the specified instant
+ * @throws NullPointerException if otherInstant is null
+ */
+ public boolean isBefore(MispInstant otherInstant) {
+ return compareTo(otherInstant) < 0;
+ }
+
+ /**
+ * Checks if this instant is equal to the specified {@code MispInstant}.
+ *
+ * @param otherInstant the other instant, null returns false
+ * @return true if the other instant is equal to this one
+ */
+ @Override
+ public boolean equals(Object otherInstant) {
+ if (this == otherInstant) {
+ return true;
+ }
+ if (otherInstant instanceof MispInstant) {
+ MispInstant other = (MispInstant) otherInstant;
+ return this.seconds == other.seconds
+ && this.nanos == other.nanos;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code for this instant.
+ *
+ * @return a suitable hash code
+ */
+ @Override
+ public int hashCode() {
+ return ((int) (seconds ^ (seconds >>> 32))) + 51 * nanos;
+ }
+
+}
diff --git a/src/main/java/org/threeten/extra/scale/SystemUtcRules.java b/src/main/java/org/threeten/extra/scale/SystemUtcRules.java
index f1ac8dbe..9ceab961 100644
--- a/src/main/java/org/threeten/extra/scale/SystemUtcRules.java
+++ b/src/main/java/org/threeten/extra/scale/SystemUtcRules.java
@@ -75,6 +75,22 @@ final class SystemUtcRules extends UtcRules implements Serializable {
*/
static final SystemUtcRules INSTANCE = new SystemUtcRules();
+ /**
+ * Seconds part of the difference between TAI and MISP.
+ *
+ * This is the number of seconds between 1958-01-01T00:00:00(TAI) and
+ * 1970-01-01T00:00.00(UTC).
+ */
+ private static final long TAI_OFFSET_SEC = 378691208;
+
+ /**
+ * Nanoseconds part of the difference between TAI and MISP.
+ *
+ * This is the fractional part of the number of seconds between
+ * 1958-01-01T00:00:00(TAI) and 1970-01-01T00:00.00(UTC).
+ */
+ private static final long TAI_OFFSET_NANOS = 82000;
+
/**
* The table of leap second dates.
*/
@@ -206,6 +222,14 @@ public UtcInstant convertToUtc(TaiInstant taiInstant) {
return UtcInstant.ofModifiedJulianDay(mjd, nod);
}
+ @Override
+ public UtcInstant convertToUtc(MispInstant mispInstant) {
+ long secs = mispInstant.getMispSeconds() + TAI_OFFSET_SEC;
+ long nos = mispInstant.getNano() + TAI_OFFSET_NANOS;
+ TaiInstant tai = TaiInstant.ofTaiSeconds(secs, nos);
+ return convertToUtc(tai);
+ }
+
//-----------------------------------------------------------------------
/**
* Loads the rules from files in the class loader, often jar files.
diff --git a/src/main/java/org/threeten/extra/scale/UtcRules.java b/src/main/java/org/threeten/extra/scale/UtcRules.java
index 212ffc2a..ac185b57 100644
--- a/src/main/java/org/threeten/extra/scale/UtcRules.java
+++ b/src/main/java/org/threeten/extra/scale/UtcRules.java
@@ -226,6 +226,70 @@ public TaiInstant convertToTai(UtcInstant utcInstant) {
*/
public abstract UtcInstant convertToUtc(TaiInstant taiInstant);
+ /**
+ * Converts a {@code MispInstant} to a {@code UtcInstant}.
+ *
+ * This method converts from the MISP time scale to the UTC time scale using the
+ * leap-second rules of the implementation.
+ *
+ * @param mispInstant the MISP instant to convert, not null
+ * @return the converted UTC instant, not null
+ * @throws DateTimeException if the valid range is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public abstract UtcInstant convertToUtc(MispInstant mispInstant);
+
+ //-----------------------------------------------------------------------
+ /**
+ * Converts a {@code UtcInstant} to a {@code MispInstant}.
+ *
+ * This method converts from the UTC to the MISP time-scale using the
+ * leap-second rules of the implementation.
+ *
+ * @param utcInstant the UTC instant to convert, not null
+ * @return the converted MISP instant, not null
+ * @throws DateTimeException if the valid range is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public MispInstant convertToMisp(UtcInstant utcInstant) {
+ TaiInstant tai = convertToTai(utcInstant);
+ return MispInstant.of(tai);
+ }
+
+ /**
+ * Converts a {@code MispInstant} to an {@code Instant}.
+ *
+ * This method converts from the MISP time-scale to one with 86400 subdivisions
+ * per day using the leap-second rules of the implementation.
+ *
+ * The standard implementation uses UTC-SLS. It uses
+ * {@link #convertToUtc(MispInstant)} and {@link #convertToInstant(UtcInstant)}.
+ *
+ * @param mispInstant the MISP instant to convert, not null
+ * @return the converted instant, not null
+ * @throws DateTimeException if the valid range is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public Instant convertToInstant(MispInstant mispInstant) {
+ return convertToInstant(convertToUtc(mispInstant));
+ }
+
+ /**
+ * Converts an {@code Instant} to a {@code MispInstant}.
+ *
+ * This method converts from UTC-SLS to the MISP time-scale using the
+ * leap-second rules of the implementation.
+ *
+ * @param instant the Instant to convert, not null
+ * @return the converted MISP instant, not null
+ * @throws DateTimeException if the valid range is exceeded
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public MispInstant convertToMisp(Instant instant) {
+ TaiInstant tai = convertToTai(instant);
+ return MispInstant.of(tai);
+ }
+
//-----------------------------------------------------------------------
/**
* Converts a {@code UtcInstant} to an {@code Instant}.
diff --git a/src/test/java/org/threeten/extra/scale/MockUtcRulesAlwaysLeap.java b/src/test/java/org/threeten/extra/scale/MockUtcRulesAlwaysLeap.java
index 51db5187..8397cfce 100644
--- a/src/test/java/org/threeten/extra/scale/MockUtcRulesAlwaysLeap.java
+++ b/src/test/java/org/threeten/extra/scale/MockUtcRulesAlwaysLeap.java
@@ -66,4 +66,9 @@ public UtcInstant convertToUtc(TaiInstant taiInstant) {
return null;
}
+ @Override
+ public UtcInstant convertToUtc(MispInstant mispInstant) {
+ return null;
+ }
+
}
diff --git a/src/test/java/org/threeten/extra/scale/MockUtcRulesLeapOn1000.java b/src/test/java/org/threeten/extra/scale/MockUtcRulesLeapOn1000.java
index b492974c..cbfb1bd1 100644
--- a/src/test/java/org/threeten/extra/scale/MockUtcRulesLeapOn1000.java
+++ b/src/test/java/org/threeten/extra/scale/MockUtcRulesLeapOn1000.java
@@ -66,4 +66,9 @@ public UtcInstant convertToUtc(TaiInstant taiInstant) {
return null;
}
+ @Override
+ public UtcInstant convertToUtc(MispInstant mispInstant) {
+ return null;
+ }
+
}
diff --git a/src/test/java/org/threeten/extra/scale/TestMispInstant.java b/src/test/java/org/threeten/extra/scale/TestMispInstant.java
new file mode 100644
index 00000000..1fd254f8
--- /dev/null
+++ b/src/test/java/org/threeten/extra/scale/TestMispInstant.java
@@ -0,0 +1,741 @@
+/*
+ * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of JSR-310 nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.threeten.extra.scale;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.time.Duration;
+import java.time.Instant;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+
+import com.tngtech.junit.dataprovider.DataProvider;
+import com.tngtech.junit.dataprovider.UseDataProvider;
+
+/**
+ * Test MispInstant.
+ */
+public class TestMispInstant {
+
+ @Test
+ public void test_interfaces() {
+ assertTrue(Serializable.class.isAssignableFrom(MispInstant.class));
+ assertTrue(Comparable.class.isAssignableFrom(MispInstant.class));
+ }
+
+ @Test
+ public void test_serialization() throws Exception {
+ MispInstant test = MispInstant.ofMispSeconds(2, 3);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try ( ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(test);
+ }
+ try ( ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
+ assertEquals(test, ois.readObject());
+ }
+ }
+
+ @Test
+ public void factory_ofMispSecondslong_long() {
+ for (long i = -2; i <= 2; i++) {
+ for (int j = 0; j < 10; j++) {
+ MispInstant t = MispInstant.ofMispSeconds(i, j);
+ assertEquals(i, t.getMispSeconds());
+ assertEquals(j, t.getNano());
+ }
+ for (int j = -10; j < 0; j++) {
+ MispInstant t = MispInstant.ofMispSeconds(i, j);
+ assertEquals(i - 1, t.getMispSeconds());
+ assertEquals(j + 1000000000, t.getNano());
+ }
+ for (int j = 999999990; j < 1000000000; j++) {
+ MispInstant t = MispInstant.ofMispSeconds(i, j);
+ assertEquals(i, t.getMispSeconds());
+ assertEquals(j, t.getNano());
+ }
+ }
+ }
+
+ @Test
+ public void factory_ofMispSeconds_long_long_nanosNegativeAdjusted() {
+ MispInstant test = MispInstant.ofMispSeconds(2L, -1);
+ assertEquals(1, test.getMispSeconds());
+ assertEquals(999999999, test.getNano());
+ }
+
+ @Test
+ public void factory_ofMispSeconds_long_long_tooBig() {
+ assertThrows(ArithmeticException.class, () -> MispInstant.ofMispSeconds(Long.MAX_VALUE, 1000000000));
+ }
+
+ @Test
+ public void factory_of_Instant_null() {
+ assertThrows(NullPointerException.class, () -> MispInstant.of((Instant) null));
+ }
+
+ @Test
+ public void factory_of_UtcInstant_null() {
+ assertThrows(NullPointerException.class, () -> MispInstant.of((UtcInstant) null));
+ }
+
+ @Test
+ public void test_fromTaiInstant() {
+ TaiInstant tai = TaiInstant.parse("378691208.000082000s(TAI)");
+ MispInstant misp = MispInstant.of(tai);
+ assertEquals(0, misp.getMispSeconds());
+ assertEquals(0, misp.getNano());
+ }
+
+ @Test
+ public void test_toTaiInstant() {
+ MispInstant misp = MispInstant.ofMispSeconds(0, 0);
+ TaiInstant tai = misp.toTaiInstant();
+ assertEquals(378691208, tai.getTaiSeconds());
+ assertEquals(82000, tai.getNano());
+ }
+
+ @Test
+ public void test_fromInstant() {
+ Instant instant = Instant.parse("2022-03-05T00:00:08.000082Z");
+ MispInstant misp = MispInstant.of(instant);
+ // 37 seconds for TAI to UTC offset, and 8.000082 for MISP to TAI offset
+ // 59643L is the MJD for 2022-03-05
+ // 40587L is the MJD for 1970-01-01
+ assertEquals((59643L - 40587L) * 24 * 60 * 60 + 37, misp.getMispSeconds());
+ assertEquals(0, misp.getNano());
+ }
+
+ @Test
+ public void test_fromUtcInstant() {
+ UtcInstant utcInstant = UtcInstant.parse("2022-03-05T00:00:08.000082Z");
+ MispInstant misp = MispInstant.of(utcInstant);
+ // 37 seconds for TAI to UTC offset, and 8.000082 for MISP to TAI offset
+ // 59643L is the MJD for 2022-03-05
+ // 40587L is the MJD for 1970-01-01
+ assertEquals((59643L - 40587L) * 24 * 60 * 60 + 37, misp.getMispSeconds());
+ assertEquals(0, misp.getNano());
+ }
+
+ @Test
+ public void test_toUtcInstant() {
+ MispInstant misp1 = MispInstant.ofMispSeconds((59643L - 40587L) * 24 * 60 * 60, 0);
+ MispInstant misp2 = misp1.minus(Duration.ofSeconds(8, 82000));
+ MispInstant misp3 = misp2.plus(Duration.ofSeconds(37, 0));
+ UtcInstant utc = misp3.toUtcInstant();
+ assertEquals("2022-03-05T00:00:00Z", utc.toString());
+ }
+
+ @Test
+ public void test_toInstant() {
+ MispInstant misp1 = MispInstant.ofMispSeconds((59643L - 40587L) * 24 * 60 * 60, 0);
+ MispInstant misp2 = misp1.minus(Duration.ofSeconds(8, 82000));
+ MispInstant misp3 = misp2.plus(Duration.ofSeconds(37, 0));
+ Instant instant = misp3.toInstant();
+ assertEquals("2022-03-05T00:00:00Z", instant.toString());
+ }
+
+ @DataProvider
+ public static Object[][] data_withMispSeconds() {
+ return new Object[][]{
+ {0L, 12345L, 1L, 1L, 12345L},
+ {0L, 12345L, -1L, -1L, 12345L},
+ {7L, 12345L, 2L, 2L, 12345L},
+ {7L, 12345L, -2L, -2L, 12345L},
+ {-99L, 12345L, 3L, 3L, 12345L},
+ {-99L, 12345L, -3L, -3L, 12345L},};
+ }
+
+ @ParameterizedTest
+ @UseDataProvider("data_withMispSeconds")
+ public void test_ofMispSeconds(long secs, long nanos, long newMisp, Long expectedSeconds, Long expectedNanos) {
+ MispInstant i = MispInstant.ofMispSeconds(secs, nanos).withMispSeconds(newMisp);
+ assertEquals(expectedSeconds.longValue(), i.getMispSeconds());
+ assertEquals(expectedNanos.longValue(), i.getNano());
+ }
+
+ @DataProvider
+ public static Object[][] data_withNano() {
+ return new Object[][]{
+ {0L, 12345L, 1, 0L, 1L},
+ {7L, 12345L, 2, 7L, 2L},
+ {-99L, 12345L, 3, -99L, 3L},
+ {-99L, 12345L, 999999999, -99L, 999999999L},
+ {-99L, 12345L, -1, null, null},
+ {-99L, 12345L, 1000000000, null, null},};
+ }
+
+ @ParameterizedTest
+ @UseDataProvider("data_withNano")
+ public void test_withNano(long secs, long nanos, int newNano, Long expectedSeconds, Long expectedNanos) {
+ MispInstant i = MispInstant.ofMispSeconds(secs, nanos);
+ if (expectedSeconds != null) {
+ MispInstant withNano = i.withNano(newNano);
+ assertEquals(expectedSeconds.longValue(), withNano.getMispSeconds());
+ assertEquals(expectedNanos.longValue(), withNano.getNano());
+ } else {
+ assertThrows(IllegalArgumentException.class, () -> i.withNano(newNano));
+ }
+ }
+
+ @DataProvider
+ public static Object[][] data_plus() {
+ return new Object[][]{
+ {Long.MIN_VALUE, 0, Long.MAX_VALUE, 0, -1, 0},
+ {-4, 666666667, -4, 666666667, -7, 333333334},
+ {-4, 666666667, -3, 0, -7, 666666667},
+ {-4, 666666667, -2, 0, -6, 666666667},
+ {-4, 666666667, -1, 0, -5, 666666667},
+ {-4, 666666667, -1, 333333334, -4, 1},
+ {-4, 666666667, -1, 666666667, -4, 333333334},
+ {-4, 666666667, -1, 999999999, -4, 666666666},
+ {-4, 666666667, 0, 0, -4, 666666667},
+ {-4, 666666667, 0, 1, -4, 666666668},
+ {-4, 666666667, 0, 333333333, -3, 0},
+ {-4, 666666667, 0, 666666666, -3, 333333333},
+ {-4, 666666667, 1, 0, -3, 666666667},
+ {-4, 666666667, 2, 0, -2, 666666667},
+ {-4, 666666667, 3, 0, -1, 666666667},
+ {-4, 666666667, 3, 333333333, 0, 0},
+ {-3, 0, -4, 666666667, -7, 666666667},
+ {-3, 0, -3, 0, -6, 0},
+ {-3, 0, -2, 0, -5, 0},
+ {-3, 0, -1, 0, -4, 0},
+ {-3, 0, -1, 333333334, -4, 333333334},
+ {-3, 0, -1, 666666667, -4, 666666667},
+ {-3, 0, -1, 999999999, -4, 999999999},
+ {-3, 0, 0, 0, -3, 0},
+ {-3, 0, 0, 1, -3, 1},
+ {-3, 0, 0, 333333333, -3, 333333333},
+ {-3, 0, 0, 666666666, -3, 666666666},
+ {-3, 0, 1, 0, -2, 0},
+ {-3, 0, 2, 0, -1, 0},
+ {-3, 0, 3, 0, 0, 0},
+ {-3, 0, 3, 333333333, 0, 333333333},
+ {-2, 0, -4, 666666667, -6, 666666667},
+ {-2, 0, -3, 0, -5, 0},
+ {-2, 0, -2, 0, -4, 0},
+ {-2, 0, -1, 0, -3, 0},
+ {-2, 0, -1, 333333334, -3, 333333334},
+ {-2, 0, -1, 666666667, -3, 666666667},
+ {-2, 0, -1, 999999999, -3, 999999999},
+ {-2, 0, 0, 0, -2, 0},
+ {-2, 0, 0, 1, -2, 1},
+ {-2, 0, 0, 333333333, -2, 333333333},
+ {-2, 0, 0, 666666666, -2, 666666666},
+ {-2, 0, 1, 0, -1, 0},
+ {-2, 0, 2, 0, 0, 0},
+ {-2, 0, 3, 0, 1, 0},
+ {-2, 0, 3, 333333333, 1, 333333333},
+ {-1, 0, -4, 666666667, -5, 666666667},
+ {-1, 0, -3, 0, -4, 0},
+ {-1, 0, -2, 0, -3, 0},
+ {-1, 0, -1, 0, -2, 0},
+ {-1, 0, -1, 333333334, -2, 333333334},
+ {-1, 0, -1, 666666667, -2, 666666667},
+ {-1, 0, -1, 999999999, -2, 999999999},
+ {-1, 0, 0, 0, -1, 0},
+ {-1, 0, 0, 1, -1, 1},
+ {-1, 0, 0, 333333333, -1, 333333333},
+ {-1, 0, 0, 666666666, -1, 666666666},
+ {-1, 0, 1, 0, 0, 0},
+ {-1, 0, 2, 0, 1, 0},
+ {-1, 0, 3, 0, 2, 0},
+ {-1, 0, 3, 333333333, 2, 333333333},
+ {-1, 666666667, -4, 666666667, -4, 333333334},
+ {-1, 666666667, -3, 0, -4, 666666667},
+ {-1, 666666667, -2, 0, -3, 666666667},
+ {-1, 666666667, -1, 0, -2, 666666667},
+ {-1, 666666667, -1, 333333334, -1, 1},
+ {-1, 666666667, -1, 666666667, -1, 333333334},
+ {-1, 666666667, -1, 999999999, -1, 666666666},
+ {-1, 666666667, 0, 0, -1, 666666667},
+ {-1, 666666667, 0, 1, -1, 666666668},
+ {-1, 666666667, 0, 333333333, 0, 0},
+ {-1, 666666667, 0, 666666666, 0, 333333333},
+ {-1, 666666667, 1, 0, 0, 666666667},
+ {-1, 666666667, 2, 0, 1, 666666667},
+ {-1, 666666667, 3, 0, 2, 666666667},
+ {-1, 666666667, 3, 333333333, 3, 0},
+ {0, 0, -4, 666666667, -4, 666666667},
+ {0, 0, -3, 0, -3, 0},
+ {0, 0, -2, 0, -2, 0},
+ {0, 0, -1, 0, -1, 0},
+ {0, 0, -1, 333333334, -1, 333333334},
+ {0, 0, -1, 666666667, -1, 666666667},
+ {0, 0, -1, 999999999, -1, 999999999},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 1, 0, 1},
+ {0, 0, 0, 333333333, 0, 333333333},
+ {0, 0, 0, 666666666, 0, 666666666},
+ {0, 0, 1, 0, 1, 0},
+ {0, 0, 2, 0, 2, 0},
+ {0, 0, 3, 0, 3, 0},
+ {0, 0, 3, 333333333, 3, 333333333},
+ {0, 333333333, -4, 666666667, -3, 0},
+ {0, 333333333, -3, 0, -3, 333333333},
+ {0, 333333333, -2, 0, -2, 333333333},
+ {0, 333333333, -1, 0, -1, 333333333},
+ {0, 333333333, -1, 333333334, -1, 666666667},
+ {0, 333333333, -1, 666666667, 0, 0},
+ {0, 333333333, -1, 999999999, 0, 333333332},
+ {0, 333333333, 0, 0, 0, 333333333},
+ {0, 333333333, 0, 1, 0, 333333334},
+ {0, 333333333, 0, 333333333, 0, 666666666},
+ {0, 333333333, 0, 666666666, 0, 999999999},
+ {0, 333333333, 1, 0, 1, 333333333},
+ {0, 333333333, 2, 0, 2, 333333333},
+ {0, 333333333, 3, 0, 3, 333333333},
+ {0, 333333333, 3, 333333333, 3, 666666666},
+ {1, 0, -4, 666666667, -3, 666666667},
+ {1, 0, -3, 0, -2, 0},
+ {1, 0, -2, 0, -1, 0},
+ {1, 0, -1, 0, 0, 0},
+ {1, 0, -1, 333333334, 0, 333333334},
+ {1, 0, -1, 666666667, 0, 666666667},
+ {1, 0, -1, 999999999, 0, 999999999},
+ {1, 0, 0, 0, 1, 0},
+ {1, 0, 0, 1, 1, 1},
+ {1, 0, 0, 333333333, 1, 333333333},
+ {1, 0, 0, 666666666, 1, 666666666},
+ {1, 0, 1, 0, 2, 0},
+ {1, 0, 2, 0, 3, 0},
+ {1, 0, 3, 0, 4, 0},
+ {1, 0, 3, 333333333, 4, 333333333},
+ {2, 0, -4, 666666667, -2, 666666667},
+ {2, 0, -3, 0, -1, 0},
+ {2, 0, -2, 0, 0, 0},
+ {2, 0, -1, 0, 1, 0},
+ {2, 0, -1, 333333334, 1, 333333334},
+ {2, 0, -1, 666666667, 1, 666666667},
+ {2, 0, -1, 999999999, 1, 999999999},
+ {2, 0, 0, 0, 2, 0},
+ {2, 0, 0, 1, 2, 1},
+ {2, 0, 0, 333333333, 2, 333333333},
+ {2, 0, 0, 666666666, 2, 666666666},
+ {2, 0, 1, 0, 3, 0},
+ {2, 0, 2, 0, 4, 0},
+ {2, 0, 3, 0, 5, 0},
+ {2, 0, 3, 333333333, 5, 333333333},
+ {3, 0, -4, 666666667, -1, 666666667},
+ {3, 0, -3, 0, 0, 0},
+ {3, 0, -2, 0, 1, 0},
+ {3, 0, -1, 0, 2, 0},
+ {3, 0, -1, 333333334, 2, 333333334},
+ {3, 0, -1, 666666667, 2, 666666667},
+ {3, 0, -1, 999999999, 2, 999999999},
+ {3, 0, 0, 0, 3, 0},
+ {3, 0, 0, 1, 3, 1},
+ {3, 0, 0, 333333333, 3, 333333333},
+ {3, 0, 0, 666666666, 3, 666666666},
+ {3, 0, 1, 0, 4, 0},
+ {3, 0, 2, 0, 5, 0},
+ {3, 0, 3, 0, 6, 0},
+ {3, 0, 3, 333333333, 6, 333333333},
+ {3, 333333333, -4, 666666667, 0, 0},
+ {3, 333333333, -3, 0, 0, 333333333},
+ {3, 333333333, -2, 0, 1, 333333333},
+ {3, 333333333, -1, 0, 2, 333333333},
+ {3, 333333333, -1, 333333334, 2, 666666667},
+ {3, 333333333, -1, 666666667, 3, 0},
+ {3, 333333333, -1, 999999999, 3, 333333332},
+ {3, 333333333, 0, 0, 3, 333333333},
+ {3, 333333333, 0, 1, 3, 333333334},
+ {3, 333333333, 0, 333333333, 3, 666666666},
+ {3, 333333333, 0, 666666666, 3, 999999999},
+ {3, 333333333, 1, 0, 4, 333333333},
+ {3, 333333333, 2, 0, 5, 333333333},
+ {3, 333333333, 3, 0, 6, 333333333},
+ {3, 333333333, 3, 333333333, 6, 666666666},
+ {Long.MAX_VALUE, 0, Long.MIN_VALUE, 0, -1, 0},};
+ }
+
+ @ParameterizedTest
+ @UseDataProvider("data_plus")
+ public void test_plus(long seconds, int nanos, long plusSeconds, int plusNanos, long expectedSeconds, int expectedNanoOfSecond) {
+ MispInstant i = MispInstant.ofMispSeconds(seconds, nanos).plus(Duration.ofSeconds(plusSeconds, plusNanos));
+ assertEquals(expectedSeconds, i.getMispSeconds());
+ assertEquals(expectedNanoOfSecond, i.getNano());
+ }
+
+ @Test
+ public void test_plus_overflowTooBig() {
+ MispInstant i = MispInstant.ofMispSeconds(Long.MAX_VALUE, 999999999);
+ assertThrows(ArithmeticException.class, () -> i.plus(Duration.ofSeconds(0, 1)));
+ }
+
+ @Test
+ public void test_plus_overflowTooSmall() {
+ MispInstant i = MispInstant.ofMispSeconds(Long.MIN_VALUE, 0);
+ assertThrows(ArithmeticException.class, () -> i.plus(Duration.ofSeconds(-1, 999999999)));
+ }
+
+ @DataProvider
+ public static Object[][] data_minus() {
+ return new Object[][]{
+ {Long.MIN_VALUE, 0, Long.MIN_VALUE + 1, 0, -1, 0},
+ {-4, 666666667, -4, 666666667, 0, 0},
+ {-4, 666666667, -3, 0, -1, 666666667},
+ {-4, 666666667, -2, 0, -2, 666666667},
+ {-4, 666666667, -1, 0, -3, 666666667},
+ {-4, 666666667, -1, 333333334, -3, 333333333},
+ {-4, 666666667, -1, 666666667, -3, 0},
+ {-4, 666666667, -1, 999999999, -4, 666666668},
+ {-4, 666666667, 0, 0, -4, 666666667},
+ {-4, 666666667, 0, 1, -4, 666666666},
+ {-4, 666666667, 0, 333333333, -4, 333333334},
+ {-4, 666666667, 0, 666666666, -4, 1},
+ {-4, 666666667, 1, 0, -5, 666666667},
+ {-4, 666666667, 2, 0, -6, 666666667},
+ {-4, 666666667, 3, 0, -7, 666666667},
+ {-4, 666666667, 3, 333333333, -7, 333333334},
+ {-3, 0, -4, 666666667, 0, 333333333},
+ {-3, 0, -3, 0, 0, 0},
+ {-3, 0, -2, 0, -1, 0},
+ {-3, 0, -1, 0, -2, 0},
+ {-3, 0, -1, 333333334, -3, 666666666},
+ {-3, 0, -1, 666666667, -3, 333333333},
+ {-3, 0, -1, 999999999, -3, 1},
+ {-3, 0, 0, 0, -3, 0},
+ {-3, 0, 0, 1, -4, 999999999},
+ {-3, 0, 0, 333333333, -4, 666666667},
+ {-3, 0, 0, 666666666, -4, 333333334},
+ {-3, 0, 1, 0, -4, 0},
+ {-3, 0, 2, 0, -5, 0},
+ {-3, 0, 3, 0, -6, 0},
+ {-3, 0, 3, 333333333, -7, 666666667},
+ {-2, 0, -4, 666666667, 1, 333333333},
+ {-2, 0, -3, 0, 1, 0},
+ {-2, 0, -2, 0, 0, 0},
+ {-2, 0, -1, 0, -1, 0},
+ {-2, 0, -1, 333333334, -2, 666666666},
+ {-2, 0, -1, 666666667, -2, 333333333},
+ {-2, 0, -1, 999999999, -2, 1},
+ {-2, 0, 0, 0, -2, 0},
+ {-2, 0, 0, 1, -3, 999999999},
+ {-2, 0, 0, 333333333, -3, 666666667},
+ {-2, 0, 0, 666666666, -3, 333333334},
+ {-2, 0, 1, 0, -3, 0},
+ {-2, 0, 2, 0, -4, 0},
+ {-2, 0, 3, 0, -5, 0},
+ {-2, 0, 3, 333333333, -6, 666666667},
+ {-1, 0, -4, 666666667, 2, 333333333},
+ {-1, 0, -3, 0, 2, 0},
+ {-1, 0, -2, 0, 1, 0},
+ {-1, 0, -1, 0, 0, 0},
+ {-1, 0, -1, 333333334, -1, 666666666},
+ {-1, 0, -1, 666666667, -1, 333333333},
+ {-1, 0, -1, 999999999, -1, 1},
+ {-1, 0, 0, 0, -1, 0},
+ {-1, 0, 0, 1, -2, 999999999},
+ {-1, 0, 0, 333333333, -2, 666666667},
+ {-1, 0, 0, 666666666, -2, 333333334},
+ {-1, 0, 1, 0, -2, 0},
+ {-1, 0, 2, 0, -3, 0},
+ {-1, 0, 3, 0, -4, 0},
+ {-1, 0, 3, 333333333, -5, 666666667},
+ {-1, 666666667, -4, 666666667, 3, 0},
+ {-1, 666666667, -3, 0, 2, 666666667},
+ {-1, 666666667, -2, 0, 1, 666666667},
+ {-1, 666666667, -1, 0, 0, 666666667},
+ {-1, 666666667, -1, 333333334, 0, 333333333},
+ {-1, 666666667, -1, 666666667, 0, 0},
+ {-1, 666666667, -1, 999999999, -1, 666666668},
+ {-1, 666666667, 0, 0, -1, 666666667},
+ {-1, 666666667, 0, 1, -1, 666666666},
+ {-1, 666666667, 0, 333333333, -1, 333333334},
+ {-1, 666666667, 0, 666666666, -1, 1},
+ {-1, 666666667, 1, 0, -2, 666666667},
+ {-1, 666666667, 2, 0, -3, 666666667},
+ {-1, 666666667, 3, 0, -4, 666666667},
+ {-1, 666666667, 3, 333333333, -4, 333333334},
+ {0, 0, -4, 666666667, 3, 333333333},
+ {0, 0, -3, 0, 3, 0},
+ {0, 0, -2, 0, 2, 0},
+ {0, 0, -1, 0, 1, 0},
+ {0, 0, -1, 333333334, 0, 666666666},
+ {0, 0, -1, 666666667, 0, 333333333},
+ {0, 0, -1, 999999999, 0, 1},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 1, -1, 999999999},
+ {0, 0, 0, 333333333, -1, 666666667},
+ {0, 0, 0, 666666666, -1, 333333334},
+ {0, 0, 1, 0, -1, 0},
+ {0, 0, 2, 0, -2, 0},
+ {0, 0, 3, 0, -3, 0},
+ {0, 0, 3, 333333333, -4, 666666667},
+ {0, 333333333, -4, 666666667, 3, 666666666},
+ {0, 333333333, -3, 0, 3, 333333333},
+ {0, 333333333, -2, 0, 2, 333333333},
+ {0, 333333333, -1, 0, 1, 333333333},
+ {0, 333333333, -1, 333333334, 0, 999999999},
+ {0, 333333333, -1, 666666667, 0, 666666666},
+ {0, 333333333, -1, 999999999, 0, 333333334},
+ {0, 333333333, 0, 0, 0, 333333333},
+ {0, 333333333, 0, 1, 0, 333333332},
+ {0, 333333333, 0, 333333333, 0, 0},
+ {0, 333333333, 0, 666666666, -1, 666666667},
+ {0, 333333333, 1, 0, -1, 333333333},
+ {0, 333333333, 2, 0, -2, 333333333},
+ {0, 333333333, 3, 0, -3, 333333333},
+ {0, 333333333, 3, 333333333, -3, 0},
+ {1, 0, -4, 666666667, 4, 333333333},
+ {1, 0, -3, 0, 4, 0},
+ {1, 0, -2, 0, 3, 0},
+ {1, 0, -1, 0, 2, 0},
+ {1, 0, -1, 333333334, 1, 666666666},
+ {1, 0, -1, 666666667, 1, 333333333},
+ {1, 0, -1, 999999999, 1, 1},
+ {1, 0, 0, 0, 1, 0},
+ {1, 0, 0, 1, 0, 999999999},
+ {1, 0, 0, 333333333, 0, 666666667},
+ {1, 0, 0, 666666666, 0, 333333334},
+ {1, 0, 1, 0, 0, 0},
+ {1, 0, 2, 0, -1, 0},
+ {1, 0, 3, 0, -2, 0},
+ {1, 0, 3, 333333333, -3, 666666667},
+ {2, 0, -4, 666666667, 5, 333333333},
+ {2, 0, -3, 0, 5, 0},
+ {2, 0, -2, 0, 4, 0},
+ {2, 0, -1, 0, 3, 0},
+ {2, 0, -1, 333333334, 2, 666666666},
+ {2, 0, -1, 666666667, 2, 333333333},
+ {2, 0, -1, 999999999, 2, 1},
+ {2, 0, 0, 0, 2, 0},
+ {2, 0, 0, 1, 1, 999999999},
+ {2, 0, 0, 333333333, 1, 666666667},
+ {2, 0, 0, 666666666, 1, 333333334},
+ {2, 0, 1, 0, 1, 0},
+ {2, 0, 2, 0, 0, 0},
+ {2, 0, 3, 0, -1, 0},
+ {2, 0, 3, 333333333, -2, 666666667},
+ {3, 0, -4, 666666667, 6, 333333333},
+ {3, 0, -3, 0, 6, 0},
+ {3, 0, -2, 0, 5, 0},
+ {3, 0, -1, 0, 4, 0},
+ {3, 0, -1, 333333334, 3, 666666666},
+ {3, 0, -1, 666666667, 3, 333333333},
+ {3, 0, -1, 999999999, 3, 1},
+ {3, 0, 0, 0, 3, 0},
+ {3, 0, 0, 1, 2, 999999999},
+ {3, 0, 0, 333333333, 2, 666666667},
+ {3, 0, 0, 666666666, 2, 333333334},
+ {3, 0, 1, 0, 2, 0},
+ {3, 0, 2, 0, 1, 0},
+ {3, 0, 3, 0, 0, 0},
+ {3, 0, 3, 333333333, -1, 666666667},
+ {3, 333333333, -4, 666666667, 6, 666666666},
+ {3, 333333333, -3, 0, 6, 333333333},
+ {3, 333333333, -2, 0, 5, 333333333},
+ {3, 333333333, -1, 0, 4, 333333333},
+ {3, 333333333, -1, 333333334, 3, 999999999},
+ {3, 333333333, -1, 666666667, 3, 666666666},
+ {3, 333333333, -1, 999999999, 3, 333333334},
+ {3, 333333333, 0, 0, 3, 333333333},
+ {3, 333333333, 0, 1, 3, 333333332},
+ {3, 333333333, 0, 333333333, 3, 0},
+ {3, 333333333, 0, 666666666, 2, 666666667},
+ {3, 333333333, 1, 0, 2, 333333333},
+ {3, 333333333, 2, 0, 1, 333333333},
+ {3, 333333333, 3, 0, 0, 333333333},
+ {3, 333333333, 3, 333333333, 0, 0},
+ {Long.MAX_VALUE, 0, Long.MAX_VALUE, 0, 0, 0},};
+ }
+
+ @ParameterizedTest
+ @UseDataProvider("data_minus")
+ public void test_minus(long seconds, int nanos, long minusSeconds, int minusNanos, long expectedSeconds, int expectedNanoOfSecond) {
+ MispInstant i = MispInstant.ofMispSeconds(seconds, nanos).minus(Duration.ofSeconds(minusSeconds, minusNanos));
+ assertEquals(expectedSeconds, i.getMispSeconds());
+ assertEquals(expectedNanoOfSecond, i.getNano());
+ }
+
+ @Test
+ public void test_minus_overflowTooSmall() {
+ MispInstant i = MispInstant.ofMispSeconds(Long.MIN_VALUE, 0);
+ assertThrows(ArithmeticException.class, () -> i.minus(Duration.ofSeconds(0, 1)));
+ }
+
+ @Test
+ public void test_minus_overflowTooBig() {
+ MispInstant i = MispInstant.ofMispSeconds(Long.MAX_VALUE, 999999999);
+ assertThrows(ArithmeticException.class, () -> i.minus(Duration.ofSeconds(-1, 999999999)));
+ }
+
+ @Test
+ public void test_durationUntil_fifteenSeconds() {
+ MispInstant misp1 = MispInstant.ofMispSeconds(10, 0);
+ MispInstant misp2 = MispInstant.ofMispSeconds(25, 0);
+ Duration test = misp1.durationUntil(misp2);
+ assertEquals(15, test.getSeconds());
+ assertEquals(0, test.getNano());
+ }
+
+ @Test
+ public void test_durationUntil_twoNanos() {
+ MispInstant misp1 = MispInstant.ofMispSeconds(4, 5);
+ MispInstant misp2 = MispInstant.ofMispSeconds(4, 7);
+ Duration test = misp1.durationUntil(misp2);
+ assertEquals(0, test.getSeconds());
+ assertEquals(2, test.getNano());
+ }
+
+ @Test
+ public void test_durationUntil_twoNanosNegative() {
+ MispInstant misp1 = MispInstant.ofMispSeconds(4, 9);
+ MispInstant misp2 = MispInstant.ofMispSeconds(4, 7);
+ Duration test = misp1.durationUntil(misp2);
+ assertEquals(-1, test.getSeconds());
+ assertEquals(999999998, test.getNano());
+ }
+
+ @Test
+ public void test_comparisons() {
+ doTest_comparisons_MispInstant(
+ MispInstant.ofMispSeconds(-2L, 0),
+ MispInstant.ofMispSeconds(-2L, 999999998),
+ MispInstant.ofMispSeconds(-2L, 999999999),
+ MispInstant.ofMispSeconds(-1L, 0),
+ MispInstant.ofMispSeconds(-1L, 1),
+ MispInstant.ofMispSeconds(-1L, 999999998),
+ MispInstant.ofMispSeconds(-1L, 999999999),
+ MispInstant.ofMispSeconds(0L, 0),
+ MispInstant.ofMispSeconds(0L, 1),
+ MispInstant.ofMispSeconds(0L, 2),
+ MispInstant.ofMispSeconds(0L, 999999999),
+ MispInstant.ofMispSeconds(1L, 0),
+ MispInstant.ofMispSeconds(2L, 0)
+ );
+ }
+
+ void doTest_comparisons_MispInstant(MispInstant... instants) {
+ for (int i = 0; i < instants.length; i++) {
+ MispInstant a = instants[i];
+ for (int j = 0; j < instants.length; j++) {
+ MispInstant b = instants[j];
+ if (i < j) {
+ assertTrue(a.compareTo(b) < 0);
+ assertFalse(a.equals(b));
+ assertTrue(a.isBefore(b));
+ assertFalse(a.isAfter(b));
+ } else if (i > j) {
+ assertTrue(a.compareTo(b) > 0);
+ assertFalse(a.equals(b));
+ assertFalse(a.isBefore(b));
+ assertTrue(a.isAfter(b));
+ } else {
+ assertEquals(0, a.compareTo(b));
+ assertTrue(a.equals(b));
+ assertFalse(a.isBefore(b));
+ assertFalse(a.isAfter(b));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void test_compareTo_ObjectNull() {
+ MispInstant a = MispInstant.ofMispSeconds(0L, 0);
+ assertThrows(NullPointerException.class, () -> a.compareTo(null));
+ }
+
+ @Test
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void test_compareToNonMispInstant() {
+ Comparable c = MispInstant.ofMispSeconds(0L, 2);
+ assertThrows(ClassCastException.class, () -> c.compareTo(new Object()));
+ }
+
+ @Test
+ public void test_equals() {
+ MispInstant test5a = MispInstant.ofMispSeconds(5L, 20);
+ MispInstant test5b = MispInstant.ofMispSeconds(5L, 20);
+ MispInstant test5n = MispInstant.ofMispSeconds(5L, 30);
+ MispInstant test6 = MispInstant.ofMispSeconds(6L, 20);
+
+ assertEquals(true, test5a.equals(test5a));
+ assertEquals(true, test5a.equals(test5b));
+ assertEquals(false, test5a.equals(test5n));
+ assertEquals(false, test5a.equals(test6));
+
+ assertEquals(true, test5b.equals(test5a));
+ assertEquals(true, test5b.equals(test5b));
+ assertEquals(false, test5b.equals(test5n));
+ assertEquals(false, test5b.equals(test6));
+
+ assertEquals(false, test5n.equals(test5a));
+ assertEquals(false, test5n.equals(test5b));
+ assertEquals(true, test5n.equals(test5n));
+ assertEquals(false, test5n.equals(test6));
+
+ assertEquals(false, test6.equals(test5a));
+ assertEquals(false, test6.equals(test5b));
+ assertEquals(false, test6.equals(test5n));
+ assertEquals(true, test6.equals(test6));
+ }
+
+ @Test
+ public void test_equals_null() {
+ MispInstant test5 = MispInstant.ofMispSeconds(5L, 20);
+ assertEquals(false, test5.equals(null));
+ }
+
+ @Test
+ public void test_equals_otherClass() {
+ MispInstant test5 = MispInstant.ofMispSeconds(5L, 20);
+ assertEquals(false, test5.equals((Object) ""));
+ }
+
+ @Test
+ public void test_hashCode() {
+ MispInstant test5a = MispInstant.ofMispSeconds(5L, 20);
+ MispInstant test5b = MispInstant.ofMispSeconds(5L, 20);
+ MispInstant test5n = MispInstant.ofMispSeconds(5L, 30);
+ MispInstant test6 = MispInstant.ofMispSeconds(6L, 20);
+
+ assertEquals(true, test5a.hashCode() == test5a.hashCode());
+ assertEquals(true, test5a.hashCode() == test5b.hashCode());
+ assertEquals(true, test5b.hashCode() == test5b.hashCode());
+
+ assertEquals(false, test5a.hashCode() == test5n.hashCode());
+ assertEquals(false, test5a.hashCode() == test6.hashCode());
+ }
+
+}
+ * MispInstant.ofMispSeconds(3, 1);
+ * MispInstant.ofMispSeconds(4, -999999999);
+ * MispInstant.ofMispSeconds(2, 1000000001);
+ *
+ *
+ * @param mispSeconds the number of seconds from the epoch of 1970-01-01T00:00:00(UTC)
+ * @param nanoAdjustment the nanosecond adjustment to the number of seconds,
+ * positive or negative
+ * @return the MISP instant, not null
+ * @throws ArithmeticException if numeric overflow occurs
+ */
+ public static MispInstant ofMispSeconds(long mispSeconds, long nanoAdjustment) {
+ long secs = Math.addExact(mispSeconds, Math.floorDiv(nanoAdjustment, NANOS_PER_SECOND));
+ int nos = (int) Math.floorMod(nanoAdjustment, NANOS_PER_SECOND); // safe cast
+ return new MispInstant(secs, nos);
+ }
+
+ /**
+ * Obtains an instance of {@code MispInstant} from a {@code TaiInstant}.
+ *
+ * @param tai the TAI instant to convert, not null
+ * @return the MISP instant, not null
+ */
+ public static MispInstant of(TaiInstant tai) {
+ long secs = tai.getTaiSeconds() - TAI_OFFSET_SEC;
+ long nos = tai.getNano() - TAI_OFFSET_NANOS;
+ return ofMispSeconds(secs, nos);
+ }
+
+ /**
+ * Obtains an instance of {@code MispInstant} from an {@code Instant}.
+ *