This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new bda068076 Add Instants.toMillisSince(Instant) (#1683)
bda068076 is described below
commit bda0680763ad90264b9cca8233bb3369c03f4e13
Author: Gary Gregory <[email protected]>
AuthorDate: Wed Jun 3 07:26:28 2026 -0400
Add Instants.toMillisSince(Instant) (#1683)
---
.../org/apache/commons/lang3/time/Instants.java | 26 +++++++-
.../apache/commons/lang3/time/InstantsTest.java | 70 ++++++++++++++++++++--
2 files changed, 90 insertions(+), 6 deletions(-)
diff --git a/src/main/java/org/apache/commons/lang3/time/Instants.java
b/src/main/java/org/apache/commons/lang3/time/Instants.java
index 3c092f96e..2b28e89cd 100644
--- a/src/main/java/org/apache/commons/lang3/time/Instants.java
+++ b/src/main/java/org/apache/commons/lang3/time/Instants.java
@@ -26,6 +26,10 @@
*/
public class Instants {
+ private static long toBound(final Instant instant, final long negBound,
final long posBound) {
+ return instant.getEpochSecond() < 0 ? negBound : posBound;
+ }
+
/**
* Converts an Instant to milliseconds bound to a {@code long} without
throwing {@link ArithmeticException}.
* <ul>
@@ -34,7 +38,7 @@ public class Instants {
* </ul>
*
* @param instant The instant to convert, not null.
- * @return long milliseconds.
+ * @return long The given Instant in milliseconds.
* @see Instant#toEpochMilli()
* @see Long#MIN_VALUE
* @see Long#MAX_VALUE
@@ -43,7 +47,25 @@ public static long toEpochMillis(final Instant instant) {
try {
return instant.toEpochMilli();
} catch (final ArithmeticException e) {
- return instant.getEpochSecond() < 0 ? Long.MIN_VALUE :
Long.MAX_VALUE;
+ return toBound(instant, Long.MIN_VALUE, Long.MAX_VALUE);
+ }
+ }
+
+ /**
+ * Converts an Instant to milliseconds sicne that Instant bound to a
{@code long} without throwing {@link ArithmeticException}.
+ * <ul>
+ * <li>If the duration milliseconds are greater than {@link
Long#MAX_VALUE}, then return {@link Long#MAX_VALUE}.</li>
+ * <li>If the duration milliseconds are lesser than {@link
Long#MIN_VALUE}, then return {@link Long#MIN_VALUE}.</li>
+ * </ul>
+ *
+ * @param instant The instant to convert, not null.
+ * @return long The duration in milliseconds since the given Instant.
+ */
+ public static long toMillisSince(final Instant instant) {
+ try {
+ return DurationUtils.since(instant).toMillis();
+ } catch (final ArithmeticException e) {
+ return toBound(instant, Long.MIN_VALUE, Long.MAX_VALUE);
}
}
diff --git a/src/test/java/org/apache/commons/lang3/time/InstantsTest.java
b/src/test/java/org/apache/commons/lang3/time/InstantsTest.java
index a64644b06..365e12282 100644
--- a/src/test/java/org/apache/commons/lang3/time/InstantsTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/InstantsTest.java
@@ -18,6 +18,7 @@
package org.apache.commons.lang3.time;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Instant;
@@ -55,14 +56,12 @@ void testToEpochMillisMinValue() {
@Test
void testToEpochMillisNegativeMillis() {
- final Instant instant = Instant.ofEpochMilli(-1_000_000L);
- assertEquals(-1_000_000L, Instants.toEpochMillis(instant));
+ assertEquals(-1_000_000L,
Instants.toEpochMillis(Instant.ofEpochMilli(-1_000_000L)));
}
@Test
void testToEpochMillisNormalInstant() {
- final Instant instant = Instant.ofEpochMilli(1_000_000L);
- assertEquals(1_000_000L, Instants.toEpochMillis(instant));
+ assertEquals(1_000_000L,
Instants.toEpochMillis(Instant.ofEpochMilli(1_000_000L)));
}
@Test
@@ -80,4 +79,67 @@ void testToEpochMillisOverflowReturnsMaxValue() {
void testToEpochMillisUnderflowReturnsMinValue() {
assertEquals(Long.MIN_VALUE, Instants.toEpochMillis(INSTANT_FAR_PAST));
}
+
+ /**
+ * Epoch instant (time zero): milliseconds since epoch should be positive
and equal to
+ * roughly the current time in millis (with a generous lower bound).
+ */
+ @Test
+ void testToMillisSinceEpoch() {
+ // As of 2026, ~1.7 trillion ms have elapsed since epoch.
+ assertTrue(Instants.toMillisSince(Instant.EPOCH) > 0);
+ }
+
+ /**
+ * A future instant one second from now; milliseconds since it should be
negative (roughly -1_000).
+ */
+ @Test
+ void testToMillisSinceFutureInstantIsNegative() {
+ final Instant oneSecondFuture = Instant.now().plusMillis(1_000);
+ final long millis = Instants.toMillisSince(oneSecondFuture);
+ // Duration from a future instant to now is negative.
+ assertTrue(millis <= -900, "Expected millis <= -900 but was " +
millis);
+ }
+
+ /**
+ * {@link Instant#MAX} (positive epoch second): the huge negative duration
from Instant.MAX to now
+ * overflows {@code long} millis; the bound is {@link Long#MAX_VALUE}
because the instant's epoch
+ * second is positive.
+ */
+ @Test
+ void testToMillisSinceInstantMaxOverflowReturnsMaxValue() {
+ assertEquals(Long.MAX_VALUE, Instants.toMillisSince(Instant.MAX));
+ }
+
+ /**
+ * {@link Instant#MIN} (negative epoch second): the huge positive duration
from Instant.MIN to now
+ * overflows {@code long} millis; the bound is {@link Long#MIN_VALUE}
because the instant's epoch
+ * second is negative.
+ */
+ @Test
+ void testToMillisSinceInstantMinOverflowReturnsMinValue() {
+ assertEquals(Long.MIN_VALUE, Instants.toMillisSince(Instant.MIN));
+ }
+
+ /**
+ * Instant.now(); milliseconds since should be very close to zero (within
a generous tolerance).
+ */
+ @Test
+ void testToMillisSinceNowIsNearZero() {
+ final Instant now = Instant.now();
+ final long millis = Instants.toMillisSince(now);
+ // Allow a generous 5_000 ms window for slow test environments.
+ assertTrue(millis >= 0 && millis < 5_000, "Expected millis in [0,
5000) but was " + millis);
+ }
+
+ /**
+ * A past instant one second ago; milliseconds since it should be
approximately 1_000.
+ */
+ @Test
+ void testToMillisSincePastInstantIsPositive() {
+ final Instant oneSecondAgo = Instant.now().minusMillis(1_000);
+ final long millis = Instants.toMillisSince(oneSecondAgo);
+ // Should be at least 1_000 ms (wall time may have advanced slightly
more).
+ assertTrue(millis >= 1_000, "Expected millis >= 1000 but was " +
millis);
+ }
}