☕ Java

Duration

Duration is the java.time class representing a time-based amount measured in exact seconds and nanoseconds — a fixed-length quantity like '90 minutes' or '3.5 seconds' rather than a calendar-based amount like Period's years/months/days. This entry covers why Duration is built on a fixed-length second rather than calendar units, how it is created from various time units and parsed from ISO-8601 duration strings, how Duration.between() measures elapsed time between temporal values, and the specific pitfall of using Duration against date-only types it isn't designed to support.

Duration as a Fixed-Length, Time-Based Amount

Duration stores its amount as a count of seconds plus a nanosecond adjustment, both fixed-length units with no calendar ambiguity whatsoever — unlike Period's years/months/days, a second is always exactly the same length regardless of which second it is or what date it falls on, which makes Duration the correct type for any quantity of time that should behave as a constant, addable amount: how long a task took to run, a timeout value, a cooldown period, the length of a video. This is the precise opposite design philosophy from Period, and the two types are deliberately not interchangeable in general — there is no meaningful way to convert "3 months" into a fixed number of seconds without anchoring it to specific real calendar dates first, so Duration simply does not attempt to model calendar-based quantities at all. A practical consequence of this fixed-length design is that Duration arithmetic is always exact and never needs to consult any calendar rules, time zone, or leap-year table — adding Duration.ofHours(2) to anything always means exactly 7200 seconds later, full stop, which is both Duration's main strength (utterly predictable, simple arithmetic) and the source of the one pitfall covered later in this entry (it does not, by itself, know about or adjust for daylight saving transitions the way calendar-aware addition on ZonedDateTime does).
Java
// ── Duration — fixed-length seconds + nanoseconds, no calendar ambiguity
Duration twoHours = Duration.ofHours(2);
System.out.println(twoHours.getSeconds());   // 7200 — ALWAYS exactly 7200, no exceptions

// ── Period and Duration are NOT generally interchangeable ─────────────
// Period threeMonths = Period.ofMonths(3);
// Duration equivalent = threeMonths.???   // no general conversion exists —
// "3 months" has no fixed number of seconds without anchoring to real dates

// ── Duration arithmetic never consults a calendar or time zone ───────
Duration combined = Duration.ofHours(1).plus(Duration.ofMinutes(30));
System.out.println(combined);   // PT1H30M — purely additive, no calendar rules involved

Creating, Parsing, and Decomposing a Duration

Duration offers direct factory methods for every common unit — ofSeconds(n), ofMinutes(n), ofHours(n), ofDays(n) (which internally just means exactly 24 hours, not a calendar day), ofMillis(n), and ofNanos(n) — all of which internally normalize down to the same underlying seconds-plus-nanoseconds representation regardless of which factory method was used to construct them, meaning Duration.ofMinutes(60) and Duration.ofHours(1) produce genuinely equal Duration instances. Duration.parse(text) accepts the ISO-8601 duration format specifically (a string starting with "PT" for a purely time-based duration, like "PT2H30M" for two hours thirty minutes), throwing DateTimeParseException for any text not matching that format, and Duration.toString() produces that same ISO-8601 representation in reverse. Decomposing an existing Duration back into individual human-readable units is done via toHours(), toMinutes(), toSeconds(), toMillis(), and toNanos(), each returning the total duration flattened entirely into that single unit (toMinutes() on a Duration of 90 seconds returns 1, truncating any remainder, not 1.5) — for getting just the "remainder" portion within a larger unit (such as "the minutes part of an hours+minutes display"), the to{Unit}Part() family (toHoursPart(), toMinutesPart(), toSecondsPart(), and so on, added in Java 9) returns each field's contribution in isolation rather than the cumulative flattened total.
Java
// ── Multiple equivalent ways to construct the SAME underlying Duration
Duration fromMinutes = Duration.ofMinutes(90);
Duration fromHoursAndMinutes = Duration.ofHours(1).plus(Duration.ofMinutes(30));
System.out.println(fromMinutes.equals(fromHoursAndMinutes));   // true — same total seconds

// ── Parsing/formatting — ISO-8601 duration format, "PT" prefix ────────
Duration parsed = Duration.parse("PT2H30M");   // 2 hours, 30 minutes
System.out.println(parsed);                     // PT2H30M — same format, round-trips

// ── Flattened totals vs individual "Part" components ──────────────────
Duration d = Duration.ofMinutes(150);            // 2 hours, 30 minutes total

System.out.println(d.toMinutes());      // 150 — entire duration flattened into minutes
System.out.println(d.toHours());        // 2 — entire duration flattened into hours, truncated

