Java Dates and Time — The java.time API (Complete Guide)

3 minute read

Java 8 introduced the java.time package — a modern, immutable, thread-safe date/time API that replaces the broken java.util.Date and Calendar.


Why Not java.util.Date?

Problem java.util.Date java.time
Mutable ✅ Bug-prone ❌ Immutable
Thread-safe ❌ ✅
Time zones Confusing Clean ZoneId API
Month indexing 0-based (Jan=0) 1-based (Jan=1)
Year handling Year – 1900 Normal year

Always use java.time for new code.


LocalDate — Date Without Time

import java.time.LocalDate;

LocalDate today = LocalDate.now();           // 2026-04-20
LocalDate specific = LocalDate.of(2026, 1, 15); // 2026-01-15
LocalDate parsed = LocalDate.parse("2026-12-25");

today.getYear()        // 2026
today.getMonth()       // APRIL
today.getMonthValue()  // 4
today.getDayOfMonth()  // 20
today.getDayOfWeek()   // MONDAY
today.getDayOfYear()   // 110
today.isLeapYear()     // false

Manipulating Dates

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

today.withMonth(12)     // 2026-12-20
today.withDayOfMonth(1) // 2026-04-01

All methods return a new LocalDate (immutable).


LocalTime — Time Without Date

import java.time.LocalTime;

LocalTime now = LocalTime.now();           // 14:30:45.123
LocalTime lunch = LocalTime.of(12, 30);   // 12:30
LocalTime precise = LocalTime.of(14, 30, 45); // 14:30:45

now.getHour()    // 14
now.getMinute()  // 30
now.getSecond()  // 45

LocalTime later = now.plusHours(2).plusMinutes(15);

LocalDateTime — Date + Time

import java.time.LocalDateTime;

LocalDateTime now = LocalDateTime.now();
LocalDateTime meeting = LocalDateTime.of(2026, 4, 20, 14, 30);

// Combine date and time
LocalDate date = LocalDate.of(2026, 4, 20);
LocalTime time = LocalTime.of(14, 30);
LocalDateTime combined = LocalDateTime.of(date, time);

ZonedDateTime — With Time Zone

import java.time.ZonedDateTime;
import java.time.ZoneId;

ZonedDateTime nowUtc = ZonedDateTime.now(ZoneId.of("UTC"));
ZonedDateTime nowNY  = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime nowIST = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));

// Convert between zones
ZonedDateTime tokyoTime = nowNY.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));

List Available Zones

ZoneId.getAvailableZoneIds().stream()
    .filter(z -> z.startsWith("America"))
    .sorted()
    .forEach(System.out::println);

Instant — Machine Timestamp

Represents a point on the timeline (UTC), typically for timestamps and logging:

import java.time.Instant;

Instant now = Instant.now();        // 2026-04-20T09:30:45.123Z
long epoch = now.toEpochMilli();    // milliseconds since 1970-01-01
Instant from = Instant.ofEpochMilli(epoch);

Duration — Time-Based Amount

import java.time.Duration;

Duration d = Duration.ofHours(2).plusMinutes(30);
d.toMinutes()    // 150

Duration between = Duration.between(
    LocalTime.of(9, 0), LocalTime.of(17, 30));
between.toHours()  // 8

Period — Date-Based Amount

import java.time.Period;

Period p = Period.of(1, 2, 3);        // 1 year, 2 months, 3 days
Period between = Period.between(
    LocalDate.of(2024, 1, 1),
    LocalDate.of(2026, 4, 20));

between.getYears()   // 2
between.getMonths()  // 3
between.getDays()    // 19

DateTimeFormatter — Parsing and Formatting

Built-in Formatters

import java.time.format.DateTimeFormatter;

LocalDate date = LocalDate.now();
date.format(DateTimeFormatter.ISO_LOCAL_DATE)  // "2026-04-20"

Custom Patterns

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MMM yyyy");
String formatted = date.format(fmt);  // "20 Apr 2026"

// Parse from string
LocalDate parsed = LocalDate.parse("20 Apr 2026", fmt);

DateTime Patterns

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
System.out.println(now.format(dtf));  // "20/04/2026 14:30:45"
Pattern Meaning Example
yyyy Year 2026
MM Month (number) 04
MMM Month (short) Apr
MMMM Month (full) April
dd Day 20
HH Hour (24h) 14
hh Hour (12h) 02
mm Minute 30
ss Second 45
a AM/PM PM
EEEE Day name Monday

Comparing Dates

LocalDate a = LocalDate.of(2026, 1, 1);
LocalDate b = LocalDate.of(2026, 12, 31);

a.isBefore(b)   // true
a.isAfter(b)    // false
a.isEqual(a)    // true
a.compareTo(b)  // negative

Days Between Dates

import java.time.temporal.ChronoUnit;

long days = ChronoUnit.DAYS.between(
    LocalDate.of(2026, 1, 1),
    LocalDate.of(2026, 4, 20));
// 109

Practical Examples

Age Calculator

public static int calculateAge(LocalDate birthDate) {
    return Period.between(birthDate, LocalDate.now()).getYears();
}

calculateAge(LocalDate.of(1990, 6, 15))  // 35

Next Business Day

public static LocalDate nextBusinessDay(LocalDate date) {
    LocalDate next = date.plusDays(1);
    while (next.getDayOfWeek() == DayOfWeek.SATURDAY ||
           next.getDayOfWeek() == DayOfWeek.SUNDAY) {
        next = next.plusDays(1);
    }
    return next;
}

Common Mistakes

Mistake Problem Fix
Using java.util.Date Mutable, confusing API Use java.time
Ignoring time zones Wrong times across regions Use ZonedDateTime
LocalDateTime for timestamps No time zone info Use Instant
Not storing dates as UTC Inconsistent data Store UTC, display local

Key Takeaways

  1. Use LocalDate for dates, LocalTime for times, LocalDateTime for both.
  2. Use ZonedDateTime when time zones matter.
  3. Use Instant for machine timestamps.
  4. Use Duration for time amounts, Period for date amounts.
  5. Use DateTimeFormatter for custom parsing and formatting.
  6. All java.time classes are immutable and thread-safe.

What’s Next?

Next: Wrapper Classes — autoboxing, unboxing, Integer.parseInt(), and when to use wrappers vs primitives.

Updated: