Unravel › Java concepts › Time and dates
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
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.
- 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.
- 2LocalDate.parse("2026-03-01") uses ISO format; LocalDate.parse(text, formatter) uses yours and throws DateTimeParseException for bad text
- 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.
- 4TemporalAdjusters.firstDayOfMonth(), lastDayOfMonth(), next(DayOfWeek.MONDAY) find calendar positions; YearMonth.lengthOfMonth() gives the days in a month
- Formatters are immutable and thread safe (unlike SimpleDateFormat): make them static final
- Parsing with ofPattern is lenient about locale: month names depend on the Locale
- A DateTimeFormatter is compiled once into a tree of printer-parser objects
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); }}- line 5 uses ofPattern, the core of Formatting, parsing and date arithmetic
- line 7 uses DateTimeParseException, the core of Formatting, parsing and date arithmetic
- line 9 uses isAfter, the core of Formatting, parsing and date arithmetic
- 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
mminstead ofMM.YYYYinstead ofyyyy.- Using
Periodwhere 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:
- Types:
DateTimeParseExceptionDateTimeExceptionTemporalAdjustersDayOfWeekYearMonthMonth - Method calls:
DateTimeFormatter.ofPattern()ofPattern()ChronoUnit.DAYS.between()Duration.between()Period.between()plusDays()plusMonths()isBefore()isAfter()withDayOfMonth()lengthOfMonth()ISO_LOCAL_DATE()
Part of a learning path
- I/O, networking and time · previous: Properties files, Locale and resource bundles · next: Time zones, DST and Instant
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.