Date & Time API

Instant & Machine Time

15 min Lesson 3 of 13

Instant & Machine Time

Most of the java.time API is designed around human time — calendars, time zones, months, and day-of-week. But computers and distributed systems often need something simpler: a single number that unambiguously represents a point on the global timeline. That is exactly what Instant provides.

The Unix Epoch and What "Machine Time" Means

By international convention, time on computers is measured from the Unix epoch: midnight UTC on 1 January 1970 (1970-01-01T00:00:00Z). Every moment in history is expressed as a signed count of seconds (and fractions of seconds) relative to that anchor.

Machine time is timezone-free, locale-free, and calendar-free. There is no concept of "Tuesday" or "March" — just a number on a number line. This makes it perfect for:

  • Timestamping log entries and audit records
  • Measuring elapsed wall-clock time in benchmarks
  • Comparing events across distributed systems in different regions
  • Storing and transmitting time in databases and JSON APIs

The Instant Class

java.time.Instant represents a point on the timeline with nanosecond precision. Internally it holds two fields: long epochSecond (seconds since 1970-01-01T00:00:00Z) and int nanos (the nanosecond adjustment within that second, 0–999,999,999).

import java.time.Instant; Instant now = Instant.now(); // current moment, UTC Instant epoch = Instant.EPOCH; // 1970-01-01T00:00:00Z Instant future = Instant.MAX; // far future sentinel Instant past = Instant.MIN; // far past sentinel System.out.println(now); // e.g. 2026-06-09T07:42:00.123456789Z System.out.println(now.getEpochSecond()); // seconds since epoch System.out.println(now.getNano()); // nanosecond-of-second
The trailing Z is not optional. The toString() of every Instant ends in Z (Zulu, the NATO phonetic for UTC). This is the ISO-8601 representation of a UTC timestamp. When you parse or emit timestamps for APIs, that Z signals "this is already in UTC — no local offset".

Creating Instants

Beyond Instant.now() there are several factory methods:

// From epoch-second + nano adjustment Instant t1 = Instant.ofEpochSecond(1_000_000_000L); // 2001-09-09T01:46:40Z Instant t2 = Instant.ofEpochSecond(1_000_000_000L, 500_000_000); // + 500 ms // From epoch-millisecond (common when reading System.currentTimeMillis()) Instant t3 = Instant.ofEpochMilli(System.currentTimeMillis()); // From a string (ISO-8601 with Z or explicit offset) Instant t4 = Instant.parse("2024-01-15T09:30:00Z");

Querying an Instant

Instant t = Instant.parse("2024-01-15T09:30:00.250Z"); long sec = t.getEpochSecond(); // 1705311000 int nano = t.getNano(); // 250_000_000 (250 ms expressed as nanoseconds) long ms = t.toEpochMilli(); // 1705311000250
Prefer toEpochMilli() when integrating with legacy code (JDBC timestamps, Date, external libraries) because millisecond precision is the common denominator. Only reach for getNano() when you genuinely need sub-millisecond granularity.

Machine Time vs Human Time: Why the Distinction Matters

Consider storing a user's appointment. If you store it as an Instant you capture the UTC point-in-time, but you lose the intent: the user said "9 AM in New York" — and if a daylight-saving transition happens before the appointment, 9 AM UTC is no longer 9 AM local time. That is a human-time problem.

Conversely, if you store a distributed log event as a ZonedDateTime in a server's local zone, and that server is in a different region from another server, correlating events across the two logs becomes a painful timezone conversion exercise. That is a problem Instant solves trivially.

The rule of thumb:

  • Use Instant for: log timestamps, audit trails, inter-service event ordering, measuring durations, storing "when did this happen in absolute terms".
  • Use ZonedDateTime / LocalDateTime for: calendars, scheduling, anything the user sees and reasons about in their local time.

Arithmetic on Instants

Instant supports addition and subtraction via Duration (covered in the next lesson), plus convenience methods for common units:

import java.time.Instant; import java.time.Duration; import java.time.temporal.ChronoUnit; Instant start = Instant.now(); Instant oneHourLater = start.plus(Duration.ofHours(1)); Instant tenSecsEarlier = start.minus(10, ChronoUnit.SECONDS); Instant thirtyMsLater = start.plusMillis(30); System.out.println(start.isBefore(oneHourLater)); // true System.out.println(start.isAfter(tenSecsEarlier)); // true // Measure elapsed time precisely Instant before = Instant.now(); Thread.sleep(50); Instant after = Instant.now(); long elapsed = Duration.between(before, after).toMillis(); System.out.println("Elapsed: " + elapsed + " ms"); // ~50
Do not use Instant for elapsed-time benchmarks in performance-critical code. Instant.now() reads the system wall clock, which can jump backwards during NTP corrections. For high-precision benchmarking use System.nanoTime() — it is monotonic (never goes backward) but has no epoch meaning and cannot be converted to a date. Use Instant when you need a real wall-clock timestamp; use System.nanoTime() when you only need a duration.

Converting Between Instant and Human-Readable Types

You will frequently need to convert an Instant into a zoned or local representation for display, or convert back after user input:

import java.time.*; Instant instant = Instant.now(); ZoneId ny = ZoneId.of("America/New_York"); // Instant → ZonedDateTime ZonedDateTime zdt = instant.atZone(ny); System.out.println(zdt); // 2026-06-09T03:42:00.123-04:00[America/New_York] // ZonedDateTime → Instant Instant back = zdt.toInstant(); // OffsetDateTime (no DST rules, just a fixed offset) OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC);

Instant and Legacy APIs

Before Java 8 the world used java.util.Date and java.sql.Timestamp. You will encounter these in older codebases, JDBC drivers, and ORM mappings. The bridge is straightforward:

import java.time.Instant; import java.util.Date; import java.sql.Timestamp; // java.util.Date ↔ Instant Date legacyDate = Date.from(Instant.now()); Instant fromDate = legacyDate.toInstant(); // java.sql.Timestamp ↔ Instant Timestamp ts = Timestamp.from(Instant.now()); Instant fromTs = ts.toInstant();

Summary

Instant is the java.time representation of machine time: a timezone-free, calendar-free point on the UTC timeline measured from the Unix epoch. Use it wherever you need an unambiguous global timestamp — logging, event ordering, storage, and inter-service communication. For anything a human reads or schedules in a local timezone, convert to ZonedDateTime. The next lesson introduces Duration and Period, the two classes that represent spans of time.