El objeto Date de JavaScript lleva décadas siendo una fuente de bugs sutiles: los meses empiezan en 0, el tiempo local y UTC se mezclan de forma confusa, no hay soporte real de zonas horarias y la aritmética de fechas obliga a manejar milisegundos. La Temporal API llega al Stage 3 de TC39 para resolver todos estos problemas con un diseño moderno, inmutable y explícito sobre zonas horarias.
Por qué Date está roto
Antes de ver Temporal, conviene recordar por qué Date es problemático:
// Los meses van de 0 a 11: enero es 0, diciembre es 11
new Date(2024, 0, 1); // 1 de enero
new Date(2024, 11, 31); // 31 de diciembre
// Date.parse no es consistente entre navegadores
Date.parse('25/12/2024'); // NaN en algunos, número en otros
// No hay zona horaria: todo es UTC o local del sistema
const d = new Date('2024-12-25T00:00:00');
// ¿Es medianoche en Madrid, en UTC, en Nueva York?
// Depende del motor JavaScript
// Mutabilidad: setMonth modifica el objeto original
const fecha = new Date(2024, 0, 31);
fecha.setMonth(1); // Febrero tiene 28-29 días: salta a marzo 2 o 3
Temporal.PlainDate: fechas sin tiempo ni zona horaria
Temporal.PlainDate representa una fecha del calendario sin información de hora ni zona horaria. Los meses empiezan en 1 (como en el mundo real) y todos los objetos son inmutables:
// Instalar el polyfill oficial:
// npm install @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill';
const hoy = Temporal.Now.plainDateISO();
console.log(hoy.toString()); // "2024-06-15"
console.log(hoy.year, hoy.month, hoy.day); // 2024, 6, 15
const navidad = Temporal.PlainDate.from({ year: 2024, month: 12, day: 25 });
const diasHastaNavidad = hoy.until(navidad).days;
console.log(`Faltan ${diasHastaNavidad} días para Navidad`);
// Aritmética de fechas: devuelve un nuevo objeto, no modifica el original
const manana = hoy.add({ days: 1 });
const mesQueViene = hoy.add({ months: 1 });
const finDeAnio = hoy.with({ month: 12, day: 31 });
// Comparación
console.log(Temporal.PlainDate.compare(navidad, hoy)); // 1 (navidad > hoy)
console.log(navidad.equals(hoy)); // false
Temporal.ZonedDateTime: fecha, hora y zona horaria
Temporal.ZonedDateTime es el tipo más completo: incluye fecha, hora y zona horaria real (no solo un offset UTC). Gestiona correctamente el horario de verano, saltos de hora y zonas horarias sin ambigüedad:
// Instante actual en una zona horaria específica
const ahoraMadrid = Temporal.Now.zonedDateTimeISO('Europe/Madrid');
console.log(ahoraMadrid.toString());
// "2024-06-15T14:30:00+02:00[Europe/Madrid]"
// Crear un ZonedDateTime concreto
const reunion = Temporal.ZonedDateTime.from({
year: 2024,
month: 10,
day: 27, // día en que cambia el horario en España
hour: 2,
minute: 30,
timeZone: 'Europe/Madrid',
disambiguation: 'compatible', // resolver ambigüedad por cambio de hora
});
// Sumar duración respetando zonas horarias y horario de verano
const mananaReunion = reunion.add({ days: 1 });
// La hora es correcta aunque haya pasado un cambio de horario
// Convertir entre zonas horarias
const enNuevaYork = reunion.withTimeZone('America/New_York');
console.log(enNuevaYork.hour); // la hora local correcta en NY
Temporal.Duration: duraciones explícitas
Temporal.Duration representa un período de tiempo con componentes individuales (años, meses, semanas, días, horas, etc.) sin confundirlos con milisegundos:
const duracion = Temporal.Duration.from({ years: 1, months: 6, days: 15 });
console.log(duracion.toString()); // "P1Y6M15D"
const inicio = Temporal.PlainDate.from('2024-01-01');
const fin = Temporal.PlainDate.from('2024-06-15');
const diferencia = inicio.until(fin, { largestUnit: 'month' });
console.log(diferencia.months, diferencia.days); // 5, 14
// Redondear duraciones
const tiempoEjecucion = Temporal.Duration.from({ seconds: 125 });
const redondeado = tiempoEjecucion.round({ largestUnit: 'minute' });
console.log(redondeado.minutes, redondeado.seconds); // 2, 5
Instant: momentos absolutos en el tiempo
Temporal.Instant representa un punto exacto en el tiempo universal, independiente de cualquier zona horaria o calendario. Es el equivalente preciso a un timestamp Unix:
const ahora = Temporal.Now.instant(); console.log(ahora.epochMilliseconds); // como Date.now() console.log(ahora.epochNanoseconds); // precisión de nanosegundos // Convertir Date a Instant y viceversa const fecha = new Date(); const instant = Temporal.Instant.fromEpochMilliseconds(fecha.getTime()); const fechaDeVuelta = new Date(instant.epochMilliseconds); // Ordenar eventos por tiempo const eventos = [evento1, evento2, evento3]; eventos.sort((a, b) => Temporal.Instant.compare(a.instant, b.instant));
La Temporal API está en Stage 3 de TC39 desde 2021 y tiene soporte experimental en Firefox (detrás de una flag) y en todos los entornos a través del polyfill oficial @js-temporal/polyfill. Puedes usarla en producción hoy con el polyfill. La adopción nativa en navegadores se espera en cuanto termine el período de revisión del Stage 3.
