Formatting & parsing dates in Java
DateTimeFormatter patterns; legacy Date/Calendar/SimpleDateFormat problems.
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 2024Case 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!
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));18:0718:1206:07
Show the answer
18:12 — uppercase **MM is the month (December = 12). Minutes are lowercase mm**.
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);25/12/20242024-12-25Throws 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.
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");
// DateTimeParseExceptionSharing a formatter between threads
static SimpleDateFormat FMT =
new SimpleDateFormat("dd/MM/yyyy");
// + mutable Date, 0-based monthsSimpleDateFormat keeps mutable state while working — shared between threads it returns corrupt dates.
static final DateTimeFormatter FMT =
DateTimeFormatter
.ofPattern("dd/MM/yyyy");Immutable and thread-safe: one shared constant is perfect.
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
- MM = month, mm = minute, HH = 0-23 hour, hh = 1-12
- yyyy = year; YYYY = week-based year (a classic bug)
- parse() throws DateTimeParseException on a mismatch
- Legacy Date is mutable; SimpleDateFormat isn't thread-safe
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));- 2024-07-04
- 2024-05-04
- 2024-7-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);- 2024-05-03
- 2024-03-05
- 05/03/2024
- 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.