Voltar para o Blog

JavaScript Temporal API: O Fim do Date e Moment.js Esta Proximo

Ola HaWkers, depois de anos de desenvolvimento, a Temporal API esta finalmente chegando aos navegadores principais. Esta nova API nativa promete resolver todos os problemas historicos do objeto Date no JavaScript.

Vamos explorar como usar a Temporal API e preparar seus projetos para essa mudanca.

Por Que Precisamos da Temporal API

Problemas do Date Atual

O objeto Date do JavaScript foi criado em 10 dias em 1995, copiado do Java, e nunca foi corrigido.

Problemas classicos do Date:

// Problema 1: Meses comecam em 0
const date = new Date(2026, 0, 20); // Janeiro, nao Fevereiro
console.log(date.getMonth()); // 0, nao 1

// Problema 2: Anos de 2 digitos
const y2k = new Date(99, 0, 1);
console.log(y2k.getFullYear()); // 1999, nao 99 ou 2099

// Problema 3: Mutabilidade
const original = new Date(2026, 0, 20);
const modified = original;
modified.setMonth(5);
console.log(original.getMonth()); // 5 - original foi alterado!

// Problema 4: Parsing inconsistente
new Date('2026-01-20'); // UTC
new Date('2026/01/20'); // Local
new Date('01-20-2026'); // Invalido em alguns navegadores

// Problema 5: Sem suporte a timezone
// Nao existe forma nativa de trabalhar com fusos horarios

// Problema 6: Sem suporte a duracao
// Nao existe forma de representar "2 horas e 30 minutos"

O Que a Temporal Resolve

A Temporal API foi projetada para corrigir todos esses problemas:

Principios da Temporal:

  1. Imutabilidade: Todos os objetos sao imutaveis
  2. Clareza: APIs explicitas sem ambiguidade
  3. Timezones: Suporte completo a fusos horarios
  4. Precisao: Nanosegundos de precisao
  5. Tipos separados: Diferentes tipos para diferentes necessidades

Tipos Principais da Temporal

Visao Geral dos Tipos

A Temporal introduz varios tipos especializados:

// Temporal.Instant - momento exato no tempo (UTC)
const instant = Temporal.Instant.from('2026-01-20T15:30:00Z');

// Temporal.ZonedDateTime - data/hora com timezone
const zonedDateTime = Temporal.ZonedDateTime.from({
  year: 2026,
  month: 1,
  day: 20,
  hour: 15,
  minute: 30,
  timeZone: 'America/Sao_Paulo'
});

// Temporal.PlainDateTime - data/hora sem timezone
const plainDateTime = Temporal.PlainDateTime.from({
  year: 2026,
  month: 1,
  day: 20,
  hour: 15,
  minute: 30
});

// Temporal.PlainDate - apenas data
const plainDate = Temporal.PlainDate.from({
  year: 2026,
  month: 1,
  day: 20
});

// Temporal.PlainTime - apenas hora
const plainTime = Temporal.PlainTime.from({
  hour: 15,
  minute: 30,
  second: 0
});

// Temporal.PlainYearMonth - ano e mes
const yearMonth = Temporal.PlainYearMonth.from({
  year: 2026,
  month: 1
});

// Temporal.PlainMonthDay - mes e dia (aniversarios)
const monthDay = Temporal.PlainMonthDay.from({
  month: 1,
  day: 20
});

// Temporal.Duration - duracao de tempo
const duration = Temporal.Duration.from({
  hours: 2,
  minutes: 30
});

Quando Usar Cada Tipo

Tipo Caso de Uso Exemplo
Instant Timestamps de eventos Logs, auditoria
ZonedDateTime Eventos locais Reunioes, voos
PlainDateTime Eventos sem fuso Templates
PlainDate Datas apenas Aniversarios, feriados
PlainTime Horas apenas Horarios de funcionamento
Duration Intervalos Tempo de execucao

Usando a Temporal na Pratica

Criando Datas

// A partir de string ISO
const date1 = Temporal.PlainDate.from('2026-01-20');

// A partir de objeto
const date2 = Temporal.PlainDate.from({
  year: 2026,
  month: 1,
  day: 20
});

// Data atual
const today = Temporal.Now.plainDateISO();
console.log(today.toString()); // "2026-01-20"

// DateTime atual com timezone
const now = Temporal.Now.zonedDateTimeISO('America/Sao_Paulo');
console.log(now.toString());
// "2026-01-20T12:30:00-03:00[America/Sao_Paulo]"

Manipulando Datas

const date = Temporal.PlainDate.from('2026-01-20');

