The java.time package (since Java 8, JSR-310) replaces the legacy java.util.Date and Calendar with an immutable, thread-safe, and intuitive date/time API.

Why java.time?

Problem with old API java.time Solution
Mutable (Date.setTime()) Immutable objects
Not thread-safe Thread-safe by design
Poor API design Clear, fluent API
No timezone support in Date Explicit timezone types

Core Classes

Class Purpose Example
LocalDate Date without time 2024-06-15
LocalTime Time without date 14:30:00
LocalDateTime Date and time, no timezone 2024-06-15T14:30
ZonedDateTime Date, time, and timezone 2024-06-15T14:30+08:00[Asia/Shanghai]
Instant UTC timestamp 2024-06-15T06:30:00Z
Duration Time-based amount 2 hours, 30 minutes
Period Date-based amount 1 year, 3 months

Creating Date/Time Objects

  LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, 5, 20);
LocalDate parsed = LocalDate.parse("2024-06-15");

LocalTime now = LocalTime.now();
LocalTime meeting = LocalTime.of(14, 30, 0);

LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime event = LocalDateTime.of(2024, 6, 15, 14, 30);

Instant timestamp = Instant.now();
Instant epoch = Instant.ofEpochSecond(1_700_000_000);
  

Arithmetic

  LocalDate nextWeek = today.plusDays(7);
LocalDate lastMonth = today.minusMonths(1);
LocalDate nextYear = today.plusYears(1);

LocalDateTime later = dateTime.plusHours(3).plusMinutes(30);

Duration twoHours = Duration.ofHours(2);
Duration between = Duration.between(startTime, endTime);

Period threeMonths = Period.ofMonths(3);
Period age = Period.between(birthday, today);
System.out.println(age.getYears() + " years old");
  

Comparison

  LocalDate d1 = LocalDate.of(2024, 1, 1);
LocalDate d2 = LocalDate.of(2024, 6, 15);

System.out.println(d1.isBefore(d2));  // true
System.out.println(d1.isAfter(d2));   // false
System.out.println(d1.equals(d2));    // false

// Extract fields
System.out.println(d2.getYear());     // 2024
System.out.println(d2.getMonth());    // JUNE
System.out.println(d2.getDayOfWeek()); // SATURDAY
  

Timezones

  ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZoneId utc = ZoneId.of("UTC");

ZonedDateTime zdt = ZonedDateTime.now(shanghai);
ZonedDateTime converted = zdt.withZoneSameInstant(ZoneId.of("America/New_York"));

// All ZoneIds
ZoneId.getAvailableZoneIds().forEach(System.out::println);
  

Instant — Machine Timestamp

  Instant now = Instant.now();
Instant later = now.plusSeconds(3600);

long epochMilli = now.toEpochMilli();
Instant fromEpoch = Instant.ofEpochMilli(epochMilli);
  

Best for storing timestamps in databases and APIs (always UTC).

Converting Legacy Date

  // Date → Instant → LocalDateTime
Date oldDate = new Date();
Instant instant = oldDate.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

// LocalDateTime → Instant → Date
Instant inst = ldt.atZone(ZoneId.systemDefault()).toInstant();
Date converted = Date.from(inst);
  

Best Practices

  • Use LocalDate/LocalDateTime for business logic without timezone
  • Use ZonedDateTime when timezone matters (user-facing times)
  • Use Instant for storage and API timestamps (UTC)
  • Use Duration for time intervals, Period for calendar intervals
  • Never use java.util.Date or Calendar in new code