Português

Um guia completo para usar a API Temporal do JavaScript para cálculos de intervalos de tempo precisos e intuitivos, cobrindo tudo, desde a criação básica de durações até aritmética avançada e formatação.

Duração Temporal do JavaScript: Dominando Cálculos de Intervalos de Tempo

A API Temporal do JavaScript introduz uma forma moderna e poderosa de lidar com datas, horas e intervalos de tempo. O objeto Temporal.Duration representa um período de tempo, fornecendo uma abordagem clara e intuitiva para realizar cálculos com intervalos de tempo. Este artigo aprofunda-se nos detalhes de Temporal.Duration, demonstrando como criar, manipular e formatar durações para vários casos de uso.

O que é Temporal.Duration?

Temporal.Duration representa um período de tempo, expressando-o em termos de anos, meses, dias, horas, minutos, segundos e frações de segundo (milissegundos, microssegundos, nanossegundos). Ao contrário dos objetos Date que representam um ponto específico no tempo, Temporal.Duration representa uma quantidade de tempo. Ele adere ao formato de duração ISO 8601 (por exemplo, P1Y2M10DT2H30M representa 1 ano, 2 meses, 10 dias, 2 horas e 30 minutos). A API Temporal foi projetada para ser mais intuitiva e menos propensa a erros do que o objeto legado Date.

Criando Objetos Temporal.Duration

Existem várias maneiras de criar objetos Temporal.Duration:

1. A partir de um Objeto Simples

Você pode criar uma duração passando um objeto com as propriedades desejadas:

const duration = new Temporal.Duration(1, 2, 10, 2, 30, 0, 0, 0);
console.log(duration.toString()); // Saída: P1Y2M10DT2H30M

Isso cria uma duração de 1 ano, 2 meses, 10 dias, 2 horas e 30 minutos. Note que os argumentos correspondem à seguinte ordem: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds.

2. A partir de uma String ISO 8601

Você também pode criar uma duração a partir de uma string de duração ISO 8601 usando Temporal.Duration.from():

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.toString()); // Saída: P1Y2M10DT2H30M

Isso é particularmente útil ao lidar com durações armazenadas em formato de string ou recebidas de uma fonte externa.

3. Usando os métodos add() e subtract() com Temporal.Instant, Temporal.ZonedDateTime, etc.

Quando você adiciona ou subtrai Temporal.Duration de outros tipos Temporais (como Temporal.Instant ou Temporal.ZonedDateTime), um Temporal.Duration é retornado representando a diferença entre os dois pontos no tempo, se você então os subtrair. Por exemplo:

const now = Temporal.Now.zonedDateTimeISO();
const later = now.add({ hours: 5 });
const duration = later.since(now);
console.log(duration.toString()); // Saída: PT5H

Acessando Componentes da Duração

Você pode acessar os componentes individuais de um objeto Temporal.Duration usando suas propriedades:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.years);      // Saída: 1
console.log(duration.months);     // Saída: 2
console.log(duration.days);       // Saída: 10
console.log(duration.hours);      // Saída: 2
console.log(duration.minutes);     // Saída: 30
console.log(duration.seconds);     // Saída: 0
console.log(duration.milliseconds); // Saída: 0
console.log(duration.microseconds); // Saída: 0
console.log(duration.nanoseconds);  // Saída: 0

Realizando Aritmética com Durações

Objetos Temporal.Duration suportam adição e subtração usando os métodos add() e subtract(). Esses métodos retornam um novo objeto Temporal.Duration representando o resultado da operação.

const duration1 = Temporal.Duration.from("P1Y2M");
const duration2 = Temporal.Duration.from("P3M4D");

const addedDuration = duration1.add(duration2);
console.log(addedDuration.toString()); // Saída: P1Y5M4D

const subtractedDuration = duration1.subtract(duration2);
console.log(subtractedDuration.toString()); // Saída: P10M26D

Você também pode encadear esses métodos para cálculos mais complexos:

const duration = Temporal.Duration.from("P1D").add({ hours: 12 }).subtract({ minutes: 30 });
console.log(duration.toString()); // Saída: P1DT11H30M

O método negated() retorna um novo objeto Temporal.Duration com todos os componentes negados:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const negatedDuration = duration.negated();
console.log(negatedDuration.toString()); // Saída: -P1Y2M10DT2H30M

O método abs() retorna um novo objeto Temporal.Duration com todos os componentes como valores positivos (valores absolutos):

const duration = Temporal.Duration.from("-P1Y2M10DT2H30M");
const absoluteDuration = duration.abs();
console.log(absoluteDuration.toString()); // Saída: P1Y2M10DT2H30M

O método with() permite que você crie uma nova instância Temporal.Duration com algumas, ou todas, as propriedades alteradas para novos valores. Se um valor não for especificado no objeto de argumento, então o valor original da duração será usado. Por exemplo:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const newDuration = duration.with({ years: 2, days: 5 });
console.log(newDuration.toString()); // Saída: P2Y2M5DT2H30M

Normalizando Durações

As durações às vezes podem ser expressas de forma não normalizada (por exemplo, P1Y12M, que poderia ser simplificado para P2Y). O método normalized() tenta simplificar uma duração para sua forma mais compacta. No entanto, ele requer uma data de referência para lidar com as complexidades das durações variáveis dos meses. Para normalizar corretamente, você precisará de uma instância Temporal.PlainDate, Temporal.ZonedDateTime ou Temporal.Instant.

Por exemplo, normalizar uma duração envolvendo meses e dias requer uma data de referência:

const duration = Temporal.Duration.from("P1M32D");
const referenceDate = Temporal.PlainDate.from("2024-01-01");
const normalizedDuration = duration.normalized({ relativeTo: referenceDate });
console.log(normalizedDuration.toString()); // Saída: P2M1D

Neste exemplo, a duração P1M32D é normalizada em relação a 1º de janeiro de 2024, resultando em P2M1D porque janeiro tem 31 dias.

Se você estiver lidando apenas com componentes de tempo (horas, minutos, segundos, etc.), pode normalizar sem uma data de referência:

const duration = Temporal.Duration.from("PT25H61M");
const normalizedDuration = duration.normalized({ relativeTo: null }); //ou omita o argumento relativeTo
console.log(normalizedDuration.toString()); // Saída: P1DT2H1M

Comparando Durações

Você pode comparar durações usando o método compare(). Este método retorna:

const duration1 = Temporal.Duration.from("P1Y");
const duration2 = Temporal.Duration.from("P6M");

const comparisonResult = Temporal.Duration.compare(duration1, duration2);
console.log(comparisonResult); // Saída: 1

Exemplos Práticos

1. Calculando o Tempo Até um Evento

Suponha que você queira calcular o tempo restante até um evento específico. Usando Temporal.Now.zonedDateTimeISO() para obter a hora atual e subtraindo a data do evento. Se a data do evento já passou, a saída será negativa.

const eventDate = Temporal.ZonedDateTime.from({ timeZone: 'America/Los_Angeles', year: 2024, month: 12, day: 25, hour: 9, minute: 0, second: 0 });
const now = Temporal.Now.zonedDateTimeISO('America/Los_Angeles');

const durationUntilEvent = eventDate.since(now);

console.log(durationUntilEvent.toString()); // Saída: ex., P262DT14H30M (dependendo da data e hora atuais)

2. Rastreando a Duração de Tarefas de Projeto

No gerenciamento de projetos, você pode usar Temporal.Duration para rastrear a duração estimada ou real das tarefas.

const task1EstimatedDuration = Temporal.Duration.from("PT8H"); // 8 horas
const task2EstimatedDuration = Temporal.Duration.from("PT16H"); // 16 horas

const totalEstimatedDuration = task1EstimatedDuration.add(task2EstimatedDuration);
console.log(`Duração total estimada: ${totalEstimatedDuration.toString()}`); // Saída: Duração total estimada: P1DT

