La API Temporal de JavaScript: un reemplazo moderno de Date
JavaScript Date lleva décadas roto de formas bien conocidas. TC39 Temporal — Stage 4 desde principios de 2025, ya en navegadores modernos y Node.js — separa con limpieza los instantes, las fechas de reloj de pared y las fechas-hora zonificadas. Esta guía explica los nuevos tipos, cómo se corresponden con los timestamps Unix y cómo migrar.
Temporal vs Date de un vistazo
El Date integrado de JavaScript ha sido una fuente conocida de bugs durante décadas: es mutable, solo entiende la zona local y UTC, sus meses se indexan desde cero y su parsing está laxamente especificado. Temporal es el reemplazo normalizado — Stage 4 y parte de ECMAScript 2026 — y corrige cada uno de esos problemas con tipos inmutables hechos a propósito. La tabla siguiente es el contraste general; el resto de esta guía cubre los tipos y cómo se corresponden con los timestamps Unix.
| Aspecto | Date (heredado) | Temporal |
|---|---|---|
| Mutabilidad | Mutable | Inmutable |
| Precisión | Milisegundos | Nanosegundos |
| Zonas horarias | Solo local + UTC | Cualquier zona IANA (ZonedDateTime) |
| Indexación de meses | Base 0 (0 = enero) | Base 1 (1 = enero) |
| Parsing | Permisivo, definido por la implementación | ISO 8601 / RFC 9557 estricto |
| Instante vs reloj de pared | Confundidos en un solo tipo | Tipos separados |
Por qué existe Temporal
JavaScript Date confunde varios conceptos distintos: un instante exacto, una hora de reloj de pared y una fecha-hora zonificada. Date también tiene rarezas de parsing bien conocidas, una sola zona (la del usuario) y precisión de milisegundos. Temporal expone los conceptos subyacentes como tipos separados y admite precisión de nanosegundos, zonas IANA arbitrarias y sistemas de calendario explícitos.
- Stage 4 desde principios de 2025; lanzado primero por defecto en Firefox 139
- Ahora disponible en Chrome 144+, Firefox 139+ y Node.js 26+
- Los polyfills (@js-temporal/polyfill) funcionan en cualquier entorno moderno
- Diseñado para coexistir con Date — existen métodos de interoperabilidad en ambos sentidos
Los tipos esenciales de Temporal
Un pequeño conjunto de tipos cubre con limpieza el problema de las fechas-hora. Cada uno tiene una forma de cadena canónica (ISO 8601, extendida por RFC 9557 para el tipo zonificado):
| Tipo | Representa | Cadena de ejemplo |
|---|---|---|
| Temporal.Instant | Un instante exacto, como un timestamp Unix (precisión ns) | 2026-01-01T00:00:00Z |
| Temporal.ZonedDateTime | Instante + zona IANA + calendario | 2026-01-01T09:00:00+09:00[Asia/Tokyo] |
| Temporal.PlainDateTime | Fecha + hora, sin zona | 2026-01-01T09:00:00 |
| Temporal.PlainDate | Una fecha de calendario (p. ej. un cumpleaños) | 2026-01-01 |
| Temporal.PlainTime | Una hora de reloj de pared | 09:00:00 |
| Temporal.Duration | Una duración, consciente de unidades | P3DT4H10M |
Trabajar con timestamps Unix
Temporal.Instant es el tipo timestamp Unix. La API publicada solo expone conversiones epoch en milisegundos y nanosegundos — los métodos anteriores fromEpochSeconds y fromEpochMicroseconds se eliminaron antes del Stage 4 — así que convierte segundos Unix multiplicando por 1000. Usa Instant para almacenamiento y transporte, y ZonedDateTime cuando necesites una vista de reloj de pared en una zona concreta.
- Temporal.Now.instant() — el momento exacto actual
- Temporal.Instant.fromEpochMilliseconds(Date.now()) — desde un timestamp JS en milisegundos
- Temporal.Instant.fromEpochMilliseconds(1700000000 * 1000) — desde segundos Unix (× 1000)
- Temporal.Instant.fromEpochNanoseconds(1700000000000000000n) — desde nanosegundos (BigInt)
- instant.epochMilliseconds y instant.epochNanoseconds — leer de vuelta el valor epoch
- instant.toZonedDateTimeISO("America/New_York") — una vista de reloj de pared en una zona
Zonas y DST manejados correctamente
ZonedDateTime lleva una zona IANA, así que las transiciones de DST, las horas de hueco y las horas de solapamiento se manejan todas explícitamente. La opción de desambiguación te deja elegir una política para horas locales ambiguas, y el formato de cadena canónico es RFC 9557 (un timestamp ISO 8601 con la zona entre corchetes).
- zdt.timeZoneId — el nombre IANA (p. ej. America/Los_Angeles)
- zdt.add({ hours: 1 }) — aritmética consciente del DST en términos de reloj de pared
- zdt.until(zdt2) — distancia entre dos fechas-hora zonificadas
- Opciones de desambiguación: "earlier", "later", "compatible" (por defecto — posterior para el hueco, anterior para el solapamiento), "reject"
- Ida y vuelta: instante ↔ fecha-hora zonificada es sin pérdida
Patrones de migración desde Date
La estrategia de migración más sencilla es introducir Temporal primero en las fronteras (parsing de entrada, formateo de salida) e ir moviendo gradualmente la lógica interna.
- Date → Instant: Temporal.Instant.fromEpochMilliseconds(date.getTime())
- Instant → Date: new Date(instant.epochMilliseconds)
- Para código nuevo, prefiere Temporal en todas partes y convierte a Date solo en los bordes de la API
- date-fns y Luxon tienen ambos modos / adaptadores conscientes de Temporal
- JSON: Temporal.Instant.prototype.toJSON devuelve una cadena ISO 8601
Soporte en navegadores y runtimes
Temporal se lanzó en 2025 y el soporte nativo ya es amplio, pero aún no universal. El polyfill es la vía más segura mientras los navegadores antiguos y Safari estable sigan en servicio.
- Firefox 139+: el primero en lanzarlo, habilitado por defecto (mayo de 2025)
- Chrome 144+: habilitado por defecto (enero de 2026)
- Node.js 26+: habilitado por defecto (mayo de 2026)
- Safari: disponible en Technology Preview, aún no en una versión estable
- Polyfill: @js-temporal/polyfill o temporal-polyfill — misma API, cualquier entorno moderno
Referencias relacionadas
FAQ
- ¿Es Temporal un reemplazo de Date?
- Sí — Temporal está diseñado para reemplazar por completo a Date en el código nuevo. Date permanece en el lenguaje por compatibilidad hacia atrás, y ambos interoperan vía milisegundos epoch.
- ¿Cómo convierto un timestamp Unix a Temporal?
- Usa Temporal.Instant.fromEpochMilliseconds(). Para segundos Unix, multiplica por 1000: Temporal.Instant.fromEpochMilliseconds(seconds * 1000). La API publicada solo tiene fromEpochMilliseconds y fromEpochNanoseconds — el antiguo método fromEpochSeconds se eliminó antes de que Temporal alcanzara el Stage 4.
- ¿Temporal admite nanosegundos?
- Sí. Temporal.Instant tiene precisión de nanosegundos internamente y expone epochNanoseconds como un BigInt.
- ¿Puedo usar Temporal en Node.js hoy?
- En Node.js 26+ Temporal está habilitado por defecto. En versiones anteriores de Node, instala @js-temporal/polyfill — la superficie de la API es idéntica.
- ¿Qué navegadores admiten Temporal?
- Firefox 139+ (mayo de 2025), Chrome 144+ (enero de 2026) y Node.js 26+ (mayo de 2026) incluyen Temporal por defecto. Safari lo tiene en Technology Preview. Para cualquier cosa anterior, usa el @js-temporal/polyfill.
- ¿Cómo maneja Temporal el hueco del DST?
- Los constructores y la aritmética de ZonedDateTime aceptan una opción de desambiguación. "compatible" (por defecto) elige el instante posterior para los huecos y el anterior para los solapamientos; "reject" lanza un error si la hora local es ambigua.