Date & Time API
java.time: Local/Zoned/Instant, Duration vs Period, formatting and parsing
Why Not Date / Calendar
Pre-Java-8 Date / Calendar are historical baggage: mutable (not thread-safe), months start at 0, messy design. New projects always use java.time (JSR-310, inspired by Joda-Time): immutable, thread-safe, clean API. All examples below use java.time.
- Use cases: user signup time, order countdown, scheduled tasks, cross-timezone meetings, billing cycles
- Core classes at a glance: LocalDate (date) / LocalTime (time) / LocalDateTime (zoneless date+time) / ZonedDateTime (with zone) / Instant (epoch timestamp) / Duration (length in seconds/nanos) / Period (length in years/months/days)
LocalDate / LocalTime / LocalDateTime
Three "zoneless" classes: LocalDate has only a date, LocalTime only a time, LocalDateTime both. Good for "business dates" (birthdays, business hours) independent of any specific timezone.
// LocalDemo.java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
public class LocalDemo {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, Month.MAY, 12);
System.out.println(today + " " + birthday);
LocalTime noon = LocalTime.of(12, 0);
LocalTime now = LocalTime.now();
System.out.println(noon + " " + now);
LocalDateTime meeting = LocalDateTime.of(2026, 5, 12, 14, 30);
System.out.println(meeting);
// add/subtract: returns a new object
System.out.println(today.plusDays(7));
System.out.println(today.minusMonths(1));
System.out.println(today.withDayOfMonth(1)); // the 1st of this month
// field access
System.out.println("year=" + today.getYear() + " month=" + today.getMonthValue() + " day=" + today.getDayOfMonth());
System.out.println("weekday=" + today.getDayOfWeek());
}
}ZonedDateTime and Time Zones
Use case: show "what time it is in Shanghai now", "the time a London colleague sees", cross-timezone meetings. ZonedDateTime = LocalDateTime + ZoneId. The same instant shows different clock numbers under different ZoneIds.
// ZonedDemo.java
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class ZonedDemo {
public static void main(String[] args) {
ZoneId sh = ZoneId.of("Asia/Shanghai");
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime nowSh = ZonedDateTime.now(sh);
ZonedDateTime nowNy = nowSh.withZoneSameInstant(ny); // same instant, switch time zone
System.out.println(nowSh);
System.out.println(nowNy);
// their Instants are equal
System.out.println(nowSh.toInstant().equals(nowNy.toInstant())); // true
}
}Instant: the epoch Timestamp
Use cases: passing values to external systems, timestamp columns in a DB, log ordering, computing time differences. Instant is "seconds/nanos since 1970-01-01 UTC" — zoneless, an absolute instant.
// InstantDemo.java
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class InstantDemo {
public static void main(String[] args) {
Instant now = Instant.now();
System.out.println(now); // 2026-05-12T08:30:00.123Z
System.out.println(now.toEpochMilli()); // Unix millis
System.out.println(now.getEpochSecond()); // Unix seconds
// Instant <-> ZonedDateTime
ZonedDateTime sh = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(sh);
// reconstruct from epoch millis
Instant fromMs = Instant.ofEpochMilli(1_715_000_000_000L);
System.out.println(fromMs);
}
}Duration vs Period
Duration expresses "a precise length based on seconds/nanos" (5 hours, 200 ms); Period expresses "a calendar-level length in years/months/days" (2 months, 3 years). They are not interchangeable: "one month" has no fixed length (28~31 days) and cannot be expressed with Duration.
// DurationPeriod.java
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
public class DurationPeriod {
public static void main(String[] args) {
// Duration: seconds/nanos
Duration d = Duration.ofMinutes(90).plusSeconds(30);
System.out.println(d); // PT1H30M30S
System.out.println(d.toMinutes() + "m"); // 90m
LocalTime t1 = LocalTime.of(9, 0);
LocalTime t2 = LocalTime.of(17, 30);
Duration worked = Duration.between(t1, t2);
System.out.println(worked.toHours() + "h");
// Period: years/months/days
Period p = Period.of(2, 6, 0); // 2 years 6 months
LocalDate from = LocalDate.of(2020, 1, 1);
LocalDate to = LocalDate.of(2026, 5, 12);
Period age = Period.between(from, to);
System.out.printf("%dy %dm %dd%n", age.getYears(), age.getMonths(), age.getDays());
}
}DateTimeFormatter: Formatting and Parsing
Use cases: API time fields, log timestamps, CSV/Excel export. Provides common formats (ISO_DATE / ISO_DATE_TIME / RFC_1123) and custom patterns. **Unlike SimpleDateFormat, DateTimeFormatter is thread-safe and can be a shared static final field.**
// FormatDate.java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FormatDate {
static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.of(2026, 5, 12, 14, 30);
// standard ISO
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// custom
System.out.println(now.format(FMT));
// short form
System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
// parse
LocalDate d = LocalDate.parse("2026-05-12");
System.out.println(d);
LocalDateTime dt = LocalDateTime.parse(
"2026-05-12 14:30",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
);
System.out.println(dt);
}
}ChronoUnit: Precise-Unit Calculation
Use case: compute days / hours / months between two dates. ChronoUnit.X.between(a, b) is clearer than hand-computing Duration / Period fields.
// ChronoBetween.java
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class ChronoBetween {
public static void main(String[] args) {
LocalDate signup = LocalDate.of(2024, 3, 1);
LocalDate today = LocalDate.of(2026, 5, 12);
System.out.println(ChronoUnit.DAYS.between(signup, today)); // total days
System.out.println(ChronoUnit.MONTHS.between(signup, today)); // 26
LocalDateTime start = LocalDateTime.of(2026, 5, 12, 9, 0);
LocalDateTime end = LocalDateTime.of(2026, 5, 12, 14, 30);
System.out.println(ChronoUnit.MINUTES.between(start, end)); // 330
}
}Common Date Calculations
// DateMath.java
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
public class DateMath {
public static void main(String[] args) {
LocalDate today = LocalDate.of(2026, 5, 12);
System.out.println(today.with(TemporalAdjusters.firstDayOfMonth())); // first day of month
System.out.println(today.with(TemporalAdjusters.lastDayOfMonth())); // last day of month
System.out.println(today.with(TemporalAdjusters.next(DayOfWeek.MONDAY))); // next Monday
System.out.println(today.with(TemporalAdjusters.firstDayOfNextMonth())); // 1st of next month
// leap year
System.out.println(today.isLeapYear());
}
}