InstantsTest.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.time;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Instant;
import org.apache.commons.lang3.AbstractLangTest;
import org.junit.jupiter.api.Test;
/**
* Tests {@link Instants}.
*/
class InstantsTest extends AbstractLangTest {
/**
* An Instant far in the future whose epoch-millis overflow {@code long}: seconds chosen so that {@code seconds * 1_000} exceeds {@link Long#MAX_VALUE}.
*/
private static final Instant INSTANT_FAR_FUTURE = Instant.ofEpochSecond(Long.MAX_VALUE / 1_000 + 1_000);
/**
* An Instant far in the past whose epoch-millis underflow {@code long}: seconds chosen so that {@code seconds * 1_000} is less than {@link Long#MIN_VALUE}.
*/
private static final Instant INSTANT_FAR_PAST = Instant.ofEpochSecond(Long.MIN_VALUE / 1_000 - 1_000);
@Test
void testToEpochMillisEpoch() {
assertEquals(0L, Instants.toEpochMillis(Instant.EPOCH));
}
@Test
void testToEpochMillisMaxValue() {
assertEquals(Long.MAX_VALUE, Instants.toEpochMillis(Instant.ofEpochMilli(Long.MAX_VALUE)));
}
@Test
void testToEpochMillisMinValue() {
assertEquals(Long.MIN_VALUE, Instants.toEpochMillis(Instant.ofEpochMilli(Long.MIN_VALUE)));
}
@Test
void testToEpochMillisNegativeMillis() {
assertEquals(-1_000_000L, Instants.toEpochMillis(Instant.ofEpochMilli(-1_000_000L)));
}
@Test
void testToEpochMillisNormalInstant() {
assertEquals(1_000_000L, Instants.toEpochMillis(Instant.ofEpochMilli(1_000_000L)));
}
@Test
void testToEpochMillisNow() {
final Instant now = Instant.now();
assertEquals(now.toEpochMilli(), Instants.toEpochMillis(now));
}
@Test
void testToEpochMillisOverflowReturnsMaxValue() {
assertEquals(Long.MAX_VALUE, Instants.toEpochMillis(INSTANT_FAR_FUTURE));
}
@Test
void testToEpochMillisUnderflowReturnsMinValue() {
assertEquals(Long.MIN_VALUE, Instants.toEpochMillis(INSTANT_FAR_PAST));
}
@Test
void testToInstantEpochReturnsSameInstance() {
assertSame(Instant.EPOCH, Instants.toInstant(Instant.EPOCH));
}
@Test
void testToInstantMaxReturnsSameInstance() {
assertSame(Instant.MAX, Instants.toInstant(Instant.MAX));
}
@Test
void testToInstantMinReturnsSameInstance() {
assertSame(Instant.MIN, Instants.toInstant(Instant.MIN));
}
@Test
void testToInstantNonNullIsNotNull() {
assertNotNull(Instants.toInstant(Instant.now()));
}
@Test
void testToInstantNonNullReturnsSameInstance() {
final Instant now = Instant.now();
assertSame(now, Instants.toInstant(now));
}
@Test
void testToInstantNullReturnsEpoch() {
assertEquals(Instant.EPOCH, Instants.toInstant(null));
}
@Test
void testToInstantWithDefaultEpochReturnsSameInstance() {
assertSame(Instant.EPOCH, Instants.toInstant(Instant.EPOCH, Instant.MAX));
}
@Test
void testToInstantWithDefaultMaxReturnsSameInstance() {
assertSame(Instant.MAX, Instants.toInstant(Instant.MAX, Instant.MIN));
}
@Test
void testToInstantWithDefaultMinReturnsSameInstance() {
assertSame(Instant.MIN, Instants.toInstant(Instant.MIN, Instant.MAX));
}
@Test
void testToInstantWithDefaultNonNullIgnoresDefault() {
final Instant value = Instant.ofEpochMilli(1_000L);
final Instant defaultInstant = Instant.ofEpochMilli(9_999L);
assertSame(value, Instants.toInstant(value, defaultInstant));
}
@Test
void testToInstantWithDefaultNonNullReturnsSameInstance() {
final Instant now = Instant.now();
assertSame(now, Instants.toInstant(now, Instant.EPOCH));
}
@Test
void testToInstantWithDefaultNullAndEpochDefaultReturnsEpoch() {
assertSame(Instant.EPOCH, Instants.toInstant(null, Instant.EPOCH));
}
@Test
void testToInstantWithDefaultNullReturnsDefault() {
final Instant defaultInstant = Instant.ofEpochMilli(12345L);
assertSame(defaultInstant, Instants.toInstant(null, defaultInstant));
}
@Test
void testToInstantWithDefaultNullReturnsNullDefault() {
assertSame(null, Instants.toInstant(null, null));
}
/**
* 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);
}
}