Temporal API no JavaScript: Finalmente livre do pesadelo do Date
Se você já trabalhou com datas em JavaScript, sabe do que estou falando.
A confusão entre UTC e hora local. Os meses que começam em zero (Janeiro é 0, acredita?). O objeto Date que pode ser modificado de qualquer lugar do código. O pesadelo de converter fuso horário. E a necessidade de depender de moment.js ou date-fns pra fazer coisas básicas que deveriam vir no próprio JavaScript.
O Date tá quebrado desde 1995. Quase 30 anos aguentando esses problemas. Mas finalmente, a solução chegou!
Temporal API é uma nova API moderna de datas que está em desenvolvimento desde 2017 e já chegou ao Stage 3 do TC39. Chrome, Firefox e Safari estão implementando. Até meados de 2025, vai estar disponível em todo lugar.
Nesse artigo, vamos explorar tudo sobre Temporal: por que ela é necessária, como usar, e como migrar do moment.js.
Por que o Date é tão ruim assim?
Antes de entrar no Temporal, vamos entender por que precisamos de um substituto. O Date não é só inconveniente - ele tem problemas estruturais que causam bugs em produção.
Problema 1: É mutável (e isso é péssimo)
```javascript
const meeting = new Date('2025-01-15T10:00:00');
scheduleMeeting(meeting);
// Em algum outro lugar...
meeting.setHours(meeting.getHours() + 2);
// Eita! O original mudou
```
Depois que você passa um Date pra algum lugar, nunca sabe se alguém modificou ele.
Problema 2: Meses começam em 0 (o clássico)
```javascript
// Que mês é esse?
const date = new Date(2025, 1, 14);
console.log(date.toISOString()); // 2025-02-14 ← É fevereiro!
// Pra janeiro tem que usar 0
const january = new Date(2025, 0, 15);
```
Queria 25 de dezembro? new Date(2025, 12, 25) te dá 25 de janeiro de 2026. Todo mundo já caiu nessa.
Problema 3: O caos dos fusos horários
```javascript
const date = new Date('2025-01-15T10:00:00');
console.log(date.getHours()); // Depende do seu fuso horário!
```
Ele guarda um timestamp absoluto mas mostra na hora local. Não dá pra representar "10h da manhã em Tóquio" sem fazer conversões manuais.
Problema 4: A matemática não funciona direito
```javascript
// 31 de janeiro + 1 mês = ?
const jan31 = new Date(2025, 0, 31);
jan31.setMonth(jan31.getMonth() + 1);
console.log(jan31.toDateString()); // "Mon Mar 03 2025" ← Oi?!
```
Fevereiro não tem 31 dias, então pula pro dia 3 de março. 😱
Temporal: Design do zero
O Temporal resolve todos esses problemas com um design completamente novo.
Princípios
- Imutável: Uma vez criado, não muda. Operações retornam objetos novos.
- Tipos claros: Tipos diferentes pra usos diferentes (data, hora, fuso, duração).
- Sem surpresas: Meses começam em 1. Parsing é rigoroso.
- Fusos nativos: Suporte integrado pro banco de dados IANA.
- Feito pra humanos: APIs que funcionam como você espera.
Os tipos
```javascript
Temporal.PlainDate // Só data (2025-01-15)
Temporal.PlainTime // Só hora (10:30:00)
Temporal.PlainDateTime // Data + hora (2025-01-15T10:30:00)
Temporal.ZonedDateTime // Data + hora + fuso (2025-01-15T10:30:00[America/Sao_Paulo])
Temporal.Instant // Ponto exato no tempo (como Date mas imutável)
Temporal.Duration // Duração (2 horas, 30 minutos)
Temporal.PlainYearMonth // Ano e mês (2025-01)
Temporal.PlainMonthDay // Mês e dia (01-15, pra aniversários)
```
Só de ver o nome do tipo você já sabe o que esperar.
Parte 1: Datas (Temporal.PlainDate)
Pra quando você só precisa da data, sem hora nem fuso. Perfeito pra aniversários, feriados, prazos.
Criar datas
```javascript
// Por partes (meses começam em 1!)
const date1 = Temporal.PlainDate.from({ year: 2025, month: 1, day: 15 });
console.log(date1.toString()); // "2025-01-15"
// De string
const date2 = Temporal.PlainDate.from('2025-01-15');
// Hoje
const today = Temporal.Now.plainDateISO();
```
Pegar informações
```javascript
const date = Temporal.PlainDate.from('2025-06-15');
console.log(date.year); // 2025
console.log(date.month); // 6 (junho de verdade!)
console.log(date.day); // 15
console.log(date.dayOfWeek); // 7 (domingo. segunda=1)
console.log(date.daysInMonth); // 30
```
Matemática (finalmente funciona!)
```javascript
const date = Temporal.PlainDate.from('2025-01-31');
// Somar 1 mês - Temporal resolve
const nextMonth = date.add({ months: 1 });
console.log(nextMonth.toString()); // "2025-02-28" ← último dia de fevereiro
// Somar 2 semanas e 3 dias
const later = date.add({ weeks: 2, days: 3 });
console.log(later.toString()); // "2025-02-17"
// Encadear operações
const result = date
.add({ months: 6 })
.add({ days: 15 })
.subtract({ weeks: 1 });
```
Comparar e calcular diferenças
```javascript
const date1 = Temporal.PlainDate.from('2025-01-15');
const date2 = Temporal.PlainDate.from('2025-03-20');
const diff = date2.since(date1);
console.log(diff.days); // 64
const diffMonths = date2.since(date1, { largestUnit: 'month' });
console.log(diffMonths.toString()); // "P2M5D" (2 meses e 5 dias)
```
Parte 2: Horas (Temporal.PlainTime)
A hora que aparece no relógio, sem data nem fuso.
```javascript
const time = Temporal.PlainTime.from({ hour: 10, minute: 30 });
console.log(time.toString()); // "10:30:00"
// Operações
const later = time.add({ hours: 2, minutes: 45 });
console.log(later.toString()); // "13:15:00"
// Se passar da meia-noite, dá a volta
const pastMidnight = time.add({ hours: 16 });
console.log(pastMidnight.toString()); // "02:30:00"
```
Parte 3: Data + Hora (Temporal.PlainDateTime)
Pra quando você precisa dos dois mas não liga pro fuso horário.
```javascript
const dt = Temporal.PlainDateTime.from({
year: 2025, month: 1, day: 15,
hour: 10, minute: 30
});
console.log(dt.toString()); // "2025-01-15T10:30:00"
// Juntar data e hora separadas
const date = Temporal.PlainDate.from('2025-01-15');
const time = Temporal.PlainTime.from('10:30:00');
const combined = date.toPlainDateTime(time);
```
Parte 4: A estrela! Fusos Horários (Temporal.ZonedDateTime)
É aqui que o Temporal brilha. ZonedDateTime representa "10:30 em São Paulo" ou "15h em Lisboa", com tratamento automático do horário de verão.
Criar
```javascript
const zdt = Temporal.ZonedDateTime.from({
year: 2025, month: 1, day: 15,
hour: 10, minute: 30,
timeZone: 'America/Sao_Paulo'
});
console.log(zdt.toString());
// "2025-01-15T10:30:00-03:00[America/Sao_Paulo]"
// Hora atual em outro fuso
const lisboaNow = Temporal.Now.zonedDateTimeISO('Europe/Lisbon');
```
Converter entre fusos
```javascript
const spTime = Temporal.ZonedDateTime.from(
'2025-01-15T10:30:00[America/Sao_Paulo]'
);
// Converter pra Lisboa
const lisboaTime = spTime.withTimeZone('Europe/Lisbon');
console.log(lisboaTime.toString());
// "2025-01-15T13:30:00+00:00[Europe/Lisbon]"
// Converter pra UTC
const utc = spTime.withTimeZone('UTC');
```
Horário de Verão
```javascript
// Nos EUA, em março os relógios adiantam 1 hora
const beforeDST = Temporal.ZonedDateTime.from(
'2025-03-09T01:30:00[America/New_York]'
);
const afterDST = beforeDST.add({ hours: 1 });
console.log(afterDST.toString());
// "2025-03-09T03:30:00-04:00[America/New_York]"
// 1:30 + 1 hora = 3:30 (2:30 não existe nesse dia)
```
Parte 5: Durações (Temporal.Duration)
```javascript
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
console.log(duration.toString()); // "PT2H30M"
// Somar durações
const d1 = Temporal.Duration.from({ hours: 1, minutes: 30 });
const d2 = Temporal.Duration.from({ hours: 2, minutes: 45 });
console.log(d1.add(d2).toString()); // "PT4H15M"
// Converter 150 minutos pra horas
const mins = Temporal.Duration.from({ minutes: 150 });
const balanced = mins.round({ largestUnit: 'hour' });
console.log(balanced.toString()); // "PT2H30M"
```
Exemplos práticos
```javascript
// Calcular idade
const birthdate = Temporal.PlainDate.from('1990-05-15');
const today = Temporal.Now.plainDateISO();
const age = today.since(birthdate, { largestUnit: 'year' });
console.log(`${age.years} anos, ${age.months} meses`);
// Contagem regressiva
const deadline = Temporal.PlainDate.from('2025-12-31');
const remaining = deadline.since(today);
console.log(`Faltam ${remaining.days} dias`);
```
Parte 6: Exemplos reais
Coordenar reuniões globais
```javascript
function scheduleMeeting(dateTime, hostTz, attendeeTzs) {
const meeting = Temporal.ZonedDateTime.from(`${dateTime}[${hostTz}]`);
const schedule = new Map();
schedule.set(hostTz, meeting.toString());
for (const tz of attendeeTzs) {
schedule.set(tz, meeting.withTimeZone(tz).toString());
}
return schedule;
}
// Se a reunião é às 9h em São Paulo,
// que horas são em Lisboa e Nova York?
scheduleMeeting('2025-01-20T09:00:00', 'America/Sao_Paulo',
['Europe/Lisbon', 'America/New_York']);
```
Dias úteis
```javascript
function addBusinessDays(start, days) {
let current = start;
let remaining = days;
while (remaining > 0) {
current = current.add({ days: 1 });
if (current.dayOfWeek < 6) { // Pular fim de semana
remaining--;
}
}
return current;
}
```
Parte 7: Migrando do moment.js
Comparação de código
```javascript
// moment.js
const m = moment('2025-01-15');
m.add(1, 'month'); // Cuidado! Modifica o original
// Temporal
const t = Temporal.PlainDate.from('2025-01-15');
const next = t.add({ months: 1 }); // Original NÃO muda
// moment-timezone
moment.tz('2025-01-15 10:30', 'America/Sao_Paulo');
// Temporal
Temporal.ZonedDateTime.from('2025-01-15T10:30:00[America/Sao_Paulo]');
```
Estratégia de migração
- Código novo → usa Temporal desde o começo
- Funções adaptadoras → pra conectar código antigo e novo
- Ir trocando → começando pelo código mais problemático
- Remover dependências → quando tudo estiver migrado
```javascript
// Funções pra converter
function dateToTemporal(date) {
return Temporal.Instant.fromEpochMilliseconds(date.getTime());
}
function temporalToDate(instant) {
return new Date(instant.epochMilliseconds);
}
```
Parte 8: Como usar hoje?
No final de 2024, ainda não tá nos navegadores por padrão.
| Ambiente | Status |
|---|---|
| Chrome | Disponível com flag |
| Firefox | Em desenvolvimento |
| Safari | Em desenvolvimento |
| Node.js | Disponível com flag |
Com polyfill funciona já
```bash
npm install @js-temporal/polyfill
```
```javascript
import { Temporal } from '@js-temporal/polyfill';
const today = Temporal.Now.plainDateISO();
```
Detectar suporte nativo
```javascript
async function getTemporal() {
if (typeof globalThis.Temporal !== 'undefined') {
return globalThis.Temporal;
}
const { Temporal } = await import('@js-temporal/polyfill');
return Temporal;
}
```
Conclusão
Temporal é a maior melhoria no tratamento de datas em toda a história do JavaScript.
Por que é tão bom?
- Imutável → sem mudanças acidentais
- Tipos claros → PlainDate, ZonedDateTime... você sabe o que é cada coisa
- Faz sentido → meses a partir de 1, nomes de métodos lógicos
- Fusos de primeira → horário de verão incluído
- Matemática correta → finalmente funciona como esperado
Próximos passos:
- Testa o polyfill hoje mesmo
- Usa Temporal em projetos novos
- Planeja abandonar o moment.js
- Acompanha o progresso nos navegadores
O pesadelo do Date tá acabando. Temporal chegou! 🚀
Explore ferramentas relacionadas
Experimente estas ferramentas gratuitas do Pockit