Java Duration
Represente intervalos de tempo (horas, minutos, segundos) em Java com a classe Duration.
Duration representa um intervalo de tempo medido em segundos e nanossegundos. "Duas horas." "Quinhentos milissegundos." "Quarenta e cinco minutos." Internamente são dois números — um long seconds e um int nanos — e toda operação sobre ele é aritmética inteira. Ele não conhece calendários, meses ou DST; se você informar 24 horas, significa exatamente 86.400 segundos, independentemente de esse período abranger uma transição de DST.
Este é o tipo correto para: medições de tempo, timeouts, tentativas com backoff exponencial, "aguardar no máximo X segundos," qualquer situação em que a resposta seja um intervalo de tempo real. O tipo complementar para durações em calendário ("um mês," "dois anos") é Period, abordado no próximo capítulo.
Criação
Três 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) é a fábrica que a maioria das pessoas usa e que a maioria entende de forma errada. Ela produz exatamente n * 24 * 3600 segundos. Em um dia de transição de DST, isso não é o mesmo que "o próximo dia no mesmo horário do relógio de parede." Para "um dia depois" em forma de calendário, LocalDate.plusDays(1) ou Period.ofDays(1) são as ferramentas corretas.
O formato de string é a duração ISO-8601: PT1H30M45S é uma hora, trinta minutos, quarenta e cinco segundos. O prefixo PT é obrigatório (P = Period, T = Time). Para componentes sub-segundo, a forma é PT0.5S ou PT0.000000001S. Duration cobre tudo abaixo do dia; períodos (ano, mês, dia) pertencem 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 retorna um novo Duration (a imutabilidade que vem desde LocalDate em diante).
Os acessores de conversão:
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)Os métodos toX() retornam um único long do total naquela unidade, truncando. Os métodos getX() retornam a decomposição bruta. São coisas diferentes; confundi-los é o erro mais comum com Duration.
Duration d = Duration.ofSeconds(125);
d.toMinutes(); // 2 (125 / 60)
d.getSeconds(); // 125 (raw)Para formatação "X minutos Y segundos", use os dois:
long mins = d.toMinutes();
long secs = d.minusMinutes(mins).getSeconds();
System.out.printf("%d:%02d%n", mins, secs); // 2:05Ou, desde o Java 9, os auxiliares de decomposição 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 secondEsses são o que você precisa para "exibir 1h 23m 45s" sem precisar fazer o módulo manualmente.
Comparação
d1.isZero(); // d == 0
d1.isNegative(); // d < 0
d1.compareTo(d2);
d1.equals(d2);Duration é Comparable<Duration>. A ordenação é com sinal — uma duração negativa é menor que zero, que é menor que uma duração positiva.
Distância entre dois Temporals
A fábrica Duration.between(start, end) é a que você usará com mais frequência:
Duration d = Duration.between(start, end); // works on Instant, LocalDateTime, ZonedDateTime, LocalTimeEla aceita qualquer par de valores Temporal que suporte unidades baseadas em tempo. LocalDate (somente data) não suporta — Duration.between(date1, date2) lança DateTimeException porque uma data não tem componente de relógio. Para distância em calendário, use Period.between(date1, date2) ou ChronoUnit.DAYS.between(date1, date2).
A convenção de sinal: positivo se end for posterior a start, negativo caso contrário.
Adicionando a outros temporais
Duration é um TemporalAmount, portanto qualquer Temporal o aceita via plus/minus:
Instant later = Instant.now().plus(Duration.ofMinutes(30));
LocalDateTime then = LocalDateTime.now().plus(Duration.ofHours(8));Adicionar um Duration a um LocalDate lança exceção — pelo mesmo motivo acima, sem componente de relógio. O compilador não captura isso; a chamada falha em tempo de execução. Se você quiser fazer isso, quase certamente quis dizer Period.
Um exemplo prático: medição de tempo, formatação, backoff
O programa abaixo mede o tempo decorrido de um pequeno trecho de trabalho, exibe-o em formato legível por humanos usando os auxiliares toXxxPart, computa um cronograma de backoff exponencial e demonstra o limite entre Duration e Period, mostrando tanto Duration.between em Instants quanto Period.between em LocalDates para o mesmo intervalo de tempo conceitual.
O que observar na execução:
- O bloco
Thread.sleep(125)foi medido comoPT0.125-something-S— o estouro real dosleepé variabilidade da máquina real.Duration.between(t0, t1)foi a chamada correta: doisInstants, sem zona necessária, resposta exata com a resolução do relógio do sistema. - Os auxiliares
toXxxPart()forneceram inteiros limpos para "a parte das horas," "a parte dos minutos," "a parte dos segundos," "a parte dos milissegundos." Sem eles, você teria que fazer aritmética modulartotal / 3600e(total % 3600) / 60manualmente. Use-os sempre que precisar formatar uma duração para um humano. - O laço de backoff exponencial escalou a duração com
multipliedBy(2)e imprimiu cada resultado. A forma de string ISO-8601 (PT2S,PT4S,PT8S, ...) é o queDuration.toStringimprime; é concisa e inequívoca, ideal para logs. - O bloco "Jan 1 → Apr 1 em três formas" mostrou o limite entre
DurationePeriod.Period.between(start, end)retornouP3M— três meses de calendário.Duration.betweennos instantes UTC equivalentes retornou uma contagem de tempo real (90 dias). Ambos estão corretos; eles respondem a perguntas diferentes. Escolha o que corresponde à intenção do seu código. - O bloco final tentou
Duration.between(LocalDate, LocalDate)e obteveDateTimeException. O sistema de tipos sabe que uma data não tem componente de relógio, então se recusa a computar uma duração baseada em relógio. A correção é (1) anexar horas (startDate.atStartOfDay(zone)), ou (2) usarPeriod.betweense uma contagem de calendário é o que você realmente quis dizer. A exceção é o design correto: respostas silenciosas a essa pergunta seriam enganosas.
O que vem a seguir
Duration é o comprimento em segundos e nanossegundos. O próximo capítulo, Java Period, é o comprimento em anos, meses e dias — o equivalente em calendário. Os dois nunca se misturam: Duration não conhece meses, Period não conhece horas, e qualquer código que lide com os dois recorre a um ou outro dependendo de se a resposta deve acompanhar o calendário ou o relógio de parede.