🧰 Core APIs Toolbox · Intermediate

Formatting & parsing dates in Java

DateTimeFormatter patterns; legacy Date/Calendar/SimpleDateFormat problems.

🧩 The mysteryA date formatter prints Christmas evening as "18:12" instead of "18:07". One letter's case is wrong. Can you spot which one before it ships?

Patterns: text ↔ dates

**DateTimeFormatter.ofPattern("dd/MM/yyyy") describes a text layout. format turns a date into text; parse** turns text into a date. Printing a LocalDate itself always uses ISO: 2024-03-05.

var fmt = DateTimeFormatter
    .ofPattern("dd MMM yyyy");
LocalDate.of(2024, 7, 4).format(fmt);
// 04 Jul 2024

Case matters

**MM = month, mm = minute. HH = hour 0–23, hh = hour 1–12. yyyy = year, but YYYY = week-based year**, which differs around New Year: 2024-12-30 formats as 2025 with YYYY!

🔮 Predict it

Spot the case

What does this print?

var t = LocalDateTime.of(2024, 12, 25, 18, 7);
var f = DateTimeFormatter.ofPattern("HH:MM");
System.out.println(t.format(f));
  1. 18:07
  2. 18:12
  3. 06:07
Show the answer

18:12 — uppercase **MM is the month (December = 12). Minutes are lowercase mm**.

🔮 Predict it

Parsing a European date

What does this print?

var f = DateTimeFormatter
    .ofPattern("dd/MM/yyyy");
var d = LocalDate.parse("25/12/2024", f);
System.out.println(d);
  1. 25/12/2024
  2. 2024-12-25
  3. Throws DateTimeParseException
Show the answer

2024-12-25 — the pattern reads day 25 and month 12, but printing a LocalDate always uses ISO year-month-day.

⚠️ The trap

parse is strict

LocalDate.parse(text) without a formatter expects strict ISO with two-digit month and day. "2024-3-5" doesn't match, so it throws **DateTimeParseException** — as does any text that doesn't fit your pattern.

LocalDate.parse("2024-03-05"); // ok
LocalDate.parse("2024-3-5");
// DateTimeParseException

Sharing a formatter between threads

✗ Legacy
static SimpleDateFormat FMT =
    new SimpleDateFormat("dd/MM/yyyy");
// + mutable Date, 0-based months

SimpleDateFormat keeps mutable state while working — shared between threads it returns corrupt dates.

✓ java.time (Java 8)
static final DateTimeFormatter FMT =
    DateTimeFormatter
        .ofPattern("dd/MM/yyyy");

Immutable and thread-safe: one shared constant is perfect.

💼 In the real world

The YYYY bug

Using YYYY instead of yyyy passes every test for 51 weeks, then prints next year's number in the last days of December. It has caused real outages, including expired-looking tokens and broken logins. Many teams lint for YYYY in patterns.

Key takeaways

  1. MM = month, mm = minute, HH = 0-23 hour, hh = 1-12
  2. yyyy = year; YYYY = week-based year (a classic bug)
  3. parse() throws DateTimeParseException on a mismatch
  4. Legacy Date is mutable; SimpleDateFormat isn't thread-safe
🤯 Did you know?

java.time was designed by the author of the popular Joda-Time library, Stephen Colebourne, who co-led JSR 310 to bring a better date API into Java 8.

Practice questions

What does this print?

LocalDateTime t =
    LocalDateTime.of(2024, 7, 4, 9, 5);
var f = DateTimeFormatter.ofPattern("yyyy-mm-dd");
System.out.println(t.format(f));
  1. 2024-07-04
  2. 2024-05-04
  3. 2024-7-4
  4. Throws DateTimeException
Check your answer

2024-05-04. Lowercase mm means minute-of-hour, so the 5 minutes appear where the month should be. Months are uppercase MM.

What does this print?

LocalDate d = LocalDate.parse("05/03/2024",
    DateTimeFormatter.ofPattern("dd/MM/yyyy"));
System.out.println(d);
  1. 2024-05-03
  2. 2024-03-05
  3. 05/03/2024
  4. Throws DateTimeParseException
Check your answer

2024-03-05. The pattern reads day 05, month 03. Printing a LocalDate always uses ISO format, year-month-day: 2024-03-05.

Next: regular expressions — tiny patterns with enough power to eat your entire string by accident.