Back

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

  1. Imutável: Uma vez criado, não muda. Operações retornam objetos novos.
  2. Tipos claros: Tipos diferentes pra usos diferentes (data, hora, fuso, duração).
  3. Sem surpresas: Meses começam em 1. Parsing é rigoroso.
  4. Fusos nativos: Suporte integrado pro banco de dados IANA.
  5. 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

  1. Código novo → usa Temporal desde o começo
  2. Funções adaptadoras → pra conectar código antigo e novo
  3. Ir trocando → começando pelo código mais problemático
  4. 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.

AmbienteStatus
ChromeDisponível com flag
FirefoxEm desenvolvimento
SafariEm desenvolvimento
Node.jsDisponí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?

  1. Imutável → sem mudanças acidentais
  2. Tipos claros → PlainDate, ZonedDateTime... você sabe o que é cada coisa
  3. Faz sentido → meses a partir de 1, nomes de métodos lógicos
  4. Fusos de primeira → horário de verão incluído
  5. Matemática correta → finalmente funciona como esperado

Próximos passos:

  1. Testa o polyfill hoje mesmo
  2. Usa Temporal em projetos novos
  3. Planeja abandonar o moment.js
  4. Acompanha o progresso nos navegadores

O pesadelo do Date tá acabando. Temporal chegou! 🚀

javascripttemporal-apidate-timeecmascriptweb-developmenttypescriptmigration

Explore ferramentas relacionadas

Experimente estas ferramentas gratuitas do Pockit