// Adicionar tempo (retorna nova instancia)
const nextWeek = date.add({ days: 7 });
console.log(nextWeek.toString()); // "2026-01-27"

// Subtrair tempo
const lastMonth = date.subtract({ months: 1 });
console.log(lastMonth.toString()); // "2025-12-20"

// Modificar campos especificos
const newYear = date.with({ month: 12, day: 31 });
console.log(newYear.toString()); // "2026-12-31"

// Original permanece inalterado (imutabilidade)
console.log(date.toString()); // "2026-01-20"

Comparando Datas

const date1 = Temporal.PlainDate.from('2026-01-20');
const date2 = Temporal.PlainDate.from('2026-02-15');

// Comparacao
console.log(Temporal.PlainDate.compare(date1, date2)); // -1 (date1 < date2)

// Igualdade
console.log(date1.equals(date2)); // false

// Diferenca entre datas
const diff = date1.until(date2);
console.log(diff.toString()); // "P26D" (26 dias)
console.log(diff.days); // 26

// Diferenca em unidades especificas
const diffInWeeks = date1.until(date2, { largestUnit: 'weeks' });
console.log(diffInWeeks.toString()); // "P3W5D" (3 semanas e 5 dias)

Trabalhando com Timezones

ZonedDateTime

// Criar datetime com timezone
const meeting = Temporal.ZonedDateTime.from({
  year: 2026,
  month: 1,
  day: 20,
  hour: 15,
  minute: 0,
  timeZone: 'America/Sao_Paulo'
});

console.log(meeting.toString());
// "2026-01-20T15:00:00-03:00[America/Sao_Paulo]"

// Converter para outro fuso
const meetingInTokyo = meeting.withTimeZone('Asia/Tokyo');
console.log(meetingInTokyo.toString());
// "2026-01-21T03:00:00+09:00[Asia/Tokyo]"

// O momento e o mesmo, apenas a representacao muda
console.log(meeting.toInstant().equals(meetingInTokyo.toInstant())); // true

Lidando com Horario de Verao

// Temporal lida corretamente com DST
const beforeDST = Temporal.ZonedDateTime.from({
  year: 2026,
  month: 10,
  day: 31,
  hour: 23,
  minute: 0,
  timeZone: 'America/Sao_Paulo'
});

// Adicionar 2 horas atravessa a mudanca de horario
const afterDST = beforeDST.add({ hours: 2 });
console.log(afterDST.hour); // Hora correta considerando DST

// Verificar offset
console.log(beforeDST.offset); // "-03:00"
console.log(afterDST.offset); // "-02:00" (horario de verao)

Conversao Entre Sistemas

// De Date para Temporal
const legacyDate = new Date('2026-01-20T15:30:00Z');
const instant = Temporal.Instant.fromEpochMilliseconds(legacyDate.getTime());
const zoned = instant.toZonedDateTimeISO('America/Sao_Paulo');

// De Temporal para Date
const temporal = Temporal.ZonedDateTime.from('2026-01-20T15:30:00-03:00[America/Sao_Paulo]');
const backToDate = new Date(temporal.epochMilliseconds);

Duracoes e Intervalos

Trabalhando com Duration

// Criar duracao
const duration = Temporal.Duration.from({
  hours: 2,
  minutes: 30,
  seconds: 45
});

console.log(duration.toString()); // "PT2H30M45S"

// Aritmetica com duracoes
const doubleDuration = duration.add(duration);
console.log(doubleDuration.toString()); // "PT5H1M30S"

// Negacao
const negativeDuration = duration.negated();
console.log(negativeDuration.toString()); // "-PT2H30M45S"

// Valor absoluto
console.log(negativeDuration.abs().toString()); // "PT2H30M45S"

Calculando Diferencas

const startDate = Temporal.PlainDate.from('2026-01-01');
const endDate = Temporal.PlainDate.from('2026-12-31');

// Diferenca padrao (em dias)
const diffDays = startDate.until(endDate);
console.log(diffDays.days); // 364

// Diferenca em meses
const diffMonths = startDate.until(endDate, { largestUnit: 'months' });
console.log(diffMonths.toString()); // "P11M30D"

// Diferenca em anos
const diffYears = startDate.until(endDate, { largestUnit: 'years' });
console.log(diffYears.toString()); // "P0Y11M30D"

// Total em uma unidade especifica
const totalDays = diffDays.total('days');
console.log(totalDays); // 364

Arredondamento de Duracoes

const duration = Temporal.Duration.from({
  hours: 1,
  minutes: 45,
  seconds: 30
});

