Instant.toEpochMilli converts a timestamp to whole milliseconds, dropping its finer nanosecond fraction.
Java Instant toEpochMilli: detect lost submillisecond precision
Declare storage precision
An Instant can represent nanosecond fields, but a database column or wire field holding epoch milliseconds cannot. Converting and rebuilding the value loses the last six digits of the nanosecond field. That is a schema choice, not a harmless serialization detail.
The fixture shows an event with 123456789 nanoseconds becoming 123000000 after a millisecond round trip. Offset representations are a separate concern: equal instants can be written with different offsets even without precision loss.
Bound comparisons to stored precision
If the store truncates to milliseconds, compare against an Instant truncated to that unit or use an agreed tolerance. Never silently assume that a round trip preserves equality. For ordering events with the same stored millisecond, add a separate sequence or stable tie-break key.
Working program
import java.time.Instant;
public class LedgerTimestampPrecision {
public static void main(String[] args) {
Instant captured = Instant.parse("2024-08-12T04:00:00.123456789Z");
Instant stored = Instant.ofEpochMilli(captured.toEpochMilli());
System.out.println(captured.getNano());
System.out.println(stored.getNano());
System.out.println(captured.equals(stored));
}
}Output
123456789
123000000
falseCost and ownership
The conversion is constant application work and storage. Precision loss is irreversible after persistence; if a service needs finer ordering, choose a matching schema and serialization format before writing data.
Common Mistakes
- Do not expect toEpochMilli to preserve nanoseconds.
- Do not use timestamp equality across storage systems with different precision without a policy.
- Do not infer event order solely from a coarse timestamp when collisions are possible.
Read next
Java OffsetDateTime equality versus isEqual for event timestamps, Java date and time: local schedules versus instants, Java Clock: test expiry without sleeping, Java checked integer arithmetic: reject overflow before updating state.