System.out.println(d.toHoursPart());    // 2 — just the "hours" component in isolation
System.out.println(d.toMinutesPart());  // 30 — just the REMAINING minutes after those 2 hours
// toHoursPart()+toMinutesPart() gives a human display "2h 30m"
// toHours()+toMinutes() would WRONGLY give "2h 150m"

Duration.between() and Its Limits with Date-Only Types

Duration.between(startInclusive, endExclusive) computes the exact elapsed time-based amount between any two Temporal values that support time-based measurement — most commonly two Instant values, two LocalDateTime values, or two LocalTime values (with the midnight-wraparound caveat already covered under LocalTime) — returning a Duration expressing that exact elapsed amount down to nanosecond precision where the underlying types support it. Negative durations are produced, exactly as with Period, whenever the end value comes before the start value chronologically. Duration.between() specifically cannot be meaningfully called on two plain LocalDate values, since LocalDate carries no time-of-day component at all, and Duration is fundamentally a time-based (not date-based) measurement — attempting it throws UnsupportedTemporalTypeException at runtime, precisely because "elapsed seconds between two dates with no time-of-day" is not a question Duration is designed to answer; Period.between() (or ChronoUnit.DAYS.between()) is the correct tool for that specific date-only case instead, which is the cleanest practical rule for choosing between Period and Duration: if time-of-day or zone matters and exactness in seconds is the goal, use Duration; if only whole calendar dates are involved, use Period.
Java
// ── Duration.between — works on time-aware types ─────────────────────
Instant start = Instant.parse("2024-06-15T10:00:00Z");
Instant end = Instant.parse("2024-06-15T14:45:30Z");
Duration elapsed = Duration.between(start, end);
System.out.println(elapsed);   // PT4H45M30S

LocalDateTime ldtStart = LocalDateTime.of(2024, 6, 15, 9, 0);
LocalDateTime ldtEnd = LocalDateTime.of(2024, 6, 16, 11, 30);
Duration ldtElapsed = Duration.between(ldtStart, ldtEnd);
System.out.println(ldtElapsed.toHours());   // 26 — crosses a day boundary correctly, since
// LocalDateTime DOES carry a date component, unlike plain LocalTime

// ── Duration.between on plain LocalDate — throws, NOT date-based ─────
LocalDate dateA = LocalDate.of(2024, 1, 1);
LocalDate dateB = LocalDate.of(2024, 6, 15);
// Duration.between(dateA, dateB);
// throws UnsupportedTemporalTypeException — LocalDate has no time-of-day
// for Duration to measure in the first place

// ── Correct tool for date-only spans is Period (or ChronoUnit.DAYS) ───
Period correctSpan = Period.between(dateA, dateB);   // P5M14D

Related Topics in Date Time API

LocalDate
LocalDate is the java.time class (introduced in Java 8 as part of the redesigned date-time API replacing the deprecated java.util.Date/Calendar) representing a date without a time component or a time zone — just a year, month, and day on the ISO-8601 calendar. This entry covers why LocalDate deliberately carries no time-of-day or zone information, how it is created and parsed, how it is manipulated via its immutable, fluent with*/plus*/minus* methods, how two LocalDate instances are compared and the period between them measured, and the common pitfalls of treating it as if it secretly carried a time or zone after all.
LocalTime
LocalTime is the java.time class representing a time-of-day without any date or time zone attached — just an hour, minute, second, and nanosecond on a 24-hour clock. This entry covers what LocalTime intentionally omits and why, how to create and parse LocalTime values with varying precision, how arithmetic on LocalTime wraps around midnight instead of rolling into a different day, and how LocalTime is meant to be combined with LocalDate (via LocalDateTime) rather than used as a date substitute.
LocalDateTime
LocalDateTime is the java.time class combining a LocalDate and a LocalTime into a single date-and-time-of-day value that still, deliberately, carries no time zone or UTC-offset information. This entry covers why LocalDateTime is best understood as a composite of the two simpler types rather than a primitive type in its own right, how it is created from and decomposed back into its LocalDate/LocalTime parts, why the absence of a zone makes it unsuitable for representing one unambiguous, real-world instant, and how it relates to and converts into the zone-aware types (ZonedDateTime, Instant) when an actual moment in time is what's needed.
ZonedDateTime
ZonedDateTime is the java.time class representing a LocalDateTime combined with a ZoneId, fully resolved against the time zone's rules to also carry a specific UTC offset — making it the type capable of representing one genuinely unambiguous real-world instant while still preserving human-readable, zone-local wall-clock fields. This entry covers the relationship between ZoneId and the resolved offset, how daylight saving transitions create gaps and overlaps that ZonedDateTime must resolve, how zone-aware arithmetic differs from plain LocalDateTime arithmetic across those transitions, and when to prefer ZonedDateTime over the simpler offset-only OffsetDateTime or the zone-independent Instant.