// Arredondar para horas
const roundedHours = duration.round({
  largestUnit: 'hours',
  smallestUnit: 'hours',
  roundingMode: 'halfExpand'
});
console.log(roundedHours.toString()); // "PT2H"

// Arredondar para minutos
const roundedMinutes = duration.round({
  smallestUnit: 'minutes',
  roundingMode: 'floor'
});
console.log(roundedMinutes.toString()); // "PT1H45M"

Formatacao de Datas

Usando Intl.DateTimeFormat

const date = Temporal.PlainDate.from('2026-01-20');

// Formatacao basica
const formatter = new Intl.DateTimeFormat('pt-BR', {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

console.log(formatter.format(date));
// "terca-feira, 20 de janeiro de 2026"

// Formatacao de datetime com timezone
const dateTime = Temporal.ZonedDateTime.from({
  year: 2026,
  month: 1,
  day: 20,
  hour: 15,
  minute: 30,
  timeZone: 'America/Sao_Paulo'
});

const fullFormatter = new Intl.DateTimeFormat('pt-BR', {
  dateStyle: 'full',
  timeStyle: 'long',
  timeZone: 'America/Sao_Paulo'
});

console.log(fullFormatter.format(dateTime));
// "terca-feira, 20 de janeiro de 2026 15:30:00 BRT"

Formatacao Relativa

const now = Temporal.Now.plainDateISO();
const pastDate = now.subtract({ days: 3 });
const futureDate = now.add({ months: 2 });

// Calcular diferenca
const pastDiff = pastDate.until(now);
const futureDiff = now.until(futureDate);

// Usar Intl.RelativeTimeFormat
const rtf = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });

console.log(rtf.format(-pastDiff.days, 'day'));
// "ha 3 dias"

console.log(rtf.format(futureDiff.months, 'month'));
// "em 2 meses"

Migrando de Bibliotecas Existentes

Substituindo Moment.js

// Moment.js (antigo)
import moment from 'moment';

const m = moment('2026-01-20');
const added = m.add(7, 'days');
const formatted = m.format('DD/MM/YYYY');

// Temporal (novo)
const t = Temporal.PlainDate.from('2026-01-20');
const addedT = t.add({ days: 7 });
const formattedT = new Intl.DateTimeFormat('pt-BR').format(addedT);

// Comparacao de tamanho de bundle
// Moment.js: ~70KB minificado
// Temporal: 0KB (nativo do navegador)

Substituindo date-fns

// date-fns (antigo)
import { addDays, format, differenceInDays } from 'date-fns';
import { ptBR } from 'date-fns/locale';

const date = new Date(2026, 0, 20);
const added = addDays(date, 7);
const formatted = format(date, 'dd/MM/yyyy', { locale: ptBR });
const diff = differenceInDays(new Date(2026, 1, 20), date);

// Temporal (novo)
const t = Temporal.PlainDate.from('2026-01-20');
const addedT = t.add({ days: 7 });
const formattedT = new Intl.DateTimeFormat('pt-BR').format(t);
const diffT = t.until(Temporal.PlainDate.from('2026-02-20')).days;

Tabela de Equivalencias

Operacao Moment/date-fns Temporal
Criar data moment() / new Date() Temporal.Now.plainDateISO()
Adicionar dias .add(7, 'days') .add({ days: 7 })
Subtrair meses .subtract(1, 'month') .subtract({ months: 1 })
Formatar .format('DD/MM/YYYY') Intl.DateTimeFormat
Diferenca differenceInDays() .until().days
Comparar .isBefore() Temporal.PlainDate.compare()
Timezone moment.tz() Temporal.ZonedDateTime

Suporte Atual e Polyfills

Status de Implementacao

Em janeiro de 2026, o suporte esta assim:

Navegador Status
Chrome Flag experimental
Firefox Em desenvolvimento
Safari Em desenvolvimento
Node.js Flag --harmony-temporal
Deno Suportado
Bun Suportado

Usando Polyfill

Enquanto o suporte nativo nao chega, use o polyfill oficial:

npm install @js-temporal/polyfill
// Importar polyfill
import { Temporal } from '@js-temporal/polyfill';

// Usar normalmente
const now = Temporal.Now.plainDateISO();
console.log(now.toString());

// Verificar se nativo esta disponivel
if (typeof globalThis.Temporal !== 'undefined') {
  console.log('Usando Temporal nativo');
} else {
  console.log('Usando polyfill');
}

Configuracao TypeScript

// tsconfig.json
{
  "compilerOptions": {
    "lib": ["ES2022", "DOM"],
    "types": ["@js-temporal/polyfill"]
  }
}

