Cómo NO romper el tiempo (ni tu app)
Hay dos cosas inevitables en la vida: la muerte… y los bugs con fechas. Y uno de esos — con suerte — puedes evitarlo.
Si llevas tiempo programando, seguro alguna vez pensaste: “guardar una fecha no puede ser tan complicado”.
Error.
Sí puede. Y cuando explota, normalmente no lo hace un martes a las 11 de la mañana mientras tomas café tranquilo. Lo hace en producción, en silencio… y a veces el domingo a las 2:00 AM, justo cuando el tiempo decide hacer cosas raras.
El error clásico
Muchos sistemas empiezan así: “voy a guardar la fecha tal cual me llega”.
Entonces guardan algo como:
2026-04-09 10:00
Y ahí empieza el desastre.
Para un usuario en Houston quizá se ve bien. Para otro en New York ya cambió. Para otro sistema, ni siquiera está claro qué significa realmente ese valor.
La base de datos no está “entendiendo” una intención humana; solo está almacenando algo ambiguo… con demasiada confianza.
Los pecados capitales del manejo de fechas
1. Guardar hora local
Guardar la hora local parece cómodo… hasta que hay múltiples ciudades, usuarios remotos o integraciones.
De repente esa “10:00 AM” podría ser cualquier cosa dependiendo de quién la guardó.
2. Usar offsets fijos como GMT-5
Esto parece práctico, pero ignora cambios de horario y reglas reales de cada región.
Houston es GMT-6 en invierno y GMT-5 en verano. Usar un offset fijo es básicamente decirle al tiempo: “quédate quieto”.
No funciona.
3. Separar fecha y hora
date = '2026-04-09'
time = '10:00'
Esto funciona… hasta que no funciona.
¿Qué timezone aplica? ¿Qué pasa si hay cambio de horario justo ese día?
Dos columnas, cero contexto.
4. Ignorar el timezone en frontend y backend
Si no mandas ni conservas el contexto de zona horaria, luego nadie sabe qué quiso decir realmente el usuario.
Ni tú. Ni tu backend. Ni tu DBA a las 3 AM tratando de entender los logs.
La regla que salva vidas
La estrategia correcta para la mayoría de aplicaciones SaaS es simple:
- Guardar el instante en UTC → fuente de verdad
- Conservar el timezone → contexto humano
- Convertir solo en los bordes → entrada y salida
Ejemplo práctico
Un usuario crea una cita para las 10:00 AM en Houston.
El frontend envía:
- local datetime:
2026-04-09 10:00 - timezone:
America/Chicago
El backend convierte a UTC y guarda:
2026-04-09 15:00:00 UTC
Luego, al mostrarlo:
- Houston lo ve como 10:00 AM
- New York lo ve como 11:00 AM
Y todos siguen siendo amigos. Hasta que alguien guarda la hora en local time otra vez 😄
¿Y Unix timestamp?
Sí, se puede guardar como Unix timestamp.
Técnicamente funciona. Igual que muchas cosas que uno puede hacer en software… y que no necesariamente quiere mantener durante años.
Un Unix timestamp representa un instante en UTC como número:
1712674800
Ventajas
- Compacto
- Fácil de comparar
- Neutral respecto al timezone
Desventajas
- No es legible para humanos
- Complica debugging
- En logs luce como si tu app estuviera enviando mensajes cifrados
- No conserva el contexto del usuario ni su intención
Para sistemas con citas, reservas o lógica de negocio centrada en tiempo, prefiero guardar en tipos nativos del motor de base de datos — por ejemplo TIMESTAMPTZ en PostgreSQL — y conservar el timezone aparte cuando haga falta.
Ejemplo en JavaScript con Luxon
import { DateTime } from "luxon";
// Usuario en Houston crea cita a las 10:00 AM
const local = DateTime.fromISO("2026-04-09T10:00", {
zone: "America/Chicago"
});
// Convertir a UTC para guardar
const utc = local.toUTC();
// → 2026-04-09T15:00:00.000Z
// Mostrar en New York
const displayInNewYork = utc.setZone("America/New_York");
// → 2026-04-09T11:00:00.000-04:00
Mi conclusión
El tiempo no es complicado. 👉 Nosotros lo complicamos cuando creemos que es simple.
Si estás construyendo un sistema de citas, reservas, servicios a domicilio o cualquier app multiusuario con operaciones por horario, vale la pena resolver esto bien desde el inicio.
Mi regla personal:
- UTC como verdad absoluta
- Timezone como contexto
- Conversión solo al entrar y al salir del sistema
Eso evita bugs, discusiones inútiles… y llamadas el domingo a las 2:00 AM.
Lo que no cubrí (todavía)
Este post cubre los fundamentos, pero hay un par de madrigueras que dejé afuera a propósito:
- Transiciones de DST — ese momento mágico cuando el reloj salta hacia adelante o hacia atrás, y tu cita de las 2:30 AM o no existe o pasa dos veces. Ahí es donde empieza la verdadera diversión.
- Eventos recurrentes — “todos los martes a las 10:00 AM” suena simple hasta que el usuario se muda a otra zona horaria, o el DST mueve toda la serie una hora. UTC solo no resuelve esto — necesitas guardar la hora local original y el timezone, y recalcular cada ocurrencia.
- Temporal API — la nueva API nativa de fechas de JavaScript viene en camino y eventualmente reemplazará la necesidad de librerías como Luxon. Pero el soporte en browsers todavía no es universal, así que por ahora, Luxon (o
date-fns-tz) sigue siendo la opción práctica. - MySQL vs PostgreSQL — mencioné
TIMESTAMPTZen PostgreSQL, pero MySQL tiene su propia pesadilla conDATETIMEvsTIMESTAMP. Diferentes motores, diferentes trampas.
Cada uno de estos merece su propio post. Pendientes.
Este es el mismo tipo de pensamiento de ingeniería que aplico cuando construyo productos reales: resolver los problemas fundacionales difíciles temprano, para que el sistema pueda escalar con menos sorpresas… y menos incendios en producción.
Si este tema te interesó — o si alguna vez te peleaste con fechas en producción — puedes escribirme o dejar un comentario.
Me interesa ver cómo otros equipos están resolviendo este problema.
Loading comments...