2 pontos por GN⁺ 2025-01-31 | 1 comentários | Compartilhar no WhatsApp
  • O novo objeto Temporal do JavaScript está começando a entrar em versões experimentais de navegadores, marcando uma mudança para tratar data, hora, duração e calendários de forma mais precisa e consistente com uma API nativa
  • O Date existente dificultava o tratamento de fusos horários e a depuração por causa de sua estrutura centrada no horário local do usuário e em UTC, parsing instável e comportamento mutável
  • O Temporal separa instantes, horário local e durações com tipos como Temporal.Instant, Temporal.ZonedDateTime, Temporal.PlainDateTime, Temporal.Duration e Temporal.Now, além de oferecer mais de 200 métodos utilitários
  • Ainda será preciso mais tempo até haver suporte estável entre navegadores, mas no Firefox Nightly já é possível testar o Temporal por trás da configuração javascript.options.experimental.temporal
  • Nesta semana, a MDN adicionou mais de 270 páginas de documentação sobre Temporal, e na página de documentação do TC39 com @js-temporal/polyfill é possível executar os exemplos sem mudar flags

As antigas limitações do Date no JavaScript

  • Quando o JavaScript foi criado em 1995, o objeto Date copiou a implementação inicial de java.util.Date do Java, e embora o Java a tenha substituído em 1997, o JavaScript manteve praticamente a mesma API por quase 30 anos
  • Os maiores problemas são a falta de suporte a fusos horários e o comportamento instável de parsing
    • Só oferece suporte ao horário local do usuário e a UTC
    • O próprio Date é mutable, o que pode gerar bugs difíceis de rastrear
    • Também é difícil lidar com cálculos que atravessam o Daylight Saving Time (DST) e mudanças históricas de calendário
  • Por causa dessas limitações, muitos desenvolvedores passaram a depender de bibliotecas especializadas como Moment.js ou date-fns para tratar datas e horas em aplicações

Como o Temporal muda o modelo de data e hora

  • O Temporal foi projetado como um substituto completo para o objeto Date, com o objetivo de tornar o gerenciamento de datas e horas mais confiável e previsível
  • Ele oferece suporte à representação de fusos horários e calendários, além de métodos nativos para conversão, comparação, cálculo e formatação
  • A superfície da API tem mais de 200 métodos utilitários, e a documentação do Temporal na MDN recebeu descrições e exemplos

Principais tipos do Temporal

  • O Temporal distingue instantes únicos, horário de parede local e durações em tipos diferentes
  • As principais estruturas são as seguintes
    • Temporal.Duration: a diferença entre dois instantes
    • Temporal.Instant: um instante único representado por timestamp
    • Temporal.ZonedDateTime: data e hora com fuso horário
    • Temporal.PlainDateTime: data e hora completas sem fuso horário
      • Temporal.PlainDate: representa apenas a data
      • Temporal.PlainYearMonth: ano e mês
      • Temporal.PlainMonthDay: mês e dia
      • Temporal.PlainTime: representa apenas a hora
    • Temporal.Now: API para obter a hora atual em instâncias de várias classes ou em formatos específicos

Como usar na prática, com exemplos

  • É possível obter a data e a hora atuais como string ISO e passar fusos horários para vários métodos
    • Temporal.Now.plainDateTimeISO() retorna a data e hora atuais no fuso horário do sistema
    • Temporal.Now.plainDateTimeISO("America/New_York") retorna a data e hora atuais no fuso horário de New York
  • Também é possível trabalhar com calendários além do gregoriano, criando datas em sistemas como o hebraico, chinês e islâmico
    • O cálculo do próximo Ano-Novo Chinês usa o dia 1 de M01 no calendário chinês
    • Na saída de exemplo no momento da escrita, o próximo Ano-Novo Chinês é 1/29/2025
  • Um timestamp Unix Epoch em milissegundos pode ser transformado em instante com Temporal.Instant.fromEpochMilliseconds() e comparado com Temporal.Now.instant() para calcular o tempo restante
    • Para o timestamp de exemplo 1851222399924, o tempo a partir de agora é calculado com smallestUnit: "hour"
    • A saída de @js-temporal/polyfill é 31,600 hr, enquanto a do Firefox Nightly é PT31600H
  • A implementação do Firefox para toLocaleString atualmente não produz strings sensíveis ao locale
    • Esse comportamento parece estar mais próximo de uma decisão de design do que de uma limitação técnica, e a formatação de duration em si é possível
    • A implementação do polyfill e a do Firefox podem convergir mais tarde
  • O método compare() pode ser usado para ordenar arrays
    • Com durations.sort(Temporal.Duration.compare), a ordenação fica PT1H, PT1H30M, PT1H45M, PT2H

