A Clock supplies instants and a time zone so application code can read time through an explicit dependency instead of a hidden system call.
Java Clock: test expiry without sleeping
The complete program targets Java 8. Compile it as one source file; its output is checked against the lesson.
Define the expiry boundary
A payment reservation expires at a stored Instant. The predicate uses now greater than or equal to expiry. Equality matters: switching to a strict greater-than comparison grants an extra instant at the boundary. That is a business decision, not a formatting preference.
Use an instant for an absolute deadline. A LocalDateTime has no offset and cannot identify a unique point on the timeline during an overlap. Inject the clock into the service constructor; tests can then use a fixed instant without waiting or changing machine time.
Separate civil time from elapsed time
Clock.fixed returns the supplied instant whenever read. Clock.offset moves that view by a duration, which lets this fixture check both sides of an expiry boundary using the same reservation. Neither object simulates an operating system clock that ticks by itself.
For measuring elapsed latency inside one JVM, a monotonic time source such as System.nanoTime addresses a different problem. Wall time can change after synchronization. Do not use a fixed clock to conclude that concurrent timing or scheduler behavior has been tested.
Working program
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
public class ReservationExpiry {
private final Clock clock;
ReservationExpiry(Clock clock) { this.clock = clock; }
boolean expired(Instant deadline) { return !clock.instant().isBefore(deadline); }
public static void main(String[] args) {
Instant deadline = Instant.parse("2026-01-12T12:00:00Z");
Clock boundary = Clock.fixed(deadline, ZoneOffset.UTC);
System.out.println(new ReservationExpiry(Clock.offset(boundary, Duration.ofSeconds(-1))).expired(deadline));
System.out.println(new ReservationExpiry(boundary).expired(deadline));
}
}Output
false
trueCosts and boundaries
Each comparison is bounded work with constant application state. Storage precision can truncate timestamps, so test after converting to the database precision used in production. This fixture has no database and cannot establish that conversion contract.
Common Mistakes
- Do not scatter Instant.now() through a service that needs reproducible tests.
- Choose whether equality means expired.
- A wall clock is not a monotonic latency counter.
Read next
Java date and time: local schedules versus instants, Java test boundaries: injected collaborators and behavior-focused checks, Constructor injection.
Apply this contract in Spring
Spring constructor injection: required dependencies stay visible. These lessons keep framework assembly separate from the Java contract.
