🧰 Core APIs Toolbox · Intermediate

Duration vs Period in Java

Time-based vs date-based amounts.

🧩 The mystery"How many days between Jan 15 and Mar 10?" Your code says 24. The real answer is 55. The code isn't buggy — it answered a different question. Which one?

Stopwatch vs calendar

**Duration is a stopwatch: an exact amount of time in seconds and nanoseconds ("90 minutes"). Period is a calendar amount in years, months and days** ("1 month 3 days") whose real length varies — a month is 28 to 31 days.

Duration call = Duration.ofMinutes(45);
Period trial = Period.ofDays(14);
LocalDate end = start.plus(trial);

Reading ISO-8601

Both print in ISO-8601: **P starts the amount and T** separates the time part. PT1H30M = 1 hour 30 minutes; P1M3D = 1 month 3 days. Careful: **P90M means 90 *months*** — minutes always come after the T. Duration normalises minutes into hours.

🔮 Predict it

Seconds to ISO

What does this print?

System.out.println(Duration.ofSeconds(3725));
  1. PT3725S
  2. PT1H2M5S
  3. P1H2M5S
Show the answer

PT1H2M5S — 3725 seconds = 1 hour, 2 minutes and 5 seconds, normalised and written after the T.

🔮 Predict it

Months and days, separately

What does this print?

Period p = Period.between(
    LocalDate.of(2024, 1, 10),
    LocalDate.of(2024, 3, 15));
System.out.println(p);
System.out.println(p.getDays());
  1. P65D 65
  2. P2M5D 5
  3. P2M5D 65
Show the answer

P2M5D and 5. A Period stores months and days separately; **getDays() is only the days part**, not the total.

Total days: ChronoUnit

To count every day between two dates, use **ChronoUnit.DAYS.between(a, b)**. (Subtracting getDayOfYear() values breaks across years.)

long days = ChronoUnit.DAYS.between(
    LocalDate.of(2024, 1, 10),
    LocalDate.of(2024, 3, 15)); // 65
⚠️ The trap

Duration can't measure dates

Duration needs seconds, but a LocalDate has no time of day, so this throws **UnsupportedTemporalTypeException**. For dates use Period or ChronoUnit.DAYS, or convert with atStartOfDay() first.

Duration.between(
    LocalDate.of(2024, 1, 1),
    LocalDate.of(2024, 1, 2));
// UnsupportedTemporalTypeException
💼 In the real world

Choosing right on the job

Timeouts, SLAs and cache expiry are Durations. Free trials, contract terms and "age in years" are Periods. Confusing getDays() with total days is a classic bug in billing and reporting code — it passes tests until a range crosses a month.

Key takeaways

  1. Duration: hours, minutes, seconds — for times and Instants
  2. Period: years, months, days — for LocalDates
  3. Period.getDays() is only the days part, not the total
  4. ChronoUnit.DAYS.between(a, b) gives the total days
🤯 Did you know?

In ISO-8601, PT1M is one minute and P1M is one month — a single letter T makes a difference of roughly 43,000 times.

Practice questions

What does this print?

System.out.println(Duration.ofMinutes(90));
  1. PT90M
  2. PT1H30M
  3. 1:30
  4. P90M
Check your answer

PT1H30M. Duration.toString uses ISO-8601 and normalises minutes into hours: PT1H30M. In ISO, P starts the amount and T separates the time part; P90M would mean 90 months.

What does this print?

Period p = Period.between(
    LocalDate.of(2024, 1, 15),
    LocalDate.of(2024, 3, 10));
System.out.println(p);
System.out.println(p.getDays());
  1. P55D 55
  2. P1M24D 24
  3. P2M5D 5
  4. P1M24D 55
Check your answer

P1M24D 24. From 15 Jan to 15 Feb is 1 month; from 15 Feb to 10 Mar is 24 days (2024's February has 29 days). getDays() returns just that 24, not the 55 total days.

Next: turning dates into text and back — where mm and MM are completely different things.