BackendBench
Coffee

Unravel › Java concepts › Time and dates

Java language

Formatting, parsing and date arithmetic

DateTimeFormatter.ofPattern("dd.MM.yyyy") turns dates into text and back, and methods like plusDays, isBefore and ChronoUnit.DAYS.between do date arithmetic without the pitfalls of old Date code.

Learn these first

At a glance

Mental model

A calendar and a stopwatch with a built-in translator: add three weeks, count the days between two dates, and write the result in whichever notation the reader expects.

What happens, in order
  1. 1Pattern letters: yyyy year, MM month number, MMM month name, dd day, HH 24-hour, hh 12-hour, mm minutes, ss seconds, a am/pmCase matters: mm is minutes, MM is months.
  2. 2LocalDate.parse("2026-03-01") uses ISO format; LocalDate.parse(text, formatter) uses yours and throws DateTimeParseException for bad text
  3. 3date.plusMonths(1) on January 31 gives February 28 or 29 (it clamps)ChronoUnit.DAYS.between(a, b) counts days; Period.between(a, b) returns years, months and days; Duration.between(t1, t2) returns exact elapsed time.
  4. 4TemporalAdjusters.firstDayOfMonth(), lastDayOfMonth(), next(DayOfWeek.MONDAY) find calendar positions; YearMonth.lengthOfMonth() gives the days in a month
Remember
Watch out
mm instead of MMYYYY instead of yyyyUsing Period where elapsed time is neededCatching no exception when parsing user input
Example (highlighted lines explain it)
import java.time.*;import java.time.format.*;import java.time.temporal.ChronoUnit;public class Billing {  private static final DateTimeFormatter DAY = DateTimeFormatter.ofPattern("dd.MM.yyyy");  static LocalDate parse(String s) {    try { return LocalDate.parse(s, DAY); } catch (DateTimeParseException e) { throw new IllegalArgumentException("bad date " + s, e); }  }  static long daysOverdue(LocalDate due, LocalDate today) { return today.isAfter(due) ? ChronoUnit.DAYS.between(due, today) : 0; }  static String nextInvoice(LocalDate d) { return d.plusMonths(1).format(DAY); }}
  1. line 5 uses ofPattern, the core of Formatting, parsing and date arithmetic
  2. line 7 uses DateTimeParseException, the core of Formatting, parsing and date arithmetic
  3. line 9 uses isAfter, the core of Formatting, parsing and date arithmetic
  4. line 10 uses plusMonths, the core of Formatting, parsing and date arithmetic
Read the full explanation (about 1 min)

DateTimeFormatter.ofPattern("dd.MM.yyyy") turns dates into text and back, and methods like plusDays, isBefore and ChronoUnit.DAYS.between do date arithmetic without the pitfalls of old Date code.

A mental model

A calendar and a stopwatch with a built-in translator: add three weeks, count the days between two dates, and write the result in whichever notation the reader expects.

How it works

Pattern letters: yyyy year, MM month number, MMM month name, dd day, HH 24-hour, hh 12-hour, mm minutes, ss seconds, a am/pm. Case matters: mm is minutes, MM is months.

LocalDate.parse("2026-03-01") uses ISO format; LocalDate.parse(text, formatter) uses yours and throws DateTimeParseException for bad text.

date.plusMonths(1) on January 31 gives February 28 or 29 (it clamps). ChronoUnit.DAYS.between(a, b) counts days; Period.between(a, b) returns years, months and days; Duration.between(t1, t2) returns exact elapsed time.

TemporalAdjusters.firstDayOfMonth(), lastDayOfMonth(), next(DayOfWeek.MONDAY) find calendar positions; YearMonth.lengthOfMonth() gives the days in a month.

Going deeper

Formatters are immutable and thread safe (unlike SimpleDateFormat): make them static final. Use yyyy, not YYYY (week-based year, wrong around New Year).

Parsing with ofPattern is lenient about locale: month names depend on the Locale. Give one explicitly (Locale.ENGLISH) for fixed input formats.

Do not compare dates by formatting them to strings or subtracting milliseconds; use isBefore, isAfter, equals or compareTo.

Under the hood

A DateTimeFormatter is compiled once into a tree of printer-parser objects. The value classes are immutable and based on the ISO calendar; arithmetic uses epoch-day or epoch-second counts internally.

Common mistakes

  • mm instead of MM.
  • YYYY instead of yyyy.
  • Using Period where elapsed time is needed.
  • Catching no exception when parsing user input.

How Unravel recognises it

When you paste code, Unravel looks for these signs of formatting, parsing and date arithmetic. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Formatting, parsing and date arithmetic?

DateTimeFormatter.ofPattern("dd.MM.yyyy") turns dates into text and back, and methods like plusDays, isBefore and ChronoUnit.DAYS.between do date arithmetic without the pitfalls of old Date code.

What are the most common mistakes with Formatting, parsing and date arithmetic?

mm instead of MM. YYYY instead of yyyy. Using Period where elapsed time is needed. Catching no exception when parsing user input.

How can I see Formatting, parsing and date arithmetic in real code?

Paste your own Java into Unravel and it will point out where formatting, parsing and date arithmetic appears, explain how it works at the detail level you choose, and draw the flow of the code around it.