Suporte em navegadores e como testar

1 comentários

 
GN⁺ 2025-01-31
Opiniões do Hacker News
  • Temporal é excelente. Usei em produção por um bom tempo via polyfill, e ele resolve muitos dos problemas que eu encontrava com a API antiga Date()
    Parece que pegou inspiração de boas bibliotecas de tempo, como chrono do Rust e Joda Time do Java, e juntou tudo em uma API fácil de usar
    Ele é um pouco mais complexo por separar tempo simples, instantes e tempos com fuso horário, mas o próprio tempo é complexo, então o desenvolvedor precisa encarar essa complexidade
    Mesmo uma operação como “adicionar um dia a este timestamp” precisa necessariamente decidir se isso é em relação a um fuso horário específico; caso contrário, ficam bugs por causa do horário de verão, de mudanças no fuso horário do usuário ou de diferenças no fuso horário do servidor
    Ele também resolve a diferença de serialização entre timestamps com offset fixo (2025-01-01T00:00+02:00) e um fuso horário específico (Europe/Paris)

    • Vale a pena ver também o crate de Rust Jiff. Ele foi inspirado no Temporal e também traz uma comparação com outros crates de data e hora, incluindo chrono: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
      Pessoalmente, acho difícil dizer que o Temporal foi fortemente inspirado no chrono do Rust; parece ter sido mais influenciado por Joda, pelo java.time atual e por bibliotecas de data e hora em JavaScript como date-fns e Moment.js
    • Pessoas com certa idade aprenderam há muito tempo a não agendar tarefas cron entre 01:00 e 03:00 no horário local em ambientes de produção
    • Concordo, mas há um obstáculo. Muita gente não gosta de usar nomes de fusos horários que apontam para uma localização real
      Elas não vão procurar se Europe/Paris significa uma região ampla da França ou apenas Paris; ficam incomodadas porque parece que estão dizendo que moram em Paris
      Isso foi doloroso ao agendar coisas como eventos de comunidades online. As pessoas querem usar offsets fixos e aceitam expressões como CET/CEST, mas não gostam de algo como Europe/suascoordenadasexatas
      Sei que, na prática, a maioria dos fusos horários cobre regiões bem amplas, mas há muita gente que se preocupa com privacidade e, ainda assim, não pensa em pesquisar a diferença entre “hora” e “fuso horário”
    • chrono e Joda Time são coisas de épocas diferentes. Quando o Rust 1.0 saiu, Java já tinha implementado essa abordagem na biblioteca padrão com java.time, e não era necessária uma biblioteca de terceiros para esse uso
    • Gostaria de saber com mais detalhes qual é o problema da diferença de serialização entre timestamps com offset fixo e timestamps de um fuso horário específico
  • Demorei um bom tempo para encontrar isso na documentação, mas Temporal.ZonedDateTime.prototype.withTimeZone() converte de um fuso horário para outro: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    Se você pegar um valor criado com Temporal.ZonedDateTime.from("2021-08-01T12:00[America/New_York]") e passar para withTimeZone("Europe/Paris"), ele vira 2021-08-01T18:00:00+02:00[Europe/Paris]
    Conversão de fuso horário e tratamento de durações são recursos centrais que uma biblioteca precisa cobrir, então é bom que o Temporal trate ambos. Porém, parece que Temporal.Duration não tem uma função format personalizada para produzir algo como 1h03m, por exemplo duration.format('H\hmm\m')

  • No momento, há várias propostas amadurecendo no TC39, e Temporal é uma das que eu gostaria que fossem lançadas logo
    Sei que, para os principais navegadores darem suporte a novos recursos, é necessário esforço de desenvolvimento dos engenheiros do v8, JSC e SpiderMonkey, então sou muito grato a quem está fazendo isso avançar
    Do ponto de vista de quem está impaciente, dá vontade de torcer dizendo: “Façam logo Records e Tuples também! Podem pular a guerra da sintaxe de pipes!”

    • Records e Tuples parecem ter pouca chance de serem lançados, ou pelo menos uma chance muito baixa de serem lançados com a semântica de igualdade que as pessoas querem
      Pelo que entendo, os fornecedores de navegadores já são bastante contrários, neste momento, a adicionar novos tipos primitivos. As dificuldades sobre como a igualdade deveria funcionar são explicadas nesta thread: https://github.com/tc39/proposal-record-tuple/issues/387
      No início eu não esperava que BigInt fosse ser tão usado, mas, na prática, continuam surgindo situações em que faz falta uma coleção com igualdade estrutural
      Sei que a oposição a adicionar novos tipos primitivos também afeta pattern matching, mas não sei bem o motivo, e tenho acompanhado menos essa proposta
    • É ótimo que Temporal esteja chegando, e há muitos outros bons recursos, mas é difícil ser otimista em relação às propostas específicas mencionadas
      Pipeline, pattern matching e Records+Tuples estão todos em desenvolvimento há mais de 4 anos e ainda estão nos estágios 1 ou 2. No último ano, parece que quase não houve avanço significativo, exceto talvez em pattern matching
      Segundo issues no repositório de Records e Tuples, houve resistência à semântica de valor, isto é, a se === poderia funcionar. Sem semântica de valor, a utilidade da proposta cai bastante
      Para chegarem ao estágio 3, eles ainda levariam pelo menos mais 1 ou 2 anos, ou talvez nem cheguem lá. Ainda assim, as funções de agrupamento de arrays saíram
    • O que eu quero é pattern matching: https://tc39.es/proposal-pattern-matching/
      Infelizmente, ainda está no estágio 1, então deve levar mais alguns anos
    • Sobre Records, fico me perguntando se Dictionary era uma proposta do ECMA5 ou se era um recurso peculiar do AS3
      O Dictionary do AS3 era um tipo que usava referências de objetos, strings e números como chaves únicas e permitia associar valores de qualquer tipo. O garbage collector também tratava isso para que, enquanto o próprio objeto de dicionário estivesse vivo, as referências não fossem fracas
      Pense no Set do JavaScript, mas usando qualquer tipo de chave especificado de forma fortemente tipada e emitindo erro em tempo de compilação. Sinto falta daquela linguagem
    • Não é um problema apenas dos principais fabricantes de navegadores. Na época em que o ES6 estava prestes a sair, eu estava criando um mecanismo de inferência para JavaScript e tinha um suporte razoável a ES5, mas tive que desistir quando vieram muitas atualizações de sintaxe
      Essas atualizações basicamente quebraram o ecossistema JavaScript, e agora parece que só restaram linguagens que compilam para JS. Quem ainda usa JS vanilla parece alguém fazendo aplicativos em assembly
  • Tenho curiosidade sobre como os dados de cada fuso horário dentro do Temporal são mantidos atualizados
    Fico me perguntando se a estrutura é o TC39 atualizar os dados em algum lugar e cada navegador copiar esses dados internamente e lançar uma nova versão, e se, caso o usuário não tenha atualizado para um navegador com os novos dados, ele verá horários incorretos no meu site
    Por exemplo, o México aboliu o horário de verão em 2022: https://en.wikipedia.org/wiki/Time_in_Mexico
    Bibliotecas de terceiros como pytz ou moment-timezone podem ser atualizadas no lado do servidor, e, se isso for tratado pela biblioteca padrão, basta atualizar a versão da linguagem, mas não sei como isso funciona com Temporal e o navegador do visitante

    • Ao ler “Time Zones and Resolving Ambiguity” no documento da proposta, ele apenas diz que usa o banco de dados de fusos horários da IANA, sem explicar quando ele é atualizado: https://tc39.es/proposal-temporal/docs/timezone.html
      O computador do usuário também deve ter acesso a esse banco de dados e provavelmente lida com atualizações, mas não tenho certeza. O navegador também poderia baixar e manter esse banco de dados separadamente, e isso deve depender da implementação
      Banco de dados relacionado: https://en.wikipedia.org/wiki/Tz_database
    • Como é um recurso embutido no navegador, provavelmente será atualizado durante atualizações do navegador. Os fornecedores de navegadores têm trabalhado bastante para atualizações rápidas, e, no computador do usuário médio, o navegador provavelmente é o software que se mantém mais atualizado
    • Os navegadores já precisam manter locales atualizados por causa dos helpers de Intl, então investigar esse lado provavelmente levará a uma resposta parecida
    • Sistemas do tipo Unix, isto é, distribuições Linux, macOS e Android, têm uma cópia em nível de sistema do IANA Time Zone Database. Não sei se os navegadores usam isso ou incluem uma cópia própria, mas Temporal certamente usa o tzdb da IANA
      Parece, em geral, correto dizer que, se o usuário não atualizar o navegador ou o sistema para refletir os novos dados de fuso horário, poderá ver horários incorretos. Porém, isso não difere muito da prática comum atual
      A alternativa seria cada aplicação incluir seu próprio tzdb, e alguns apps fazem isso e têm bons motivos. Mas, em geral, é desperdício, aumenta inconsistências entre aplicações e provavelmente resulta em ciclos de atualização mais lentos
    • Vai depender da implementação e do sistema. A implementação acabará tendo que definir um provedor de fusos horários, que provavelmente será fornecido a partir de hooks de host/sistema que a implementação do engine oferece ao Temporal
  • É preciso adicionar um evento para detectar quando alguém muda de fuso horário
    À lista de “falsidades em que programadores acreditam sobre tempo” também poderia entrar “programadores acreditam que o fuso horário permanece fixo durante o uso”. Na realidade, milhões de pessoas atravessam fusos horários todos os dias

    • Esse tipo de evento provavelmente seria usado como mais um ponto de dados para fingerprinting de pessoas e para estimar sua localização
    • Não sei bem onde esse evento seria usado. Considerando o número de usuários afetados, fico em dúvida se a maioria das aplicações web que dependem em algum grau de tempo teria valor suficiente para ouvir e reagir diretamente a esse evento
      Claro, poderia ajudar bibliotecas
    • O fuso horário do cliente deveria importar apenas para o cliente. Fora a conveniência de mostrar timestamps no horário local do usuário, não há condição em que o relógio do cliente deva se tornar importante. Não há motivo para se importar se o usuário mudar de fuso horário
    • Como rastrear mudanças de fuso horário exige estado, parece mais correto que o desenvolvedor defina como rastrear e reagir. Talvez você não queira que o Temporal tenha uma opinião sobre como lidar com isso
    • Se você estiver em um avião ou na ISS, pode até estar em um estado sem fuso horário algum
  • É uma pena que .Now não seja uma chamada de função. Não deveria ser uma propriedade
    Se .Now for avaliado várias vezes dentro de um loop, pode retornar um valor diferente a cada vez, o que é inesperado
    C# fez a mesma escolha e não deveria ter feito: https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...

    • Acho que você entendeu errado. Now é um namespace para funções que obtêm valores reais, por exemplo Now.instant()
  • Gosto muito da proposta do Temporal, mas uma coisa me incomoda. Ela depende de igualdade por referência nas comparações
    Ou seja, Temporal.Instant.from('2020-01-01') != Temporal.Instant.from('2020-01-01')
    Isso não é ruim por si só, mas na prática elimina a capacidade de usar esses objetos como chaves de Map ou de reuni-los em um Set. Entendo por que tomaram essa decisão, mas é uma pena que não seja possível
    Talvez, se Records e Tuples realmente surgirem algum dia, apareça uma versão baseada neles

    • Existe algum tipo não primitivo em JavaScript que tenha igualdade não referencial com ==?
    • Dependendo do caso, parece possível usar .epochMilliseconds como chave ou elemento em Map/Set. Mas se, no Map, você precisar preservar dados de fuso horário ou fazer operações adicionais com Instant, será necessário manter o Instant original no valor
      No caso de Set, dá para usar algo como Map{v.epochMilliseconds:v} para preservar o Instant
      Não é ótimo, mas esse é um problema a ser atribuído ao próprio JavaScript, mais do que ao Temporal. O runtime poderia cuidar disso, mas aí o polyfill falharia
    • Mesmo que fosse possível, usar valores assim em Map/Set é parecido com usar números de ponto flutuante em Map/Set, e geralmente é uma má ideia
    • Não é porque isso é impossível em JavaScript? A não ser por algum hack estranho, como o pool de strings constantes do Java, em que todo objeto usado vira uma referência a um valor dentro de um pool global de objetos
    • Não bastaria usar String() ou '2020-01-01'?
  • Fornecer uma função de comparação que funciona com APIs existentes é um design sólido. Poderia até ser requisito obrigatório ao adicionar novos tipos
    Por exemplo, ao passar Temporal.Duration.compare para sort, dá para ordenar como PT1H, PT1H30M, PT1H45M, PT2H. Palmas para os designers

  • Entendo que o nome Temporal tente evitar conflitos com objetos de tempo comuns como Moment e Datetime, mas o nome é ruim
    Quando vi pela primeira vez, achei que fosse algo como controle de garbage collection

    • temporal significa algo relativo ao tempo em contraste com a eternidade, relativo ao tempo verbal ou a distinções de tempo na gramática, ou relativo ao tempo em oposição ao espaço: https://www.merriam-webster.com/dictionary/temporal
      Parece um bom nome
    • Achei que fosse o motor de workflow Temporal dentro do navegador
    • É uma discussão de cor de bicicletário sobre uma decisão já tomada, mas concordo que é um nome realmente ruim
      Para começar, Temporal não é um substantivo, é um adjetivo. Tem relação com tempo, mas não encaixa intuitivamente
      Mais importante: quando se escolhe um nome estranho para reduzir a chance de conflito com código antigo, a linguagem fica mais opaca com o passar do tempo
      Quando Promise e Generator foram adicionados, também havia muitas bibliotecas usando esses nomes de objetos. Ao contrário de palavras reservadas, objetos embutidos podem ser sobrescritos, então isso não foi um grande problema
      Acho que, pela saúde da linguagem no longo prazo, o comitê de padronização deveria ter insistido em Time
      Claro, essa discussão já passou, e os fabricantes de navegadores e implementadores de engines JS devem ter chegado a um acordo
    • Também acho um nome horrível. DateV2 seria melhor
      Entendo que compatibilidade retroativa seja necessária, mas Temporal soa ruim. DateTime nem é um nome existente no padrão JS; não sei por que não usaram isso. Até lá, acho que vou usar const DateTime = Temporal
  • Posts relacionados:
    JavaScript Temporal Is Coming - https://news.ycombinator.com/item?id=42809834 - janeiro de 2025, 18 comentários
    Mozilla: Temporal (Limited Availability) - https://news.ycombinator.com/item?id=42776548 - janeiro de 2025, 1 comentário
    Is It Time for the JavaScript Temporal API? - https://news.ycombinator.com/item?id=29712118 - dezembro de 2021, 100 comentários
    Temporal: Getting started with JavaScript's new date time API - https://news.ycombinator.com/item?id=27661667 - junho de 2021, 194 comentários
    JavaScript Temporal - https://news.ycombinator.com/item?id=27395236 - junho de 2021, 1 comentário
    JavaScript Proposal Temporal for Dates is now stage 3 - https://news.ycombinator.com/item?id=26432346 - março de 2021, 1 comentário

    • É ótimo que as Web APIs estejam realmente excelentes hoje em dia, mas ainda é uma pena que a primeira submissão sobre este tema tenha sido em março de 2021, quase 4 anos atrás
      A frase de que navios grandes demoram para mudar de direção se encaixa perfeitamente