// Uso com tipos
import { Temporal } from '@js-temporal/polyfill';

function calculateAge(birthDate: Temporal.PlainDate): number {
  const today = Temporal.Now.plainDateISO();
  const age = birthDate.until(today, { largestUnit: 'years' });
  return age.years;
}

const birth = Temporal.PlainDate.from('1990-05-15');
console.log(`Idade: ${calculateAge(birth)} anos`);

Casos de Uso Avancados

Sistema de Agendamento

import { Temporal } from '@js-temporal/polyfill';

interface Appointment {
  id: string;
  title: string;
  start: Temporal.ZonedDateTime;
  duration: Temporal.Duration;
  timezone: string;
}

class Scheduler {
  private appointments: Appointment[] = [];

  addAppointment(
    title: string,
    start: string,
    durationMinutes: number,
    timezone: string
  ): Appointment {
    const appointment: Appointment = {
      id: crypto.randomUUID(),
      title,
      start: Temporal.ZonedDateTime.from(`${start}[${timezone}]`),
      duration: Temporal.Duration.from({ minutes: durationMinutes }),
      timezone
    };

    this.appointments.push(appointment);
    return appointment;
  }

  getEndTime(appointment: Appointment): Temporal.ZonedDateTime {
    return appointment.start.add(appointment.duration);
  }

  hasConflict(newStart: Temporal.ZonedDateTime, duration: Temporal.Duration): boolean {
    const newEnd = newStart.add(duration);

    return this.appointments.some(apt => {
      const aptEnd = this.getEndTime(apt);

      // Verifica sobreposicao
      return (
        Temporal.ZonedDateTime.compare(newStart, aptEnd) < 0 &&
        Temporal.ZonedDateTime.compare(newEnd, apt.start) > 0
      );
    });
  }

  getAppointmentsForDay(date: Temporal.PlainDate, timezone: string): Appointment[] {
    return this.appointments.filter(apt => {
      const aptDate = apt.start.withTimeZone(timezone).toPlainDate();
      return aptDate.equals(date);
    });
  }
}

// Uso
const scheduler = new Scheduler();

scheduler.addAppointment(
  'Reuniao de equipe',
  '2026-01-20T10:00:00',
  60,
  'America/Sao_Paulo'
);

scheduler.addAppointment(
  'Call com cliente internacional',
  '2026-01-20T15:00:00',
  30,
  'America/New_York'
);

Calculo de Dias Uteis

function addBusinessDays(
  date: Temporal.PlainDate,
  days: number
): Temporal.PlainDate {
  let result = date;
  let remaining = days;

  while (remaining > 0) {
    result = result.add({ days: 1 });
    const dayOfWeek = result.dayOfWeek;

    // 6 = Sabado, 7 = Domingo
    if (dayOfWeek !== 6 && dayOfWeek !== 7) {
      remaining--;
    }
  }

  return result;
}

function getBusinessDaysBetween(
  start: Temporal.PlainDate,
  end: Temporal.PlainDate
): number {
  let count = 0;
  let current = start;

  while (Temporal.PlainDate.compare(current, end) < 0) {
    current = current.add({ days: 1 });
    if (current.dayOfWeek !== 6 && current.dayOfWeek !== 7) {
      count++;
    }
  }

  return count;
}

// Uso
const today = Temporal.PlainDate.from('2026-01-20'); // Segunda
const deadline = addBusinessDays(today, 5);
console.log(deadline.toString()); // "2026-01-27" (proxima segunda)

const businessDays = getBusinessDaysBetween(today, deadline);
console.log(`${businessDays} dias uteis`); // "5 dias uteis"

Conclusao

A Temporal API representa a maior evolucao no tratamento de datas do JavaScript desde sua criacao. Finalmente temos uma solucao nativa que resolve os problemas historicos do Date.

Pontos principais:

  1. Objetos imutaveis evitam bugs comuns
  2. Tipos separados para diferentes necessidades
  3. Suporte completo a timezones
  4. Precisao de nanosegundos
  5. API clara e sem ambiguidades

Recomendacoes:

  • Comece a usar o polyfill em novos projetos
  • Planeje migracao gradual de Moment.js/date-fns
  • Aprenda os diferentes tipos e quando usar cada um
  • Acompanhe o progresso de implementacao nos navegadores

A transicao vai levar tempo, mas vale comecar a se familiarizar com a Temporal API agora.

Para mais conteudo sobre JavaScript moderno, leia: Descobrindo o Poder do Async/Await no JavaScript.

Bora pra cima! 🦅

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário