Cuando Brendan Eich creó JavaScript en 1995, copió el objeto Date directamente de Java 1.0. Lo hizo en diez días. Nunca fue pensado para perdurar, pero aquí seguimos treinta años después cargando con sus consecuencias.
El problema más conocido es que los meses van de 0 a 11. Enero es 0, diciembre es 11. No hay ninguna razón lógica para eso salvo que Java lo hacía así y Eich no tuvo tiempo de cuestionarlo. El segundo problema es la mutabilidad: si llamas a setMonth() sobre un objeto Date, lo modificas directamente sin que nada te avise. Esto genera bugs sutiles que cuesta horas rastrear.
Las zonas horarias son otro frente abierto. Date trabaja internamente en UTC pero muestra la hora según el entorno del usuario, lo cual significa que el mismo código da resultados distintos dependiendo de dónde se ejecute. Y Date.parse() se comporta de forma diferente entre navegadores con strings que no siguen el formato ISO estricto.
Todo esto ha llevado a que durante años la respuesta estándar fuera "usa Moment.js" o "usa date-fns". Son buenas librerías, pero es un parche sobre un problema que debería estar resuelto en el propio lenguaje.
Temporal: tipos para cada situación
Temporal no reemplaza Date con un único tipo mejorado, sino que introduce varios tipos específicos según lo que necesites representar. Cada uno tiene una responsabilidad concreta, lo que elimina la ambigüedad que tanto daño hace con Date.
Los tipos principales
- Temporal.PlainDate: una fecha sin hora ni zona horaria. Útil para cumpleaños, fechas de vencimiento, cualquier cosa que no dependa de la hora del día. Ejemplo:
2026-06-24. - Temporal.PlainTime: una hora sin fecha. Para horarios de apertura, alarmas, cosas que se repiten cada día a la misma hora.
- Temporal.PlainDateTime: fecha y hora juntas pero sin zona horaria. Sirve para eventos locales donde la zona no importa, como un recordatorio en una agenda personal.
- Temporal.ZonedDateTime: fecha, hora y zona horaria. Este es el tipo que querrás usar en producción para casi todo lo que involucre usuarios en distintas partes del mundo.
- Temporal.Instant: un momento exacto en el tiempo, equivalente a un timestamp Unix. No lleva zona horaria, solo un punto fijo en la línea temporal.
- Temporal.Duration: una duración, no un punto en el tiempo. Por ejemplo, "3 horas y 30 minutos" o "2 semanas".
La clave es que todos estos tipos son inmutables. Cuando operas sobre ellos, no modificas el original: obtienes un objeto nuevo. Eso solo ya evita una categoría entera de bugs.
Operaciones que antes requerían una librería
Con Date, sumar 30 días a una fecha requería código manual o tirar de date-fns. Con Temporal es directo:
const hoy = Temporal.Now.plainDateISO();
const enTreintaDias = hoy.add({ days: 30 });
Para calcular la diferencia entre dos fechas, until() devuelve un objeto Duration:
const inicio = Temporal.PlainDate.from('2026-01-01');
const fin = Temporal.PlainDate.from('2026-06-24');
const diferencia = inicio.until(fin);
console.log(diferencia.days); // 174
Comparar fechas también es limpio. Temporal.PlainDate.compare(a, b) devuelve -1 si a es anterior, 0 si son iguales y 1 si a es posterior. Exactamente el mismo contrato que Array.sort() espera, así que puedes pasarlo directamente como función de ordenación.
Para moverse al primer o último día de un mes:
const fecha = Temporal.PlainDate.from('2026-06-24');
const primero = fecha.with({ day: 1 });
const ultimo = fecha.with({ day: fecha.daysInMonth });
Nada de aritmética manual, nada de comprobar si el mes tiene 28, 29, 30 o 31 días. Temporal lo sabe.
Zonas horarias de verdad
Esta es probablemente la parte donde Temporal marca más diferencia respecto a lo que teníamos antes.
const evento = Temporal.ZonedDateTime.from(
'2026-06-24T14:00:00[Europe/Madrid]'
);
La zona horaria va en el string entre corchetes, siguiendo el estándar IANA. Para convertir ese mismo instante a otra zona:
const enNuevaYork = evento.withTimeZone('America/New_York');
console.log(enNuevaYork.toString());
// 2026-06-24T08:00:00-04:00[America/New_York]
El cambio de hora (DST) lo gestiona Temporal automáticamente. Si un evento cae en la noche en que los relojes se adelantan, Temporal sabe qué hacer. No tienes que añadir ni quitar horas a mano ni confiar en que moment-timezone tenga la base de datos de zonas actualizada.
Esto significa que para la mayoría de casos con zonas horarias ya no necesitas moment-timezone ni date-fns-tz. Los puedes sacar de tu package.json.
Cómo migrar desde Date
Si tienes código existente que usa Date, la migración es gradual. Para convertir un objeto Date a Temporal:
const fechaVieja = new Date(); // objeto Date clásico
const instant = Temporal.Instant.fromEpochMilliseconds(fechaVieja.getTime());
const zonedDt = instant.toZonedDateTimeISO('Europe/Madrid');
Y si en algún punto necesitas volver a Date porque una librería externa lo requiere:
const fechaNueva = new Date(instant.epochMilliseconds);
El método .toLocaleString() sigue funcionando igual que siempre, así que la capa de visualización no cambia.
Interoperabilidad con Intl
Temporal está diseñado para funcionar bien con la API Intl de JavaScript, que gestiona internacionalización. Para obtener la fecha y hora actual en zona horaria local con formato español:
const ahora = Temporal.Now.zonedDateTimeISO();
console.log(ahora.toLocaleString('es-ES', { calendar: 'gregory' }));
Temporal también soporta calendarios no gregorianos. Si necesitas trabajar con el calendario hebreo, el islámico o cualquier otro que reconozca Intl:
const fechaHebrea = Temporal.PlainDate.from({
year: 2026,
month: 6,
day: 24,
calendar: 'hebrew'
});
Esto abre la puerta a aplicaciones que necesitan soportar calendarios alternativos sin añadir librerías de terceros.
Dónde está disponible en 2026
Temporal lleva tiempo en desarrollo dentro de TC39, el comité que estandariza JavaScript. Ahora mismo está en Stage 3, que es el último escalón antes de la ratificación oficial, y el soporte real está llegando.
- Deno 2.7 (febrero de 2026): soporte nativo completo, sin polyfill.
- Firefox 139+: disponible sin flags.
- Chrome 127+: disponible a través de origin trial.
- Node.js: pendiente de implementación nativa. Por ahora hay que usar el polyfill oficial
@js-temporal/polyfill. - Safari: en proceso.
Si trabajas con las novedades recientes en el runtime de Node.js y quieres usar Temporal hoy mismo, la opción más práctica es instalar el polyfill:
npm install @js-temporal/polyfill
import { Temporal } from '@js-temporal/polyfill';
El polyfill tiene la misma API que la implementación nativa, así que cuando Node añada soporte oficial solo tendrás que quitar la importación. En Deno puedes usar Temporal directamente sin instalar nada.
Para producción en navegadores hoy, la apuesta más segura es el polyfill combinado con un bundler que lo incluya solo donde haga falta. En Chrome y Firefox ya puedes detectar soporte nativo con typeof Temporal !== 'undefined' y evitar cargar el polyfill si no es necesario.
La historia de JavaScript está llena de decisiones tomadas deprisa que luego han tardado décadas en corregirse. Temporal es una de las correcciones más esperadas: llega tarde, pero llega bien hecha.
Imagen: Pexels / Pixabay
