Duration.between()

Measure the exact time between two instants or times as a Duration, and read it out in hours, minutes or milliseconds.

Signature
public static Duration between(Temporal startInclusive, Temporal endExclusive)
  • Added in Java Java 8

Parameters

ParameterTypeDescription
$startInclusiveTemporalThe start, such as an Instant, LocalDateTime, ZonedDateTime or LocalTime.
$endExclusiveTemporalThe end. If it is a different type it is converted to the type of the start.

Return value

DurationThe time between the two temporals in seconds and nanoseconds; negative if the end is before the start.

Errors

DateTimeException if the seconds between the temporals cannot be obtained (UnsupportedTemporalTypeException for date-only types such as LocalDate); ArithmeticException if the result overflows a Duration.

Duration.between(start, end) returns the exact amount of time between two points, as seconds plus nanoseconds. It works on anything with a time part: Instant, LocalDateTime, ZonedDateTime, OffsetDateTime and LocalTime. The start is inclusive and the end exclusive, and if the end comes first the result is negative.

Reading the result

toString() prints ISO-8601, such as PT2H31M35.25S. For numbers, the toXxx() methods give the whole duration in one unit (toMinutes() is 151 for 2 h 31 min), while the toXxxPart() methods, added in Java 9, give one component each (toMinutesPart() is 31). Mixing the two up is a common source of "2h 151m" in log messages.

Dates are not durations

Passing two LocalDate values throws UnsupportedTemporalTypeException: Unsupported unit: Seconds, because a date has no seconds. For whole days use ChronoUnit.DAYS.between; for years, months and days use Period.between.

Zones matter

Between two LocalDateTime values, a day is always 24 hours, because local values know nothing about clock changes. Between two ZonedDateTime or Instant values you get the real elapsed time, so midnight to midnight across the spring clock change is 23 hours. When the answer has to be physically correct, such as billing by the hour, compute it from zoned values or instants.

Back-end uses

  • Request and job timings from stored start and end timestamps.
  • A Retry-After header: the seconds between now and when a rate limit resets.
  • Checking an SLA: did the ticket get a reply within four hours?

Compare durations with compareTo, or test isNegative(); abs() drops the sign when you only care about the gap. For live timing inside one process, take System.nanoTime() at both ends and use Duration.ofNanos(end - start), which is immune to wall-clock jumps.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Logging how long a background job took from its stored startedAt and finishedAt instants.
  • Sending a Retry-After header with the seconds left until a client's rate-limit window resets.
  • Flagging support tickets whose first reply came more than four hours after they were opened.

Frequently asked questions

How do I get the difference between two times in Java?

Use Duration.between(start, end) with Instant, LocalDateTime, ZonedDateTime or LocalTime values, then read it with toMinutes(), toMillis() or the toXxxPart() methods.

Why does Duration.between throw Unsupported unit: Seconds?

You passed LocalDate values, which have no time part. Use ChronoUnit.DAYS.between for days or Period.between for years, months and days.

How do I format a Duration as hours and minutes?

Use toHours() with toMinutesPart() and toSecondsPart(), which were added in Java 9, for example "%dh %02dm".formatted(d.toHours(), d.toMinutesPart()).

More examples

Local versus zoned across a clock change

LocalDateTime ignores the UK clock change on 29 March 2026; ZonedDateTime knows that day had only 23 hours. Use zoned values or instants when the real elapsed time matters.

Example · java
import java.time.*;

void main() {
    ZoneId london = ZoneId.of("Europe/London");
    LocalDateTime from = LocalDateTime.of(2026, 3, 29, 0, 0);
    LocalDateTime to = from.plusDays(1);

    IO.println("local: " + Duration.between(from, to));
    IO.println("zoned: " + Duration.between(from.atZone(london), to.atZone(london)));
}
// Output:
// local: PT24H
// zoned: PT23H

A Retry-After header from a rate-limit reset time

Retry-After takes whole seconds, so any fraction is rounded up to avoid the client retrying too early. A reset time already in the past gives a negative duration, which is clamped to 0.

Example · java
import java.time.*;

long retryAfterSeconds(Clock clock, Instant resetAt) {
    Duration wait = Duration.between(Instant.now(clock), resetAt);
    if (wait.isNegative()) {
        return 0;
    }
    long secs = wait.toSeconds();
    return wait.toNanosPart() > 0 ? secs + 1 : secs;
}

void main() {
    Clock clock = Clock.fixed(Instant.parse("2026-05-01T12:00:00Z"), ZoneOffset.UTC);
    IO.println("Retry-After: " + retryAfterSeconds(clock, Instant.parse("2026-05-01T12:00:41.200Z")));
    IO.println("Retry-After: " + retryAfterSeconds(clock, Instant.parse("2026-05-01T12:01:00Z")));
    IO.println("Retry-After: " + retryAfterSeconds(clock, Instant.parse("2026-05-01T11:59:00Z")));
}
// Output:
// Retry-After: 42
// Retry-After: 60
// Retry-After: 0

Checking an SLA on support tickets

Durations compare with compareTo, so an SLA check is a single comparison against a Duration constant rather than a calculation in raw milliseconds.

Example · java
import java.time.*;

record Ticket(String id, Instant opened, Instant firstReply) {}

void main() {
    Duration sla = Duration.ofHours(4);
    List<Ticket> tickets = List.of(
        new Ticket("T-1", Instant.parse("2026-05-01T08:00:00Z"), Instant.parse("2026-05-01T09:30:00Z")),
        new Ticket("T-2", Instant.parse("2026-05-01T08:00:00Z"), Instant.parse("2026-05-01T12:00:00Z")),
        new Ticket("T-3", Instant.parse("2026-05-01T08:00:00Z"), Instant.parse("2026-05-01T13:05:00Z")));

    for (Ticket t : tickets) {
        Duration took = Duration.between(t.opened(), t.firstReply());
        String status = took.compareTo(sla) <= 0 ? "met" : "missed";
        IO.println(t.id() + " " + took + " " + status);
    }
}
// Output:
// T-1 PT1H30M met
// T-2 PT4H met
// T-3 PT5H5M missed

Discussion

  • Be the first to comment on this lesson.