JavaScript Date es un Desastre: Por Qué Temporal es la Solución que Necesitas
¿Te ha pasado que trabajas con fechas en JavaScript y sientes que `Date` te está jugando una mala pasada?
Si eres desarrollador, probablemente ya te topaste con problemas de zonas horarias, formatos de fecha inconsistentes o bugs raros que no tienen explicación.
Bueno, prepárate porque `Temporal` está aquí para cambiar todo eso.
El tiempo nos hace tontos a todos, y JavaScript tampoco se queda corto en ese aspecto. Honestamente, nunca me ha molestado mucho lo último — de hecho, si has trabajado con JavaScript, ya sabes que en gran medida _disfruto_ de las pequeñas peculiaridades del lenguaje, créeme o no.
Me gusta cuando puedes ver las costuras; me gusta cómo, por muy formal e inquebrantable que pueda parecer la especificación ES-262, aún puedes ver todas las decisiones buenas _y_ malas tomadas por las cientos de personas que han estado construyendo el lenguaje en pleno vuelo, si sabes dónde mirar. JavaScript tiene _carácter_.
Claro, no necesariamente hace todo _exactamente_ de la manera que uno podría esperar, pero ya sabes, si me preguntas, ¡JavaScript tiene un encanto real una vez que lo conoces!
Sin embargo, hay una parte del lenguaje donde eso inmediatamente se desmorona para mí.
// Los meses numéricos están indexados desde cero, pero los años y días no:
console.log( new Date(2026, 1, 1) );
// Resultado: Date Sun Feb 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
El constructor `Date`.
// Una cadena numérica entre 32 y 49 se asume que está en los años 2000:
console.log( new Date( "49" ) );
// Resultado: Date Fri Jan 01 2049 00:00:00 GMT-0500 (hora estándar de Colombia)
// Una cadena numérica entre 33 y 99 se asume que está en los años 1900:
console.log( new Date( "99" ) );
// Resultado: Date Fri Jan 01 1999 00:00:00 GMT-0500 (hora estándar de Colombia)
// ...Pero 100 y más empiezan desde el año cero:
console.log( new Date( "100" ) );
// Resultado: Date Fri Jan 01 0100 00:00:00 GMT-0456 (hora estándar de Colombia)
Detesto `Date` _inmensamente_.
// Una fecha basada en cadena funciona como podrías esperar:
console.log( new Date( "2026/1/2" ) );
// Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
// ¿Un cero inicial en el mes? No hay problema; uno es uno, ¿verdad?
console.log( new Date( "2026/02/2" ) );
// Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
// ¿Formato ligeramente diferente? ¡Por supuesto!
console.log( new Date( "2026-02-2" ) );
// Resultado: Date Mon Feb 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
// ¿Un cero inicial en el día? Por supuesto; ¿por qué no funcionaría?
console.log( new Date('2026/01/02') );
// Resultado: Date Fri Jan 02 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
// A menos, por supuesto, que separes el año, mes y fecha con guiones.
// Entonces se equivoca con el _día_ (y además cambia la zona horaria, ¡qué locura!).
console.log( new Date('2026-01-02') );
// Resultado: Date Thu Jan 01 2026 19:00:00 GMT-0500 (hora estándar de Colombia)
Date apesta.
Fue copiado apresurada y descaradamente de Java en el coche de camino a la escuela y obtuvo todas las mismas respuestas incorrectas, hasta el nombre en la parte superior de la página: `Date` no representa una _fecha_, representa un _tiempo_.
Internamente, las fechas se almacenan como valores numéricos llamados **valores de tiempo**: timestamps de Unix, divididos en 1,000 milisegundos — lo cual, bueno, sí, un tiempo Unix también necesariamente implica una fecha, claro, pero _aún así_: Date representa un tiempo, del cual puedes inferir una fecha.
Esto se vuelve aún más frustrante cuando trabajas con aplicaciones que necesitan manejar múltiples zonas horarias (México, Colombia, Argentina, Chile, etc.) o cuando intentas parsear fechas en formato DD/MM/YYYY que es el estándar en nuestra región.
// Timestamp Unix para el lunes, 4 de diciembre de 1995 12:00:00 AM GMT-05 (el día en que se anunció JavaScript):
const timestamp = 818053200;
console.log( new Date( timestamp * 1000 ) );
// Resultado: Date Mon Dec 04 1995 00:00:00 GMT-0500 (hora estándar de Colombia)
Palabras como “fecha” y “tiempo” significan cosas, pero, claro — _lo que sea, JavaScript_.
Java deprecó _su_ `Date` allá por 1997, solo unos años después de que el `Date` de JavaScript fuera liberado en el mundo desprevenido; mientras tanto, hemos estado atados a este desastre desde entonces. Es salvajemente inconsistente cuando se trata de analizar fechas, como has visto hasta ahora aquí.
No tiene sentido de zonas horarias más allá de la local y GMT, lo cual es un problema enorme para desarrolladores que trabajan con aplicaciones internacionales o que necesitan manejar usuarios en diferentes países (México tiene múltiples zonas horarias, Argentina y Chile tienen horario de verano, etc.).
Y hablando de eso, `Date` _solo_ respeta el modelo de calendario gregoriano. No entiende en absoluto el concepto de horario de verano de manera consistente, lo cual— quiero decir, bueno, sí, igual, pero yo no estoy _hecho de computadoras_.
Todas estas deficiencias hacen que sea excepcionalmente común usar bibliotecas de terceros como Moment.js, date-fns o Day.js para trabajar alrededor de todo esto, algunas de las cuales son absolutamente _masivas_; un drenaje de rendimiento que ha causado daño real y medible a la web, especialmente en dispositivos móviles.
Ninguna de estas es mi problema principal con `Date`. Mi queja es sobre más que analizar o sintaxis o “ergonomía del desarrollador” o el impacto de rendimiento en toda la web de soluciones completamente necesarias o incluso la definición de la palabra “fecha”. Mi problema con `Date` es profundo en el alma. Mi problema con `Date` es que usarlo significa _desviarse de la naturaleza fundamental del tiempo mismo_.
Todos los valores primitivos de JavaScript son **inmutables**, lo que significa que los valores mismos no pueden ser cambiados. El valor numérico `3` nunca puede representar nada más que el concepto de “tres” — no puedes hacer que `true` signifique algo diferente a “verdadero”. Estos son valores con significados concretos, inquebrantables, del mundo real. Sabemos qué es tres.
No puede ser alguna otra cosa que no sea tres. Estos tipos de datos inmutables se almacenan **por valor**, lo que significa que una variable que representa el valor numérico `3` efectivamente “contiene” — y por lo tanto se comporta como — el valor numérico `3`.
Cuando un valor inmutable se asigna a una variable, el motor de JavaScript crea una copia de ese valor y almacena la copia en memoria:
const theNumber = 3;
console.log( theNumber );
// Resultado: 3
Esto encaja bien con el modelo mental común para “una variable”: `theNumber` “contiene” `3`.
Cuando inicializamos `theOtherNumber` con el valor vinculado a `theNumber`, ese modelo mental se mantiene: una vez más se crea un `3` y se almacena en memoria. `theOtherNumber` ahora puede pensarse como que contiene su propio `3` discreto.
const theNumber = 3;
const theOtherNumber = theNumber;
console.log( theOtherNumber );
// Resultado: 3;
El valor de `theNumber` no cambia cuando alteramos el valor asociado con `theOtherNumber`, por supuesto — de nuevo, estamos trabajando con dos instancias discretas de `3`.
const theNumber = 3;
let theOtherNumber = theNumber;
theOtherNumber = 5;
console.log( theOtherNumber );
// Resultado: 5;
console.log( theNumber );
// Resultado: 3
Cuando cambias el valor vinculado a `theOtherNumber`, no estás cambiando el `3`, estás creando un nuevo valor numérico inmutable y vinculándolo en su lugar. De ahí un error cuando intentas manipular una variable declarada usando `const`:
const theNumber = 3;
theNumber = 5;
// Resultado: Uncaught TypeError: invalid assignment to const 'theNumber'
No puedes cambiar la vinculación de un `const`, y _definitivamente_ no puedes alterar el significado de `3`.
Los tipos de datos que _pueden_ ser cambiados después de ser creados son **mutables**, lo que significa que el valor de datos _mismo_ puede ser alterado. Los valores de objeto — cualquier valor no primitivo, como un array, map o set — son mutables.
Los objetos se almacenan **por referencia**, lo que significa que una variable que representa un objeto no contiene el objeto en sí, sino una referencia a la ubicación en memoria donde se almacena ese objeto. Cuando asignas un objeto a una variable, el motor de JavaScript almacena una referencia a ese objeto en memoria, no una copia del objeto mismo.
const theObject = { value: 3 };
console.log( theObject );
// Resultado: { value: 3 }
Cuando inicializamos `theOtherObject` con el valor vinculado a `theObject`, estamos creando una nueva referencia que apunta a la misma ubicación en memoria que `theObject`. Ambos `theObject` y `theOtherObject` ahora “apuntan” al mismo objeto en memoria.
const theObject = { value: 3 };
const theOtherObject = theObject;
console.log( theOtherObject );
// Resultado: { value: 3 };
Cuando alteramos el objeto referenciado por `theOtherObject`, estamos alterando el mismo objeto en memoria que `theObject` también está referenciando. Ambos `theObject` y `theOtherObject` ahora apuntan a un objeto con un valor diferente.
const theObject = { value: 3 };
const theOtherObject = theObject;
theOtherObject.value = 5;
console.log( theOtherObject );
// Resultado: { value: 5 };
console.log( theObject );
// Resultado: { value: 5 }
Esto es lo que significa trabajar con valores mutables: cuando alteras el objeto, estás alterando el valor real que representa, no solo la variable que lo referencia.
Y aquí está el problema con `Date`: `Date` es un objeto, lo que significa que es mutable. Cuando creas una instancia de `Date`, estás creando un objeto que representa un momento específico en el tiempo, y ese objeto puede ser alterado después de su creación.
const today = new Date();
console.log( today );
// Resultado: Date Wed Dec 31 2025 00:00:00 GMT-0500 (hora estándar de Colombia)
const tomorrow = today;
tomorrow.setDate( today.getDate() + 1 );
console.log( tomorrow );
// Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
console.log( today );
// Resultado: Date Thu Jan 01 2026 00:00:00 GMT-0500 (hora estándar de Colombia)
¡Oh no! `today` también cambió, porque `tomorrow` y `today` son referencias al mismo objeto `Date` en memoria. Cuando llamamos `setDate` en `tomorrow`, estamos alterando el mismo objeto que `today` está referenciando.
Esto es un problema fundamental. El tiempo no funciona así. El tiempo es inmutable. El 31 de diciembre de 2025 siempre será el 31 de diciembre de 2025.
No puedes “cambiar” el 31 de diciembre de 2025 para que sea el 1 de enero de 2026 — esos son dos momentos diferentes en el tiempo, y no puedes hacer que uno se convierta en el otro.
Imagínate esto en una aplicación de e-commerce donde calculas fechas de entrega, o en un sistema de reservas donde manejas fechas de check-in y check-out. Un bug así puede costarte clientes y dinero real.
Pero `Date` te permite hacer exactamente eso. Puedes tomar un objeto `Date` que representa el 31 de diciembre de 2025 y “cambiarlo” para que represente el 1 de enero de 2026, y eso es simplemente incorrecto. Es una violación de cómo funciona el tiempo en el mundo real.
const today = new Date();
const addDay = theDate => {
theDate.setDate( theDate.getDate() + 1 );
return theDate;
};
console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
// Resultado: Mañana será 1/1/2026. Hoy es 1/1/2026.
Esto es un desastre. `today` y el resultado de `addDay( today )` son el mismo objeto, por lo que ambos muestran la misma fecha. Esto no es cómo debería funcionar el tiempo.
El tiempo es inmutable. No puedes cambiar el pasado, no puedes cambiar el presente, y ciertamente no puedes cambiar el futuro simplemente alterando un objeto en memoria. El tiempo simplemente no funciona así.
Y aquí está la cosa: esto no es solo un problema filosófico. Esto causa errores reales en código real. Es fácil crear accidentalmente múltiples referencias al mismo objeto `Date` y luego alterar ese objeto de maneras que afectan todas esas referencias, lo que lleva a bugs sutiles y difíciles de rastrear.
Entra `Temporal`.
`Temporal` es una propuesta para una nueva API de JavaScript para trabajar con fechas y tiempos. Es una reescritura completa de cómo JavaScript maneja el tiempo, y está diseñada para abordar todos los problemas con `Date` que hemos estado discutiendo.
Lo más importante: `Temporal` es inmutable. Cuando creas un objeto `Temporal`, ese objeto representa un momento específico en el tiempo, y ese momento no puede ser cambiado. Cuando quieres representar un momento diferente en el tiempo, creas un nuevo objeto `Temporal`.
const today = Temporal.Now.plainDateISO();
console.log( today );
// Resultado: Temporal.PlainDate 2025-12-31
const tomorrow = today.add({ days: 1 });
console.log( tomorrow );
// Resultado: Temporal.PlainDate 2026-01-01
console.log( today );
// Resultado: Temporal.PlainDate 2025-12-31
¡Perfecto! `today` sigue siendo el 31 de diciembre de 2025, y `tomorrow` es el 1 de enero de 2026. No hay confusión, no hay efectos secundarios inesperados, no hay violación de la naturaleza fundamental del tiempo.
`Temporal` también aborda muchos de los otros problemas con `Date`:
– **Análisis consistente**: `Temporal` tiene un análisis de fechas mucho más predecible y consistente que `Date`.
– **Soporte de zonas horarias**: `Temporal` tiene soporte completo para zonas horarias, no solo la zona horaria local y GMT.
– **Soporte de calendarios**: `Temporal` puede trabajar con diferentes sistemas de calendario, no solo el calendario gregoriano.
– **API más clara**: `Temporal` tiene una API mucho más clara e intuitiva que `Date`.
Pero lo más importante es que `Temporal` respeta la naturaleza inmutable del tiempo. Cuando trabajas con `Temporal`, estás trabajando con valores que representan momentos específicos en el tiempo, y esos valores no pueden ser cambiados. Si quieres representar un momento diferente en el tiempo, creas un nuevo valor `Temporal`.
Esto es cómo debería funcionar el tiempo en programación. El tiempo es inmutable en el mundo real, y debería ser inmutable en nuestro código también.
`Temporal` todavía está en desarrollo, pero ya está disponible en las versiones más recientes de Chrome y Firefox. Pronto estará disponible en todos los navegadores principales, y finalmente podremos dejar `Date` atrás y usar una API de tiempo que realmente respete cómo funciona el tiempo.
Para desarrolladores, esto significa poder trabajar con zonas horarias de manera nativa sin depender de bibliotecas pesadas, manejar formatos de fecha locales (DD/MM/YYYY) de forma consistente, y evitar esos bugs raros que aparecen cuando trabajas con fechas en aplicaciones internacionales.
const today = Temporal.Now.plainDateISO();
// Fecha local actual:
console.log( today );
/* Resultado (expandido):
Temporal.PlainDate 2025-12-30
<prototype>: Object { … }
*/
// Año local actual:
console.log( today.year );
// Resultado: 2025
// Fecha y hora local actual:
console.log( today.toPlainDateTime() );
/* Resultado (expandido):
Temporal.PlainDateTime 2025-12-30T00:00:00
<prototype>: Object { … }
*/
// Especificar que esta fecha representa la zona horaria America/Mexico_City:
console.log( today.toZonedDateTime( "America/Mexico_City" ) );
/* Resultado (expandido):
Temporal.ZonedDateTime 2025-12-30T00:00:00-06:00[America/Mexico_City]
<prototype>: Object { … }
*/
// O trabajar con otras zonas horarias latinoamericanas:
console.log( today.toZonedDateTime( "America/Bogota" ) ); // Colombia
console.log( today.toZonedDateTime( "America/Buenos_Aires" ) ); // Argentina
console.log( today.toZonedDateTime( "America/Santiago" ) ); // Chile
// Agregar un día a esta fecha:
console.log( today.add({ days: 1 }) );
/*
Temporal.PlainDate 2025-12-31
<prototype>: Object { … }
*/
// Agregar un mes y un día a esta fecha, y restar dos años:
console.log( today.add({ months: 1, days: 1 }).subtract({ years: 2 }) );
/*
Temporal.PlainDate 2024-01-31
<prototype>: Object { … }
*/
console.log( today );
/* Resultado (expandido):
Temporal.PlainDate 2025-12-30
<prototype>: Object { … }
*/
Observa cómo ninguna de estas transformaciones requirió que manualmente creáramos nuevos objetos, _y_ que el valor del objeto referenciado por `today` permanece sin cambios. A diferencia de `Date`, los métodos que usamos para interactuar con un objeto `Temporal` resultan en objetos `Temporal` _nuevos_, en lugar de requerir que los usemos en el contexto de una nueva instancia o modificar la instancia con la que estamos trabajando — que es cómo podemos encadenar los métodos `add` y `subtract` juntos en `today.add({ months: 1, days: 1 }).subtract({ years: 2 })`.
Claro, todavía estamos trabajando con objetos, y eso significa que estamos trabajando con estructuras de datos mutables que representan valores del mundo real:
const today = Temporal.Now.plainDateISO();
today.someProperty = true;
console.log( today );
/* Resultado (expandido):
Temporal.PlainDate 2026-01-05
someProperty: true
<prototype>: Object { … }
*/
…Pero el valor representado por ese objeto `Temporal` no está destinado a ser cambiado durante el curso normal de interactuar con él — aunque el objeto sigue siendo esencialmente mutable, no estamos atrapados usando ese objeto de maneras que podrían alterar lo que significa en términos de fechas y tiempos del mundo real.
Lo acepto.
Entonces, revisemos ese pequeño script “hoy es X, mañana es Y” que escribimos usando `Date` anteriormente. Primero, lo arreglaremos asegurándonos de que estamos trabajando con dos instancias discretas de `Date` en lugar de modificar la instancia que representa la fecha de hoy:
const today = new Date();
const addDay = theDate => {
const tomorrow = new Date();
tomorrow.setDate( theDate.getDate() + 1 );
return tomorrow;
};
console.log(`Mañana será ${ addDay( today ).toLocaleDateString() }. Hoy es ${ today.toLocaleDateString() }.`);
// Resultado: Mañana será 1/1/2026. Hoy es 12/31/2025.
Gracias, lo odio.
Bien, está bien. Cumple su función, tal como lo ha hecho desde el día en que `Date` se abrió paso por primera vez en la web. No estamos alterando sin saberlo el valor de `today` ya que estamos creando una nueva instancia de `Date` dentro de nuestra función `addDay` — verboso, pero funciona, como lo ha hecho durante décadas ahora. Le agregamos `1`, que tenemos que simplemente _saber_ que significa agregar un _día._
Luego en nuestro template literal necesitamos seguir empujando a JavaScript para que nos dé la fecha en un formato que no incluya la hora actual, como una cadena. Es funcional, pero verboso.
Ahora, rehagámoslo usando `Temporal`:
const today = Temporal.Now.plainDateISO();
console.log(`Mañana será ${ today.add({ days: 1 }) }. Hoy es ${ today }.`);
// Resultado: Mañana será 2026-01-01. Hoy es 2025-12-31.
Ahora sí estamos hablando.
_Mucho mejor_. Más delgado, más eficiente, y _mucho_ menos margen para error. Queremos la fecha de hoy sin la hora, y el objeto que resulta de invocar `plainDateISO` (y cualquier nuevo objeto `Temporal` creado a partir de él) retendrá ese formato _sin_ ser coaccionado a una cadena.
Formato: _verificado_.
Queremos generar un valor que represente la fecha de hoy más un día, y queremos hacerlo de una manera donde estamos diciendo inequívocamente “agregar un día” sin conjeturas de análisis: _verificado_ y _verificado_.
Lo más importante, no queremos correr el riesgo de que nuestro objeto `today` original sea alterado sin intención — porque el resultado de llamar al método `add` siempre será un nuevo objeto `Temporal`: _verificado_.
`Temporal` va a ser una _mejora masiva_ sobre `Date`, y solo digo “va a ser” porque todavía no está completamente listo para uso en producción.
La especificación de borrador para el objeto `Temporal` propuesto ha alcanzado la etapa tres del proceso de estandarización, lo que significa que ahora está oficialmente “recomendado para implementación” — aún no es parte del estándar que informa el desarrollo continuo de JavaScript mismo, pero lo suficientemente cerca como para que los navegadores puedan empezar a experimentar con él.
Eso significa que los resultados de esa experimentación temprana pueden usarse para refinar aún más la especificación, por lo que nada está escrito en piedra todavía. Los estándares web son un proceso iterativo, después de todo.
Ahí es donde entramos tú y yo. Ahora que `Temporal` ha llegado a las versiones más recientes de Chrome y Firefox — y otros, pronto — es hora de que entremos y probemos un poco. Puede que no hayamos tenido ninguna opinión sobre `Date`, pero podemos experimentar con `Temporal` antes de que lleguen las implementaciones finales.
Pronto, JavaScript tendrá un manejo de fechas sensato y moderno, y finalmente podremos meter `Date` muy atrás en el cajón de chatarra con las bandas elásticas, tapas de frascos sin pareja, llaves misteriosas y probablemente pilas AA medio vacías — todavía presente, todavía una parte inexorable de la plataforma web, pero ya no nuestra primera, última y única forma de manejar fechas.
Y solo tuvimos que esperar— bueno, espera, déjame calcular los números rápidamente con `Temporal`:
Pruébalo
const today = Temporal.Now.plainDateISO();
const jsShipped = Temporal.PlainDate.from( "1995-12-04" );
const sinceDate = today.since( jsShipped, { largestUnit: 'year' });
console.log( `${ sinceDate.years } años, ${ sinceDate.months } meses, y ${ sinceDate.days } días.` );
Ejecutar
Claro, el mejor momento para reemplazar `Date` habría sido allá por 1995, pero oye: el segundo mejor momento es `Temporal.Now`, ¿verdad?
## ¿Por Qué Esto Importa para Desarrolladores en Latinoamérica?
Como desarrolladores latinoamericanos, trabajamos constantemente con:
– **Aplicaciones internacionales** que necesitan manejar múltiples zonas horarias
– **Formatos de fecha locales** (DD/MM/YYYY) que `Date` maneja de forma inconsistente
– **Dispositivos móviles** donde el rendimiento importa y las bibliotecas pesadas de fechas afectan la experiencia del usuario
– **E-commerce y sistemas de reservas** donde un error con fechas puede costar dinero real
`Temporal` viene a resolver todos estos problemas de forma nativa, sin necesidad de bibliotecas externas pesadas. Es hora de empezar a experimentar con esta nueva API y prepararnos para el futuro del manejo de fechas en JavaScript.
**¿Te resultó útil este artículo?** Compártelo con otros desarrolladores que también están luchando con `Date`. Y si quieres profundizar más en JavaScript moderno, asegúrate de seguir aprendiendo sobre las nuevas características del lenguaje.
