Skip to content

Clocks

Understanding Epoch Milliseconds

Epoch milliseconds is the time elapsed in milliseconds since midnight January 1st, 1970 in the UTC time zone and is held in a long data type. You might have heard about UNIX time rolling over in 2038 - this does apply to epoch seconds held in a 32-bit integer, but a long value holding epoch milliseconds will only rollover in the year 292278994.

Using epoch milliseconds within a service simplifies date handling logic by removing any need to worry about time zones and other complexities. It is also far more efficient using only 8 bytes of memory compared to Java's LocalDateTime which uses 7 bytes for the time plus 8 bytes for the date on top of a lot of static ints and longs.

The biggest challenge developers typically face with epoch milliseconds is that during debugging you will need to convert the epoch time value to something human readable.

When the service needs to interact with something external – such as a user interface – you can transform the value into a human readable date time in the desired time zone.

Samples of the clocks in use are in GitHub.

Epoch Clock

Agrona provides two implementations of the Epoch clock interface - SystemEpochClock and CachedEpochClock.

System Epoch Clock

The system epoch clock returns the current epoch time in milliseconds as a long. Internally, it returns Java's System.currentTimeMillis()

It has a default instance, so you do not need to allocate one when using it:

1
2
EpochClock clock = SystemEpochClock.INSTANCE;
long time = clock.time();

Cached Epoch Clock

The cached epoch clock has no clock internally, and you must either update the time using an external source such as a SystemEpochClock or advance the time by a given number of milliseconds. Internally it makes use of release ordered semantics.

1
2
3
4
CachedEpochClock clock = new CachedEpochClock();
clock.update(2L);
clock.advance(98L);
long time = clock.time(); //returns 100L

The cached epoch clock can be useful when moving time around multiple objects and want the "time" to remain constant until explictly updating or advancing it.

High resolution clocks

Warning

Clock precision is impacted by JDK version. Hotspot/OpenJDK 8 offers millisecond level precision, while version 11 and up offers microsecond or lower granularity.

It is not uncommon in finance for clock resolution to be higher precision than a millisecond - for example, firms classed as High Frequency traders are required to keep audit trails at the microsecond resolution under Mifid II. Nanosecond time resolution - as supported by clocks synchornised to external atomic or GPS based clocks - is also used. Some users will provide their own implementation of the EpochMicroClock and EpochNanoClock as backed by hardware specific to their environment.

Microsecond clocks

Agrona offers the SystemEpochMicroClock, which implements EpochMicroClock. Internally, this makes use of Instant, and does the necessary math to convert it into a long value representing the microseconds since epoch.

1
2
EpochMicroClock clock = new SystemEpochMicroClock();
long time = clock.microTime();

Nanosecond clocks

Agrona offers the SystemEpochNanoClock and OffsetEpochNanoClock which both implement EpochNanoClock.

SystemEpochNanoClock uses the same underlying technique as SystemEpochMicroClock, and returns the nano epoch as derived from Instant.

1
2
EpochNanoClock clock = new SystemEpochNanoClock();
long time = clock.nanoTime();

OffsetEpochNanoClock performs sampling of System.nanoTime() at a defined interval to produce an accurate nano time clock in pure Java. The sampling interval and measurement parameters can be optionally overridden.

1
2
EpochNanoClock clock = new OffsetEpochNanoClock();
long time = clock.nanoTime();