Code that depends directly on the current date or time can be difficult to test because the result changes every time the test runs.
For example, code like this is hard to test reliably:
import java.time.LocalDate;
public class SubscriptionService {
public boolean isExpired(LocalDate expiryDate) {
return expiryDate.isBefore(LocalDate.now());
}
}
The problem is LocalDate.now().
Today’s test result may be different from tomorrow’s.
The best solution is to avoid calling the system clock directly inside your business logic. Instead, inject a time source, usually java.time.Clock.
Using Clock in Production Code
Java’s java.time.Clock lets you control what “now” means.
import java.time.Clock;
import java.time.LocalDate;
public class SubscriptionService {
private final Clock clock;
public SubscriptionService(Clock clock) {
this.clock = clock;
}
public boolean isExpired(LocalDate expiryDate) {
LocalDate today = LocalDate.now(clock);
return expiryDate.isBefore(today);
}
}
Now the class no longer depends directly on the real system date.
Instead, it depends on a Clock.
Using the Real Clock in Application Code
In normal application code, pass the system clock:
import java.time.Clock;
public class Application {
public static void main(String[] args) {
SubscriptionService service =
new SubscriptionService(Clock.systemDefaultZone());
// Use the service normally
}
}
You can also use UTC if your application should not depend on the server’s local timezone:
Clock.systemUTC()
Testing with a Fixed Clock
In tests, use Clock.fixed(...) to make time predictable.
import org.junit.jupiter.api.Test;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SubscriptionServiceTest {
@Test
void isExpiredReturnsTrueWhenExpiryDateIsBeforeToday() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T10:00:00Z"),
ZoneId.of("UTC")
);
SubscriptionService service = new SubscriptionService(fixedClock);
boolean expired = service.isExpired(LocalDate.of(2026, 7, 12));
assertTrue(expired);
}
@Test
void isExpiredReturnsFalseWhenExpiryDateIsToday() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T10:00:00Z"),
ZoneId.of("UTC")
);
SubscriptionService service = new SubscriptionService(fixedClock);
boolean expired = service.isExpired(LocalDate.of(2026, 7, 13));
assertFalse(expired);
}
@Test
void isExpiredReturnsFalseWhenExpiryDateIsAfterToday() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T10:00:00Z"),
ZoneId.of("UTC")
);
SubscriptionService service = new SubscriptionService(fixedClock);
boolean expired = service.isExpired(LocalDate.of(2026, 7, 14));
assertFalse(expired);
}
}
Now the tests are stable because “today” is always 2026-07-13.
Testing Date and Time Logic
The same approach works with LocalDateTime, ZonedDateTime, Instant, and other classes from the java.time package.
Example:
import java.time.Clock;
import java.time.LocalDateTime;
public class GreetingService {
private final Clock clock;
public GreetingService(Clock clock) {
this.clock = clock;
}
public String greeting() {
int hour = LocalDateTime.now(clock).getHour();
if (hour < 12) {
return "Good morning";
}
if (hour < 18) {
return "Good afternoon";
}
return "Good evening";
}
}
Test:
import org.junit.jupiter.api.Test;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import static org.junit.jupiter.api.Assertions.assertEquals;
class GreetingServiceTest {
@Test
void greetingReturnsGoodMorningBeforeNoon() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T08:00:00Z"),
ZoneId.of("UTC")
);
GreetingService service = new GreetingService(fixedClock);
assertEquals("Good morning", service.greeting());
}
@Test
void greetingReturnsGoodAfternoonAfterNoon() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T14:00:00Z"),
ZoneId.of("UTC")
);
GreetingService service = new GreetingService(fixedClock);
assertEquals("Good afternoon", service.greeting());
}
@Test
void greetingReturnsGoodEveningInTheEvening() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T20:00:00Z"),
ZoneId.of("UTC")
);
GreetingService service = new GreetingService(fixedClock);
assertEquals("Good evening", service.greeting());
}
}
Using Clock with Spring
In a Spring application, you can register a Clock as a bean.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Clock;
@Configuration
public class TimeConfig {
@Bean
public Clock clock() {
return Clock.systemUTC();
}
}
Then inject it into your service:
import org.springframework.stereotype.Service;
import java.time.Clock;
import java.time.LocalDate;
@Service
public class SubscriptionService {
private final Clock clock;
public SubscriptionService(Clock clock) {
this.clock = clock;
}
public boolean isExpired(LocalDate expiryDate) {
return expiryDate.isBefore(LocalDate.now(clock));
}
}
In tests, replace the Clock bean with a fixed one:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SubscriptionServiceSpringTest {
@TestConfiguration
static class FixedClockConfig {
@Bean
Clock clock() {
return Clock.fixed(
Instant.parse("2026-07-13T10:00:00Z"),
ZoneId.of("UTC")
);
}
}
@Test
void isExpiredUsesFixedClock() {
SubscriptionService service =
new SubscriptionService(FixedClockConfig.class.cast(null));
}
}
A simpler unit test usually does not need Spring at all:
import org.junit.jupiter.api.Test;
import java.time.Clock;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SubscriptionServiceTest {
@Test
void isExpiredUsesFixedClock() {
Clock fixedClock = Clock.fixed(
Instant.parse("2026-07-13T10:00:00Z"),
ZoneId.of("UTC")
);
SubscriptionService service = new SubscriptionService(fixedClock);
assertTrue(service.isExpired(LocalDate.of(2026, 7, 12)));
}
}
Avoid These Approaches When Possible
Avoid scattering calls like these throughout your code:
LocalDate.now();
LocalDateTime.now();
Instant.now();
System.currentTimeMillis();
new Date();
They make tests harder because the current time cannot easily be controlled.
Also avoid adding arbitrary sleeps to tests:
Thread.sleep(1000);
Tests that depend on sleeping are usually slow and unreliable.
Good Rule of Thumb
Use this rule:
If your code needs the current time, inject a
Clock.
This makes your code:
- easier to test
- more predictable
- less dependent on the machine’s timezone
- easier to reason about
- safer around midnight and date-boundary bugs
For most Java applications, java.time.Clock is the cleanest way to test code that depends on time.
