Date / Time
java.time (JSR-310) is the modern date/time API: immutable, thread-safe, time-zone aware, and easy to read. Use Instant for machine timestamps, LocalDate for calendar dates, ZonedDateTime for "wall clock somewhere", Duration for elapsed time, Period for calendar offsets. The legacy Date/Calendar APIs are a trap — avoid.
Instant, ZonedDateTime, formatting, parsing, durations
EXAMPLE
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
public class TimeDemo {
public static void main(String[] args) {
// 1) Instant — machine timestamp (UTC, no zone)
Instant now = Instant.now();
System.out.println(now); // 2026-06-18T03:21:00.123Z
System.out.println(now.toEpochMilli());
// 2) LocalDate / LocalTime / LocalDateTime — no zone
LocalDate today = LocalDate.now();
LocalDate eom = today.with(TemporalAdjusters.lastDayOfMonth());
LocalDate next = today.plusMonths(1);
LocalTime nine = LocalTime.of(9, 30);
LocalDateTime stamp= LocalDateTime.of(today, nine);
System.out.println("eom=" + eom + " next=" + next + " stamp=" + stamp);
// 3) ZonedDateTime — wall clock at a specific zone
ZoneId syd = ZoneId.of("Australia/Sydney");
ZoneId hk = ZoneId.of("Asia/Hong_Kong");
ZonedDateTime sydMeeting = ZonedDateTime.of(stamp, syd);
ZonedDateTime hkMeeting = sydMeeting.withZoneSameInstant(hk);
System.out.println("Sydney meeting @ HK time: " + hkMeeting);
// 4) Parse + format — ISO-8601 and custom
ZonedDateTime parsed = ZonedDateTime.parse("2026-06-18T09:30:00+10:00[Australia/Sydney]");
String formatted = parsed.format(
DateTimeFormatter.ofPattern("EEE d MMM yyyy h:mm a z"));
System.out.println(formatted);
// 5) Duration vs Period
Duration runtime = Duration.between(now.minusSeconds(125), now);
System.out.println("runtime: " + runtime.toMinutesPart() + "m " + runtime.toSecondsPart() + "s");
Period age = Period.between(LocalDate.of(2020, 1, 1), today);
System.out.println("age: " + age.getYears() + " years " + age.getMonths() + " months");
// 6) Arithmetic without surprises
ZonedDateTime in7 = sydMeeting.plus(7, ChronoUnit.DAYS);
boolean weekend = in7.getDayOfWeek() == DayOfWeek.SATURDAY
|| in7.getDayOfWeek() == DayOfWeek.SUNDAY;
// 7) Daylight saving transitions — same wall clock, different offsets
ZonedDateTime preDst = ZonedDateTime.parse("2026-04-05T01:30:00+11:00[Australia/Sydney]");
ZonedDateTime postDst = preDst.plus(1, ChronoUnit.HOURS);
System.out.println("before DST: " + preDst);
System.out.println("after DST: " + postDst);
// 8) Clock — inject for tests so "now" is deterministic
Clock fixed = Clock.fixed(Instant.parse("2026-06-18T00:00:00Z"), ZoneOffset.UTC);
LocalDate dt = LocalDate.now(fixed); // always 2026-06-18
// 9) Convert to/from legacy Date when an old API forces you to
java.util.Date legacy = java.util.Date.from(now);
Instant back = legacy.toInstant();
// 10) Decision matrix
// - Machine timestamps (logs, audit, sort by event time) -> Instant
// - Calendar date with no time -> LocalDate
// - Wall clock at a known zone (UI display, scheduling) -> ZonedDateTime
// - Elapsed measurement -> Duration
// - Calendar offset (1 month, 3 years) -> Period
}
}
Why it matters
Inject a Clock into anything that uses "now". Production passes Clock.systemUTC(); tests pass Clock.fixed(...) so time-dependent tests are deterministic. Without it, tests grow flaky around midnight, daylight saving boundaries, and the leap second your CI happens to run on.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import java.time.*; LocalDate today = LocalDate.now(); LocalDateTime stamp = LocalDateTime.now(); Duration d = Duration.between(stamp.minusHours(2), stamp);Try it Yourself »
Discussion
Loading…