7 bugs de timestamp Unix que todo desarrollador ha lanzado

Estos son los errores de timestamp que causan incidentes en producción: discrepancias silenciosas de unidad, suposiciones de zona horaria, el ×1000 olvidado, el almacenamiento como cadena, el parseo ambiguo y los errores en los límites del horario de verano. Cada bug incluye el síntoma, la causa raíz y una solución práctica.

Bug 1: Falta × 1000 en JavaScript

new Date(1700000000) produce una fecha cercana a enero de 1970, no a noviembre de 2023. El constructor Date de JavaScript espera milisegundos, pero la mayoría de las APIs de servidor y bases de datos devuelven segundos Unix. Este bug suele aparecer primero en los paneles de administración porque el payload del backend es JSON válido y la interfaz simplemente muestra el año equivocado.

  • Wrong: new Date(response.created_at) — if created_at is Unix seconds
  • Right: new Date(response.created_at * 1000)
  • Detection: if your date shows a year near 1970, the timestamp is in seconds not milliseconds
  • Safe wrapper: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)

Bug 2: Unidades mezcladas en el límite de un sistema

JavaScript devuelve milisegundos. Python, Go y la mayoría de las APIs de servidor devuelven segundos. Cuando JavaScript envía un timestamp a un backend Python sin convertir, el backend interpreta un valor en milisegundos como segundos, situando el evento en el año 55792. La dirección inversa es igual de mala: la interfaz recibe segundos, los pasa directamente a new Date() y muestra enero de 1970.

  • JavaScript sends: Date.now() → 1700000000000 (ms)
  • Python receives: datetime.fromtimestamp(1700000000000) → year 55792
  • Fix: divide by 1000 before sending to a non-JavaScript system
  • Better fix: use ISO 8601 strings at API boundaries — they are unambiguous

Bug 3: Confiar en la zona horaria local del servidor

El código que usa datetime.fromtimestamp() de Python sin especificar UTC produce resultados distintos según la zona horaria configurada del servidor. Funciona en desarrollo y se rompe en producción al desplegar en otra región. La misma categoría de bug aparece en Node cuando el código formatea fechas sin una opción timeZone explícita y luego captura el resultado en los tests.

  • Wrong: datetime.fromtimestamp(ts) — uses server's local timezone
  • Right: datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
  • Wrong in JS: date.toLocaleDateString() — uses the Node process timezone
  • Right in JS: date.toISOString() or Intl.DateTimeFormat with an explicit timeZone option

Bug 4: Guardar timestamps como cadenas

Guardar un timestamp como cadena formateada en una columna VARCHAR parece inofensivo hasta que necesitas ordenar por tiempo, consultar un rango o hacer aritmética. La comparación de cadenas de fechas solo funciona con campos ISO 8601 estrictos y rellenados con ceros. Cadenas legibles como Nov 15, 2023 o 1/2/24 son formatos de visualización, no de almacenamiento.

  • Wrong: INSERT INTO events (created) VALUES ('Nov 15, 2023') — not sortable
  • Wrong: INSERT INTO events (created) VALUES ('2023-11-15') — loses time component
  • Right: use a native DATETIME/TIMESTAMPTZ column or BIGINT for Unix milliseconds
  • If a string is unavoidable: use ISO 8601 with full precision: '2023-11-15T06:13:20Z'

Bug 5: Parsear cadenas de fecha ambiguas

new Date('2024-01-01') y new Date('2024/01/01') parecen equivalentes pero se comportan distinto. El formato ISO 8601 con guiones se parsea como medianoche UTC; el formato con barras depende de la implementación y la mayoría de los navegadores lo parsean como medianoche local. Un selector de fecha, un payload de API y una fila de base de datos pueden mostrar la misma fecha de calendario y representar instantes distintos.

  • new Date('2024-01-01') → January 1, 2024 00:00:00 UTC (correct, explicit)
  • new Date('2024/01/01') → January 1, 2024 00:00:00 local time (varies by runtime)
  • new Date('January 1, 2024') → local time, may be rejected by strict parsers
  • Safe rule: always use ISO 8601 with explicit timezone: new Date('2024-01-01T00:00:00Z')

Bug 6: Sumar 24 horas para obtener mañana

Sumar 86.400.000 milisegundos parece una forma limpia de obtener mañana, pero los días de calendario locales no siempre duran 24 horas. Durante las transiciones de horario de verano, un día local puede tener 23 o 25 horas. Si la lógica del producto significa el siguiente día de calendario local, usa aritmética de calendario en la zona horaria destino, no aritmética de duración en milisegundos UTC.

  • Mal para días de calendario: next = new Date(date.getTime() + 86400000)
  • Mejor en código Date local: copia el Date y luego llama a setDate(d.getDate() + 1)
  • Para código de servidor: usa una librería con zonas horarias o aritmética de fechas de la base de datos
  • Para horarios recurrentes: guarda local_date, local_time y timezone_id, y calcula cada ocurrencia
  • Añade tests para las fechas de inicio y fin del horario de verano en la zona horaria destino

Bug 7: Filtros de fin de día inclusivos

Una consulta de analítica común usa created_at >= start y created_at <= endOfDay. Parece razonable, pero crea bugs de precisión cuando la base de datos guarda milisegundos, microsegundos o nanosegundos. El patrón más seguro es un rango semiabierto: mayor o igual que el inicio, y estrictamente menor que el inicio del periodo siguiente.

  • Risky: WHERE created_at <= '2026-05-19 23:59:59'
  • Safer: WHERE created_at >= '2026-05-19' AND created_at < '2026-05-20'
  • Half-open ranges work for seconds, milliseconds, microseconds, and nanoseconds
  • The same pattern works for months: created_at >= monthStart AND created_at < nextMonthStart

Lista de prevención en producción

La mayoría de los incidentes con timestamps se evitan con buenos nombres, tests y límites explícitos. El objetivo no es que cada línea de código de fechas sea ingeniosa; es que la unidad, la zona horaria y la semántica de rango sean aburridamente obvias.

  • Nombra los campos numéricos con su unidad: createdAtMs, expiresAtSeconds, imported_at_ms
  • Usa UTC para almacenar y zonas horarias IANA explícitas para mostrar
  • Prefiere cadenas ISO 8601 con Z u offsets en los límites de API revisados por humanos
  • Usa rangos semiabiertos para los filtros de fecha
  • Haz tests alrededor del 1 de enero, los días bisiestos, las transiciones de horario de verano y el límite del año 2038

Preguntas frecuentes sobre bugs de timestamp Unix

¿Cuál es el bug de timestamp Unix más común?
Pasar segundos donde se esperan milisegundos, o al revés. En JavaScript, new Date(unixSeconds) cae en 1970 porque el constructor espera milisegundos; multiplica primero el valor de 10 dígitos por 1000.
¿Cómo prevengo los bugs de segundos-vs-milisegundos?
Nombra los campos con su unidad (createdAtMs, expiresAtSeconds), convierte en los límites del sistema y prefiere cadenas ISO 8601 en los contratos de API para que el valor se describa a sí mismo.
¿Por qué sumar 86.400.000 ms rompe «mañana»?
Los días de calendario locales duran 23 o 25 horas durante las transiciones de horario de verano, así que sumar 24 horas fijas puede caer en la fecha equivocada. Usa aritmética de calendario en la zona horaria destino.