Saltar al contenido
AldeaCode Logo
Convertidor de Timestamp / Python Desarrollador 100% local

Convertir timestamps Unix en Python: datetime, código timezone-aware y Y2038

El módulo datetime de Python convierte timestamps Unix a fechas con una llamada. El default es hora local, fuente de más bugs que el resto del módulo junto. El código timezone-aware cuesta tres caracteres extra y te ahorra un incidente en producción.

fromtimestamp devuelve hora local por defecto

datetime.fromtimestamp(1715342400) convierte un timestamp Unix (segundos desde 1970-01-01 UTC) a un datetime en la zona horaria local de la máquina que corre el código. En el portátil de un dev en Madrid sale algo como 2024-05-10 12:00:00. En un servidor en UTC sale 2024-05-10 10:00:00. Mismo input, dos respuestas distintas.

El resultado es además un datetime naive: no tiene tzinfo adjunto, así que cualquier código que luego pregunte por la zona recibe None. Compararlo con un datetime aware lanza TypeError. Guardarlo viaja mal si el sistema consumidor asume UTC.

El arreglo es pasar el argumento tz:

from datetime import datetime, timezone
dt = datetime.fromtimestamp(1715342400, tz=timezone.utc)

El resultado ahora es un datetime aware en UTC. En cualquier máquina, en cualquier sitio, obtienes la misma respuesta.

utcfromtimestamp queda deprecada en 3.12

Durante años la respuesta canónica a "dame UTC" fue datetime.utcfromtimestamp(ts). Desde Python 3.12 (octubre 2023) la función está deprecada, programada para eliminarse en 3.14. El reemplazo es la forma con tz=timezone.utc de arriba.

La razón de la deprecación es exactamente el bug que causaba. utcfromtimestamp devolvía un datetime naive que casualmente contenía valores UTC. El código aguas abajo no sabía que era UTC y lo trataba como hora local, aplicando los offsets dos veces. La nueva API te obliga a declarar la zona, y eso se propaga al resto del programa y hace imposible el bug.

Si tienes una base de código que llama a utcfromtimestamp, planifica la migración. El reemplazo es mecánico y el warning de deprecación va a pasar a error.

.timestamp() para el camino inverso

Para volver de un datetime a un timestamp Unix, llama a .timestamp() sobre el datetime. El método devuelve un float con los segundos desde el epoch.

Si el datetime es aware, el método honra la zona correctamente. Si es naive, el método asume hora local, lo que significa que el resultado depende de dónde corra el código. Trabaja siempre con datetimes aware al cruzar fronteras de timestamp:

from datetime import datetime, timezone
ts = datetime(2026, 5, 10, 8, 0, tzinfo=timezone.utc).timestamp()
# 1778760000.0, lo mismo en cualquier máquina

Para zonas IANA arbitrarias (Europe/Madrid, America/New_York), usa el módulo de stdlib zoneinfo, disponible desde Python 3.9. dateutil.tz es la opción de tercero más antigua y sigue funcionando, pero zoneinfo es la elección moderna.

Pandas y la vía masiva

Cuando tienes una columna de timestamps (un CSV, un extracto de base de datos), pandas.to_datetime vectoriza la conversión:

import pandas as pd
df["created_at"] = pd.to_datetime(df["unix_ts"], unit="s", utc=True)

El argumento unit importa. El default es nanosegundos; los webhooks y la mayoría de APIs mandan segundos; algunas APIs derivadas de JS mandan milisegundos. Elegir mal desplaza cada timestamp 1000 o 1e6 veces, lo que produce fechas en el año 56000 o en 1970 según el sentido. El flag utc=True devuelve datetimes UTC aware, la misma lección que en stdlib.

Y2038 y time_t signed de 32 bits

El tiempo Unix entra cómodamente en un entero de 64 bits para miles de millones de años. El problema es time_t signed de 32 bits, que desborda el 2038-01-19 03:14:07 UTC. Python en sí usa enteros de 64 bits internamente y no se ve afectado. Tu preocupación son los sistemas con los que habla Python: librerías C legacy, dispositivos embebidos, sistemas de archivos ext3, columnas TIMESTAMP de MySQL en esquemas viejos.

Si guardas timestamps en una columna declarada como entero signed de 32 bits, planifica una migración antes de 2038. Si lees de un embebido cuyo firmware usa time_t de 32 bits, espera comportamiento de wraparound cerca de la fecha. El int de Python no tiene tope, así que las funciones de conversión aceptan cualquier valor, pero el límite que te importa está al otro lado del cable.

Ejemplo completo

python
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Round-trip con código timezone-aware
ts = 1778760000  # segundos desde epoch

# 1. Timestamp Unix -> datetime aware UTC
dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_utc)  # 2026-05-10 08:00:00+00:00

# 2. Convertir a una zona IANA concreta para mostrar
dt_madrid = dt_utc.astimezone(ZoneInfo("Europe/Madrid"))
print(dt_madrid)  # 2026-05-10 10:00:00+02:00 (CEST)

# 3. Datetime aware -> timestamp Unix
ts_back = dt_utc.timestamp()
print(ts_back == ts)  # True

# 4. Vía masiva con pandas para una columna de segundos
import pandas as pd
df = pd.DataFrame({"unix_ts": [1778760000, 1778763600]})
df["dt"] = pd.to_datetime(df["unix_ts"], unit="s", utc=True)
print(df)

¿Solo necesitas el resultado?

Cuando tienes un timestamp Unix de un webhook o de una línea de log y quieres ver la fecha en tu zona local sin escribir dos líneas de código, el convertidor de timestamp de aldeacode.com renderiza el resultado al instante, soporta segundos y milisegundos, y corre en tu navegador sin enviar el valor a ningún sitio.

Abrir Convertidor de Timestamp Unix →

Preguntas frecuentes

¿Por qué fromtimestamp me da una fecha distinta en mi portátil y en el servidor?

Sin argumento tz, fromtimestamp devuelve hora local. Las dos máquinas están en zonas distintas. Pasa tz=timezone.utc, o cualquier zona explícita, y ambas máquinas devuelven el mismo datetime.

¿Es seguro seguir usando utcfromtimestamp?

En 3.11 y anteriores sí, pero está deprecada en 3.12 y prevista para eliminarse en 3.14. Reemplázala con datetime.fromtimestamp(ts, tz=timezone.utc). El cambio es una línea por sitio de llamada y el resultado es un datetime aware que el código aguas abajo trata bien.

¿Me tengo que preocupar de Y2038 en código Python que escribo hoy?

En Python en sí no. Preocúpate de los sistemas con los que se integra: librerías C de 32 bits, firmware embebido, tipos de columna legacy de base de datos. Audítalos antes de 2038 y amplíalos a almacenamiento de 64 bits. El int de Python no tiene tope.