Unix Timestamp Java Examples
If you write backend code long enough, timestamp bugs stop feeling rare and start feeling seasonal.
Everything is fine on your machine, the logs look innocent, CI is green, and then a user in another timezone reports that "tomorrow" became "today" somewhere in the pipeline. It usually comes down to one of two things: unit confusion (seconds vs milliseconds), or timezone assumptions that nobody wrote down.
So this is a practical post. No ceremony. Just Unix timestamp Java examples you can use right away.
A quick anchor before the code
Unix timestamp is elapsed time since 1970-01-01T00:00:00Z.
For modern values, a rough shortcut works:
- 10 digits -> usually seconds
- 13 digits -> usually milliseconds
In Java, Instant is your safest default because it represents an absolute UTC moment.
Get the current Unix timestamp in Java
import java.time.Instant;
Instant now = Instant.now();
long unixSec = now.getEpochSecond();
long unixMs = now.toEpochMilli();
System.out.println(now); // 2026-03-11T13:05:30.123Z
System.out.println(unixSec); // 1773234330
System.out.println(unixMs); // 1773234330123
This is the pattern I recommend everywhere: get Instant first, decide unit second.
Turn timestamp numbers into readable Java date-time
When input is in seconds:
import java.time.Instant;
long tsSec = 1773234330L;
Instant instant = Instant.ofEpochSecond(tsSec);
When input is in milliseconds:
import java.time.Instant;
long tsMs = 1773234330123L;
Instant instant = Instant.ofEpochMilli(tsMs);
And when someone asks for "human time", apply a zone only at display time.
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
Instant instant = Instant.ofEpochSecond(1773234330L);
ZonedDateTime utc = instant.atZone(ZoneId.of("UTC"));
ZonedDateTime ny = instant.atZone(ZoneId.of("America/New_York"));
System.out.println(utc);
System.out.println(ny);
Same instant. Different wall-clock rendering. That's not a bug.
Going the other direction: Java time -> Unix timestamp
From Instant:
import java.time.Instant;
Instant instant = Instant.parse("2026-03-11T13:05:30Z");
long sec = instant.getEpochSecond();
long ms = instant.toEpochMilli();
From ZonedDateTime:
import java.time.ZoneId;
import java.time.ZonedDateTime;
ZonedDateTime zdt = ZonedDateTime.of(
2026, 3, 11, 21, 5, 30, 0,
ZoneId.of("Asia/Shanghai")
);
long sec = zdt.toEpochSecond();
long ms = zdt.toInstant().toEpochMilli();
From LocalDateTime, you must choose a zone explicitly, otherwise there is no single absolute answer.
import java.time.LocalDateTime;
import java.time.ZoneId;
LocalDateTime ldt = LocalDateTime.of(2026, 3, 11, 21, 5, 30);
long sec = ldt.atZone(ZoneId.of("Asia/Shanghai")).toEpochSecond();
That one line with atZone(...) is not optional. It is the whole point.
Parse strings without guessing
ISO-8601 with Z is straightforward:
import java.time.Instant;
String iso = "2026-03-11T13:05:30Z";
long unixSec = Instant.parse(iso).getEpochSecond();
Offset-based strings are just as clean:
import java.time.OffsetDateTime;
String raw = "2026-03-11T21:05:30+08:00";
long unixSec = OffsetDateTime.parse(raw).toEpochSecond();
Different string formats can map to the same exact timestamp. They often do.
Format for humans (UTC and local)
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
Instant instant = Instant.ofEpochSecond(1773234330L);
DateTimeFormatter utcFmt = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss 'UTC'")
.withZone(ZoneId.of("UTC"));
DateTimeFormatter localFmt = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss z")
.withZone(ZoneId.of("Europe/Berlin"));
System.out.println(utcFmt.format(instant));
System.out.println(localFmt.format(instant));
Relying on JVM default timezone is sometimes okay for tiny scripts, but for product code it is a risky habit that eventually costs you a debugging day you did not plan for.
A practical sec/ms normalization helper
In real systems you will receive timestamp fields called timestamp, and that name tells you exactly nothing.
public static long normalizeToMillis(long ts) {
long abs = Math.abs(ts);
if (abs >= 1_000_000_000L && abs < 10_000_000_000L) {
return ts * 1000; // seconds
}
if (abs >= 1_000_000_000_000L && abs < 10_000_000_000_000L) {
return ts; // milliseconds
}
throw new IllegalArgumentException("Unsupported or ambiguous timestamp unit: " + ts);
}
Is this mathematically universal for every historical value? No. Is it practical for modern app traffic? Usually yes.
Expiry checks and tests that don't flake
Expiry logic, keep units aligned:
import java.time.Instant;
public static boolean isExpired(long expiresAtSec) {
return Instant.now().getEpochSecond() >= expiresAtSec;
}
And for tests, prefer injecting Clock so "now" is deterministic:
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
Clock fixedClock = Clock.fixed(
Instant.parse("2026-03-11T00:00:00Z"),
ZoneOffset.UTC
);
Instant now = Instant.now(fixedClock);
Small pattern, big stability gain.
Mistakes worth catching in code review
- Mixing seconds and milliseconds in one DB column.
- Using
LocalDateTime.now()as an event timestamp and assuming UTC later. - Formatting dates for users without explicit timezone.
- Storing local-time text and calling it "timestamp".
- Introducing new
Date/Calendarcode in ajava.timeproject.
If you force explicit units in naming (created_at_sec, updated_at_ms), half of these issues disappear before runtime.
Wrap-up
Unix timestamps in Java are not hard once the model is clear: keep storage and transport in absolute time (Instant / epoch), and localize only when rendering for people. Do that consistently, and time handling becomes boring, which is exactly what you want from time handling.