Las fechas son más complejas de lo que crees

#dates
Tabla de contenidos
  1. El contexto siempre importa
  2. Un ejemplo
  3. Conclusión

A mi parecer, una de las tantas cosas que puedes aprender —y que te harán dejar de ser junior para ser todo un senior— es entender correctamente las fechas. Las fechas parecen tan simples y fáciles de manejar, pero realmente sí tienen su complejidad y, conforme te involucres más en el desarrollo de software, te vas dando cuenta de eso.

Primero que nada, en el Frontend, o más bien en JavaScript, el lenguaje de la web, el manejo de fechas siempre ha sido un poco complicado, ya que la simple clase/función de Date se queda corta para muchos casos. Han tratado de solucionarlo con mejoras constantes al lenguaje; una de esas nuevas features fue en su tiempo la API de Internacionalización (Intl), que ayuda a formatear correctamente las fechas, entre otros datos. Y más recientemente llegó la API de Temporal (esto era algo que ya realmente hacía falta).

Todas estas carencias en el lenguaje llevaron a la comunidad a buscar soluciones, y es por eso que existieron librerías como Moment.js (actualmente ya obsoleta), day.js, date-fns, entre otras. Quizá poco a poco, gracias a Temporal, ya también queden obsoletas, pero igual aún les queda para rato.

Pero esta publicación no es para hablar a fondo sobre estas librerías de manejo y formateo de fechas; hoy quiero enfocarme más en un problema tan común que no dudo que todos ya hayan pasado por él.

El contexto siempre importa

Cuando desarrollas una aplicación, es importante diseñar muy bien la arquitectura: no se trata de ir directo a programar, todo primero requiere un análisis y un plan, junto a la arquitectura y demás. En este caso, es importante que en las tablas siempre se consideren 3 campos importantes, ya que es información valiosa:

  • createdAt: cuando se crea un registro (el backend lo debe manejar automáticamente gracias a un ORM).
  • updatedAt: tener en cuenta cuando se actualiza el registro.
  • createdBy: si la aplicación requiere un usuario, es importante saber quién crea dicho registro. Y puede que haya otros para tener más información, porque la información lo es todo, pero hasta el momento esos 3 han sido los básicos que he necesitado yo.

Bueno, una vez entendido esto, ¿a qué quiero llegar?

Un ejemplo

Te toca desarrollar una aplicación, y ya está en un servidor de pruebas. Tienes un módulo de reportes y el cliente saca un reporte del mes anterior con toda la información que había, y te dice que el reporte está funcionando mal: está agregando cierta información extra del mes actual. Tú te preguntas qué está pasando, a ti en desarrollo te funciona bien…

Raro.

Hasta que, después de investigar (o le preguntas a ChatGPT, ya la IA te resuelve todo), te das cuenta de la zona horaria.

Estás en el frontend pidiendo información así:

const query = {
  startDate: "2026-08-01",
  endDate: "2026-08-31",
};

Y en desarrollo, tanto el backend como el frontend tienen la misma zona horaria.

Pero en el servidor se maneja en UTC (sin + ni - horas), porque así es como se deberían manejar siempre las fechas en los registros.

El backend está convirtiendo este objeto a objetos Date de JavaScript:

const start = new Date(query.startDate);
const end = new Date(query.endDate);

Fechas reales en el backend: Fecha inicio: 2026-08-01T00:00:00
Fecha final: 2026-08-31T00:00:00

Pero lo que realmente buscabas tú (suponiendo que tienes UTC-5) sería:

Inicio: 2026-07-31T19:00:00
Final: 2026-08-30T19:00:00

Y así te das cuenta de que las fechas son más que solo fechas: son contexto.

2026-09-10T03:20:17Z

Siguiendo el ejemplo, el cliente tiene una zona horaria GMT-2 y tú tienes la de GMT-5. Normalmente no notarás errores, porque el desfase sería de simplemente 3 horas. Por ejemplo, tú creas un registro el lunes 7 de septiembre a las 21:00; si pides ese registro, el backend debe enviarte la fecha de creación, pero vaya sorpresa: no verás que se creó el 7, sino el 8 de septiembre, porque se te debió devolver en UTC. Es decir, tendrías 2026-09-08T04:00:00Z, ya que GMT-5 = 21:00 y GMT = 03:00; a las 4 a.m. le restas 5 horas y serían las 9 p.m. del día anterior.

Y es por eso que siempre debes considerar la zona horaria. Siguiendo el ejemplo anterior, un reporte nunca será igual aquí y en China, porque la fecha inicial y la fecha final siempre deben considerarse con la zona horaria del usuario (a menos que se fije una zona horaria; y si se hace, lo ideal sería hacerlo en el frontend, pero siendo algo planeado). Es un poco complejo al principio, y es algo que un junior no suele considerar (me pasó mucho y siempre se me complicaba esto).

Y también toma en cuenta que, para un rango de fechas de, por ejemplo, un mes, siempre será mejor así:

Inicio: 2026-09-01T00:00:00Z
Fin: 2026-10-01T00:00:00Z

que así:

Inicio: 2026-09-01T00:00:00Z
Fin: 2026-10-30T23:59:59Z

pero eso ya es ser muy exactos y queda a consideración.

Conclusión

Si tienes una API o backend, siempre sé neutral en la zona horaria. Un servidor normalmente siempre manejará UTC (no lo trates de cambiar). Guarda los registros en hora neutral (sin el - o +), ya que es más complicado saber la zona horaria del usuario. Haz que el cliente maneje correctamente la zona horaria del usuario: desde el frontend no envíes fechas simples, envía el formato completo con UTC.