Java Duration
Representa intervalos de tiempo (horas, minutos, segundos) en Java con Duration.
Duration representa una longitud de tiempo medida en segundos y nanosegundos. "Dos horas." "Quinientos milisegundos." "Cuarenta y cinco minutos." Internamente son dos números — un long seconds y un int nanos — y toda operación sobre él es aritmética de enteros. No conoce calendarios, meses ni horario de verano (DST); si le indicas 24 horas, significa exactamente 86.400 segundos, independientemente de si esas 24 horas abarcan un cambio de horario.
Este es el tipo correcto para: medición de tiempos, timeouts, reintentos con retroceso exponencial, "esperar como máximo X segundos," cualquier situación donde la respuesta es un intervalo de tiempo real. El tipo complementario para longitudes de calendario ("un mes," "dos años") es Period, que se trata en el siguiente capítulo.
Creación
Tres grupos de fábricas:
Duration.ofNanos(500_000_000);
Duration.ofMillis(500);
Duration.ofSeconds(45);
Duration.ofSeconds(60, 500_000_000); // with nanos
Duration.ofMinutes(2);
Duration.ofHours(1);
Duration.ofDays(7); // exactly 7 * 24 hours
Duration.between(start, end); // from two Temporals
Duration.parse("PT1H30M"); // ISO-8601: PT[hours]H[minutes]M[seconds]SDuration.ofDays(n) es la fábrica que la mayoría de la gente usa y que la mayoría malentiende. Produce exactamente n * 24 * 3600 segundos. En un día con cambio de horario, eso no es lo mismo que "el día siguiente a la misma hora del reloj." Para un "un día después" con forma de calendario, LocalDate.plusDays(1) o Period.ofDays(1) son las herramientas correctas.
El formato de cadena es la duración ISO-8601: PT1H30M45S equivale a una hora, treinta minutos y cuarenta y cinco segundos. El prefijo PT es obligatorio (P = Period, T = Time). Para componentes sub-segundo, la forma es PT0.5S o PT0.000000001S. Duration cubre todo por debajo del día; los periodos (año, mes, día) pertenecen a Period.
Aritmética
d.plus(Duration.ofSeconds(15));
d.plusSeconds(60);
d.plusMinutes(5);
d.plusHours(2);
d.minus(Duration.ofMillis(100));
d.multipliedBy(3);
d.dividedBy(2);
d.negated(); // -d
d.abs();Cada método devuelve un nuevo Duration (la inmutabilidad que encontramos desde LocalDate en adelante).
Los accesores de conversión:
d.toNanos(); // long
d.toMillis(); // long; throws if out of long range
d.toSeconds(); // since Java 9
d.toMinutes();
d.toHours();
d.toDays(); // assumes 24h days
d.getSeconds(); // raw seconds component
d.getNano(); // raw nanos component (0-999_999_999)Los métodos toX() devuelven un único long con el total en esa unidad, truncando. Los métodos getX() devuelven el desglose en bruto. Son cosas distintas; confundirlos es el error más común con Duration.
Duration d = Duration.ofSeconds(125);
d.toMinutes(); // 2 (125 / 60)
d.getSeconds(); // 125 (raw)Para formatear "X minutos Y segundos", usa ambos:
long mins = d.toMinutes();
long secs = d.minusMinutes(mins).getSeconds();
System.out.printf("%d:%02d%n", mins, secs); // 2:05O, desde Java 9, los helpers de desglose dedicados:
d.toHoursPart(); // hours within the duration (0-23-ish on positive durations)
d.toMinutesPart(); // minutes within the duration (0-59)
d.toSecondsPart(); // seconds within the duration (0-59)
d.toMillisPart(); // millis within the secondEstos son los que necesitas para "mostrar 1h 23m 45s" sin hacer el módulo manualmente.
Comparación
d1.isZero(); // d == 0
d1.isNegative(); // d < 0
d1.compareTo(d2);
d1.equals(d2);Duration implementa Comparable<Duration>. El orden es con signo — una duración negativa es menor que cero, que es menor que una duración positiva.
Distancia entre dos Temporals
La fábrica Duration.between(start, end) es la que usarás con mayor frecuencia:
Duration d = Duration.between(start, end); // works on Instant, LocalDateTime, ZonedDateTime, LocalTimeAcepta cualquier par de valores Temporal que soporten unidades basadas en tiempo. LocalDate (solo fecha) no — Duration.between(date1, date2) lanza DateTimeException porque una fecha no tiene componente de reloj. Para distancias de calendario, usa Period.between(date1, date2) o ChronoUnit.DAYS.between(date1, date2).
La convención de signo: positivo si end es posterior a start, negativo en caso contrario.
Añadir a otros temporales
Duration es un TemporalAmount, por lo que cualquier Temporal lo acepta mediante plus/minus:
Instant later = Instant.now().plus(Duration.ofMinutes(30));
LocalDateTime then = LocalDateTime.now().plus(Duration.ofHours(8));Añadir un Duration a un LocalDate lanza una excepción — por la misma razón que antes, no tiene componente de reloj. El compilador no detecta esto; la llamada falla en tiempo de ejecución. Si te encuentras queriendo hacerlo, casi con toda certeza querías usar Period.
Un ejemplo práctico: tiempo, formato y retroceso
El programa siguiente mide el tiempo transcurrido de una pequeña tarea, lo imprime en formato legible usando los helpers toXxxPart, calcula un esquema de retroceso exponencial y demuestra la frontera entre Duration y Period mostrando tanto Duration.between sobre Instants como Period.between sobre LocalDates para el mismo intervalo conceptual.
Lo que se aprende de la ejecución:
- El bloque
Thread.sleep(125)se midió comoPT0.125-algo-S— la variación real delsleepes variabilidad de la máquina real.Duration.between(t0, t1)fue la llamada correcta: dosInstants, sin zona horaria necesaria, respuesta exacta a la resolución del reloj del sistema. - Los helpers
toXxxPart()dieron enteros limpios para "la parte de horas," "la parte de minutos," "la parte de segundos," "la parte de milisegundos." Sin ellos habría que hacer aritmética modulartotal / 3600y(total % 3600) / 60a mano. Úsalos siempre que necesites formatear una duración para un humano. - El bucle de retroceso exponencial escaló la duración con
multipliedBy(2)e imprimió cada resultado. La forma de cadena ISO-8601 (PT2S,PT4S,PT8S, ...) es lo que imprimeDuration.toString; es concisa e inequívoca, ideal para logs. - El bloque "Jan 1 → Apr 1 en tres formas" mostró la frontera entre
DurationyPeriod.Period.between(start, end)devolvióP3M— tres meses de calendario.Duration.betweensobre los instantes UTC equivalentes devolvió un conteo de tiempo real (90 días). Ambos son correctos; responden preguntas distintas. Elige el que cuyo significado coincida con la intención de tu código. - El bloque final intentó
Duration.between(LocalDate, LocalDate)y obtuvoDateTimeException. El sistema de tipos sabe que una fecha no tiene componente de reloj, por lo que rechaza calcular una duración basada en reloj. La solución es (1) adjuntar tiempos (startDate.atStartOfDay(zone)), o (2) usarPeriod.betweensi lo que realmente necesitas es un conteo de calendario. La excepción es el diseño correcto: las respuestas silenciosas a esa pregunta serían engañosas.
Qué viene después
Duration es la longitud en segundos y nanosegundos. El siguiente capítulo, Java Period, es la longitud en años, meses y días — el complemento con forma de calendario. Los dos nunca se mezclan: Duration no conoce los meses, Period no conoce las horas, y cualquier código que maneje ambos recurre a uno u otro dependiendo de si la respuesta debe seguir el calendario o el reloj de pared.