Use a fake system clock

When testing an application, it's often useful to define a fake system clock in order to exercise code that uses dates and times.

While it's always possible to change the system clock directly, many view that style as undesirable:

Instead of changing the system clock, it's possible to define a fake system clock just for your application. In production, the fake system clock returns normal time. During testing, the fake system clock is changed to return any time you need for effective test coverage.

For this to work, you need to define various different clock implementations, and be able to swap them easily. Many would choose to use a dependency-injection tool for this, or perhaps a plug-in mechanism.

For this to work, you must never reference the default system clock and time zone directly, by avoiding methods such as:

This requires some discipline, because so many code examples use the default system clock (and time zone), and because calling the above methods has become habitual.

Possible behaviors for a fake clock include:

According to your needs, you may have to use the fake system clock in some or all of these places:

Examples for Java 8

The Clock class of the java.time package lets you create a fake system clock. Its fixed method lets you quickly create a common type of fake Clock, which simply returns a fixed value in a given time zone. In general, you'll need to extend the abstract Clock class, and implement its abstract methods.

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Objects;

/** 
 Increment by 1 second each time you look at the clock.
 Starts with the default system clock's instant and time-zone.
 
 Example output:
  2018-05-26T14:00:12.778Z
  2018-05-26T14:00:13.778Z
  2018-05-26T14:00:14.778Z
  2018-05-26T14:00:15.778Z
  2018-05-26T14:00:16.778Z
   
 @since Java 8.
*/
public final class ClockTicker extends Clock {
  
  /** Simple demo of the behaviour of this class. */
  public static void main(String... args) {
    ClockTicker ticker = new ClockTicker();
    log(ticker.instant());
    log(ticker.instant());
    log(ticker.instant());
    log(ticker.instant());
    log(ticker.instant());
  }
  private static void log(Object msg){
    System.out.println(Objects.toString(msg));
  }
  
  @Override public ZoneId getZone() {
    return DEFAULT_TZONE;
  }
  
  @Override public Clock withZone(ZoneId zone) {
    return Clock.fixed(WHEN_STARTED, zone);
  }
  
  @Override public Instant instant() {
    return nextInstant();
  }
  
  //PRIVATE 
  private final Instant WHEN_STARTED = Instant.now();
  private final ZoneId DEFAULT_TZONE = ZoneId.systemDefault();
  private long count = 0;
  
  private Instant nextInstant() {
    ++count;
    return WHEN_STARTED.plusSeconds(count);
  }
} 

import java.time.Clock;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Objects;

/** 
 Set the starting date-time and time-zone, but then 
 let the time vary normally.
 
 Example output:
  2018-12-25T05:00:00Z
  Sleep for 5 seconds...
  2018-12-25T05:00:05.005Z
  Done.

 @since Java 8.
*/
public final class ClockTimeTravel extends Clock {
  
  /** Simple demo of the behaviour of this class. */
  public static void main(String[] args) throws InterruptedException {
    ClockTimeTravel timeTravel = new ClockTimeTravel(
      LocalDateTime.parse("2018-12-25T00:00:00"), ZoneOffset.of("-05:00")
    );
    log(timeTravel.instant());
    log("Sleep for 5 seconds...");
    Thread.currentThread().sleep(5000);
    log(timeTravel.instant());
    log("Done.");
  }
  
  private static void log(Object msg){
    System.out.println(Objects.toString(msg));
  }
  
  public ClockTimeTravel(LocalDateTime t0, ZoneOffset zoneOffset){
    this.zoneOffset = zoneOffset;
    this.t0LocalDateTime = t0;
    this.t0Instant = t0.toInstant(zoneOffset);
    this.whenObjectCreatedInstant = Instant.now();
  }
  
  @Override public ZoneId getZone() {
    return zoneOffset;
  }
  
  /** The caller needs to actually pass a ZoneOffset object here. */
  @Override public Clock withZone(ZoneId zone) {
    return new ClockTimeTravel(t0LocalDateTime, (ZoneOffset)zone);
  }
  
  @Override public Instant instant() {
    return nextInstant();
  }
  
  //PRIVATE
  
  /** t0 is the moment this clock object was created in Java-land. */
  private final Instant t0Instant;
  private final LocalDateTime t0LocalDateTime;
  
  private final ZoneOffset zoneOffset;
  private final Instant whenObjectCreatedInstant;
  
  /** 
   Figure out how much time has elapsed between the moment this 
   object was created, and the moment when this method is being called.
   Then, apply that diff to t0. 
  */
  private Instant nextInstant() {
    Instant now = Instant.now();
    long diff = now.toEpochMilli() - whenObjectCreatedInstant.toEpochMilli();
    return t0Instant.plusMillis(diff);
  }
} 

Example for Java < 8

The TimeSource interface allows you to define various implementations of a fake system clock:

public interface TimeSource {

  /** Return the system time. */  
  long currentTimeMillis();

} 
This implementation mimics a system clock running one day in advance:
public final class TimeSrc implements TimeSource {

  /** One day in advance of the actual time.*/
  @Override public long currentTimeMillis() {
    return System.currentTimeMillis() + ONE_DAY;
  }
  
  private static final long ONE_DAY = 24*60*60*1000;

} 
Using various TimeSource implementations, you can mimic any desired behavior for a system clock.

It's simple to configure the JDK logger to use your fake system clock. A simple custom Formatter can use your TimeSource to alter the time of the LogRecord:

import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;

public final class SimpleFormatterTimeSource extends SimpleFormatter {

  @Override public String format(LogRecord aLogRecord) {
    aLogRecord.setMillis(fTimeSource.currentTimeMillis());
    return super.format(aLogRecord);
  }

  private TimeSource fTimeSource = BuildImpl.forTimeSource();
}
 

See Also :
Use a testing framework (JUnit)
Plugin Factory