A API Temporal do JavaScript: um substituto moderno de Date
O JavaScript Date está estragado de formas bem conhecidas há décadas. O TC39 Temporal — Stage 4 desde o início de 2025, já nos navegadores modernos e no Node.js — separa de forma limpa os instantes, as datas de relógio de parede e as datas-hora com fuso. Este guia explica os novos tipos, como se relacionam com os timestamps Unix e como migrar.
Temporal vs Date num relance
O Date embutido do JavaScript é uma fonte conhecida de bugs há décadas: é mutável, só entende o fuso local e o UTC, os seus meses são indexados a zero e o seu parsing está vagamente especificado. O Temporal é o substituto normalizado — Stage 4 e parte do ECMAScript 2026 — e corrige cada um desses problemas com tipos imutáveis feitos de propósito. A tabela abaixo é o contraste geral; o resto deste guia cobre os tipos e como se relacionam com os timestamps Unix.
| Aspeto | Date (legado) | Temporal |
|---|---|---|
| Mutabilidade | Mutável | Imutável |
| Precisão | Milissegundos | Nanossegundos |
| Fusos horários | Só local + UTC | Qualquer fuso IANA (ZonedDateTime) |
| Indexação dos meses | Base 0 (0 = janeiro) | Base 1 (1 = janeiro) |
| Parsing | Permissivo, definido pela implementação | ISO 8601 / RFC 9557 estrito |
| Instante vs relógio de parede | Confundidos num só tipo | Tipos separados |
Porque existe o Temporal
O JavaScript Date confunde vários conceitos distintos: um instante exato, uma hora de relógio de parede e uma data-hora com fuso. O Date também tem peculiaridades de parsing bem conhecidas, um único fuso (o do utilizador) e precisão de milissegundos. O Temporal expõe os conceitos subjacentes como tipos separados e suporta precisão de nanossegundos, fusos IANA arbitrários e sistemas de calendário explícitos.
- Stage 4 desde o início de 2025; lançado primeiro por omissão no Firefox 139
- Agora disponível no Chrome 144+, Firefox 139+ e Node.js 26+
- Os polyfills (@js-temporal/polyfill) funcionam em qualquer ambiente moderno
- Concebido para coexistir com o Date — existem métodos de interoperabilidade nos dois sentidos
Os tipos essenciais do Temporal
Um pequeno conjunto de tipos cobre de forma limpa o problema das datas-hora. Cada um tem uma forma de cadeia canónica (ISO 8601, estendida pela RFC 9557 para o tipo com fuso):
| Tipo | Representa | Cadeia de exemplo |
|---|---|---|
| Temporal.Instant | Um instante exato, como um timestamp Unix (precisão ns) | 2026-01-01T00:00:00Z |
| Temporal.ZonedDateTime | Instante + fuso IANA + calendário | 2026-01-01T09:00:00+09:00[Asia/Tokyo] |
| Temporal.PlainDateTime | Data + hora, sem fuso | 2026-01-01T09:00:00 |
| Temporal.PlainDate | Uma data de calendário (p. ex. um aniversário) | 2026-01-01 |
| Temporal.PlainTime | Uma hora de relógio de parede | 09:00:00 |
| Temporal.Duration | Uma duração, ciente das unidades | P3DT4H10M |
Trabalhar com timestamps Unix
O Temporal.Instant é o tipo timestamp Unix. A API publicada só expõe conversões epoch em milissegundos e nanossegundos — os métodos anteriores fromEpochSeconds e fromEpochMicroseconds foram removidos antes do Stage 4 — por isso converte segundos Unix multiplicando por 1000. Usa Instant para armazenamento e transporte, e ZonedDateTime quando precisares de uma vista de relógio de parede num fuso específico.
- Temporal.Now.instant() — o momento exato atual
- Temporal.Instant.fromEpochMilliseconds(Date.now()) — a partir de um timestamp JS em milissegundos
- Temporal.Instant.fromEpochMilliseconds(1700000000 * 1000) — a partir de segundos Unix (× 1000)
- Temporal.Instant.fromEpochNanoseconds(1700000000000000000n) — a partir de nanossegundos (BigInt)
- instant.epochMilliseconds e instant.epochNanoseconds — reler o valor epoch
- instant.toZonedDateTimeISO("America/New_York") — uma vista de relógio de parede num fuso
Fusos e DST tratados corretamente
O ZonedDateTime transporta um fuso IANA, por isso as transições de DST, as horas de buraco e as horas de sobreposição são todas tratadas explicitamente. A opção de desambiguação deixa-te escolher uma política para horas locais ambíguas, e o formato de cadeia canónico é a RFC 9557 (um timestamp ISO 8601 com o fuso entre parênteses retos).
- zdt.timeZoneId — o nome IANA (p. ex. America/Los_Angeles)
- zdt.add({ hours: 1 }) — aritmética ciente do DST em termos de relógio de parede
- zdt.until(zdt2) — distância entre duas datas-hora com fuso
- Opções de desambiguação: "earlier", "later", "compatible" (por omissão — posterior para o buraco, anterior para a sobreposição), "reject"
- Ida e volta: instante ↔ data-hora com fuso é sem perdas
Padrões de migração a partir de Date
A estratégia de migração mais simples é introduzir o Temporal primeiro nas fronteiras (parsing da entrada, formatação da saída) e mover gradualmente a lógica interna.
- Date → Instant: Temporal.Instant.fromEpochMilliseconds(date.getTime())
- Instant → Date: new Date(instant.epochMilliseconds)
- Para código novo, prefere o Temporal em todo o lado e converte para Date só nas bordas da API
- O date-fns e o Luxon têm ambos modos / adaptadores cientes do Temporal
- JSON: Temporal.Instant.prototype.toJSON devolve uma cadeia ISO 8601
Suporte em navegadores e runtimes
O Temporal foi lançado em 2025 e o suporte nativo já é amplo, mas ainda não universal. O polyfill é o caminho mais seguro enquanto os navegadores antigos e o Safari estável ainda estiverem em serviço.
- Firefox 139+: o primeiro a lançá-lo, ativado por omissão (maio de 2025)
- Chrome 144+: ativado por omissão (janeiro de 2026)
- Node.js 26+: ativado por omissão (maio de 2026)
- Safari: disponível em Technology Preview, ainda não numa versão estável
- Polyfill: @js-temporal/polyfill ou temporal-polyfill — mesma API, qualquer ambiente moderno
Referências relacionadas
FAQ
- O Temporal é um substituto de Date?
- Sim — o Temporal foi concebido para substituir totalmente o Date em código novo. O Date permanece na linguagem por compatibilidade retroativa, e os dois interoperam via milissegundos epoch.
- Como converto um timestamp Unix para Temporal?
- Usa Temporal.Instant.fromEpochMilliseconds(). Para segundos Unix, multiplica por 1000: Temporal.Instant.fromEpochMilliseconds(seconds * 1000). A API publicada só tem fromEpochMilliseconds e fromEpochNanoseconds — o método mais antigo fromEpochSeconds foi removido antes de o Temporal atingir o Stage 4.
- O Temporal suporta nanossegundos?
- Sim. O Temporal.Instant tem precisão de nanossegundos internamente e expõe epochNanoseconds como um BigInt.
- Posso usar o Temporal no Node.js hoje?
- No Node.js 26+ o Temporal está ativado por omissão. Em versões anteriores do Node, instala @js-temporal/polyfill — a superfície da API é idêntica.
- Que navegadores suportam o Temporal?
- O Firefox 139+ (maio de 2025), o Chrome 144+ (janeiro de 2026) e o Node.js 26+ (maio de 2026) incluem o Temporal por omissão. O Safari tem-no em Technology Preview. Para algo mais antigo, usa o @js-temporal/polyfill.
- Como é que o Temporal trata o buraco do DST?
- Os construtores e a aritmética de ZonedDateTime aceitam uma opção de desambiguação. "compatible" (por omissão) escolhe o instante posterior para os buracos e o anterior para as sobreposições; "reject" lança um erro se a hora local for ambígua.