3. Calculando a Idade

Embora o cálculo preciso da idade exija a consideração de anos bissextos e fusos horários, Temporal.Duration pode fornecer uma estimativa razoável:

const birthDate = Temporal.PlainDate.from("1990-05-15");
const currentDate = Temporal.PlainDate.from("2024-01-20");

const ageDuration = currentDate.since(birthDate, { smallestUnit: 'years' });
console.log(`Idade estimada: ${ageDuration.years} anos`); // Saída: Idade estimada: 33 anos

4. Exibindo Durações Legíveis por Humanos

Muitas vezes, você precisa exibir durações em um formato legível por humanos. Embora Temporal.Duration não tenha funções de formatação integradas, você pode criar uma lógica de formatação personalizada:

function formatDuration(duration) {
  const parts = [];
  if (duration.years) parts.push(`${duration.years} ano${duration.years > 1 ? 's' : ''}`);
  if (duration.months) parts.push(`${duration.months} ${duration.months > 1 ? 'meses' : 'mês'}`);
  if (duration.days) parts.push(`${duration.days} dia${duration.days > 1 ? 's' : ''}`);
  if (duration.hours) parts.push(`${duration.hours} hora${duration.hours > 1 ? 's' : ''}`);
  if (duration.minutes) parts.push(`${duration.minutes} minuto${duration.minutes > 1 ? 's' : ''}`);
  if (duration.seconds) parts.push(`${duration.seconds} segundo${duration.seconds > 1 ? 's' : ''}`);

  return parts.join(', ');
}

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const formattedDuration = formatDuration(duration);
console.log(formattedDuration); // Saída: 1 ano, 2 meses, 10 dias, 2 horas, 30 minutos

Uso Avançado e Considerações

1. Manipulação de Fusos Horários

Ao lidar com intervalos de tempo que cruzam limites de fusos horários ou transições de horário de verão, é crucial usar Temporal.ZonedDateTime para garantir cálculos precisos. Usar Temporal.PlainDate e Temporal.PlainTime evitará quaisquer conversões de fuso horário.

2. Menor Unidade e Arredondamento

Os métodos `since()` e `until()` geralmente aceitam opções para definir a menor unidade para a duração resultante. Por exemplo, calcular o tempo *até* um evento e limitar os resultados a dias.

const eventDate = Temporal.PlainDate.from("2024-12-25");
const now = Temporal.PlainDate.from("2024-01-20");

const durationUntilEvent = now.until(eventDate, { smallestUnit: 'days' });

console.log(durationUntilEvent.toString()); //exemplo de saída PT340D

3. Segundos Bissextos

Temporal não leva em conta segundos bissextos nativamente. Se você precisar de extrema precisão, precisará lidar com os segundos bissextos separadamente.

4. Fusos Horários IANA

A API Temporal depende do banco de dados de fusos horários da IANA (Internet Assigned Numbers Authority). Certifique-se de que seu ambiente tenha uma versão atualizada do banco de dados IANA para lidar com precisão as conversões de fuso horário.

Melhores Práticas

Armadilhas Comuns

Casos de Uso do Mundo Real em Diferentes Culturas

A API Temporal pode ser particularmente benéfica em aplicações globais onde as diferenças de fuso horário e as nuances culturais são significativas. Aqui estão alguns exemplos:

Conclusão

Temporal.Duration fornece uma maneira robusta e intuitiva de trabalhar com intervalos de tempo em JavaScript. Ao entender suas características e melhores práticas, você pode realizar com confiança cálculos de duração precisos e confiáveis em suas aplicações. Adotar a API Temporal leva a um código mais limpo e de fácil manutenção e reduz o risco de erros associados ao tratamento legado de data e hora.

À medida que você se aprofunda na API Temporal, lembre-se de consultar a documentação oficial и experimentar diferentes cenários para entender completamente suas capacidades. Com seu design moderno e recursos abrangentes, a Temporal está pronta para revolucionar a forma como lidamos com datas, horas e durações em JavaScript.