Dominar el tiempo con la biblioteca estándar de Python: datetime al máximo
Serie 03 – Operaciones de fecha/hora, zonas horarias y conversiones de formato en un solo lugar
El tiempo no es simplemente una "cadena de texto". Los días cambian, los meses se rotan, el horario de verano entra en juego y cada región tiene su propio estándar. Por eso, al trabajar con fechas y horas es crucial distinguir entre la representación textual y el valor numérico que se puede calcular, y también incluir la zona horaria en el manejo.

En este artículo, centramos la explicación en el módulo datetime y cubrimos:
- Crear la fecha y hora actuales
- Calcular periodos (
timedelta) - Formatear y analizar cadenas (
strftime,strptime) - Gestionar zonas horarias (
timezone,zoneinfo) - Puntos de atención frecuentes y patrones confiables
1. ¿Qué ofrece datetime?
El módulo datetime incluye varios tipos que, aunque parecidos, cumplen roles distintos.
date: cuando solo necesitas año‑mes‑díatime: cuando solo necesitas hora:minuto:segundodatetime: fecha + hora (el más usado)timedelta: la diferencia (periodo) entre dos instantestimezone: zona horaria con desplazamiento fijo (ej.: UTC+9)
A partir de Python 3.9, la biblioteca estándar incorpora zoneinfo, lo que facilita el manejo de zonas horarias locales (ej.: Asia/Tokyo).
2. Obtener el "ahora": desde naive hasta aware
2.1 ¿Qué son naive y aware?
Los objetos datetime se clasifican en dos grupos.
- naive datetime: sin información de zona horaria
- aware datetime: con información de zona horaria (
tzinfo)
Mezclar ambos en cálculos o comparaciones puede provocar errores o resultados inesperados.
2.2 Valor por defecto recomendado: comenzar con UTC aware
Unificar la base de cálculo y almacenamiento en UTC suele simplificar mucho las cosas.
from datetime import datetime, timezone
utc_now = datetime.now(timezone.utc) # aware (UTC)
print(utc_now)
Si necesitas la hora local, conviértela en la fase de presentación.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_now = datetime.now(timezone.utc)
tokyo_now = utc_now.astimezone(ZoneInfo("Asia/Tokyo"))
print(utc_now)
print(tokyo_now)
3. Cálculo de fechas/hora: el papel central de timedelta
3.1 Sumar y restar
from datetime import datetime, timedelta, timezone
now = datetime.now(timezone.utc)
print(now + timedelta(days=3))
print(now - timedelta(hours=2))
3.2 Diferencia entre dos instantes
from datetime import datetime, timezone
a = datetime(2026, 1, 1, tzinfo=timezone.utc)
b = datetime(2026, 1, 10, tzinfo=timezone.utc)
delta = b - a
print(delta.days) # 9
print(delta.total_seconds()) # 777600.0
3.3 Sensibilidad al usar timedelta para "periodos"
timedelta opera en días, segundos y microsegundos. Expresiones como "un mes después" se aproximan con timedelta(days=30), pero para el calendario real (el mismo día del mes siguiente) se necesita otra aproximación, normalmente con dateutil u otras librerías externas.
4. Formato y análisis: strftime / strptime
4.1 datetime → cadena (strftime)
from datetime import datetime, timezone
dt = datetime(2026, 1, 30, 14, 5, 0, tzinfo=timezone.utc)
print(dt.strftime("%Y-%m-%d %H:%M:%S %z"))
Patrones comunes:
%Y-%m-%d: 2026-01-30%H:%M:%S: 14:05:00%z: +0000 (desplazamiento UTC)
4.2 Cadena → datetime (strptime)
from datetime import datetime
s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt)
El dt resultante es naive. Si quieres fijar la zona horaria, añade tzinfo.
from datetime import datetime, timezone
s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
print(dt)
replace(tzinfo=...)no convierte el valor de tiempo, solo le etiqueta la zona horaria. Para convertir, usaastimezone()(ver siguiente sección).
5. Zonas horarias: diferenciar timezone y zoneinfo
5.1 Desplazamiento fijo → timezone
Ej.: UTC, UTC+9 con desplazamiento constante.
from datetime import datetime, timezone, timedelta
kst_fixed = timezone(timedelta(hours=9))
dt = datetime(2026, 1, 30, 12, 0, tzinfo=kst_fixed)
print(dt)
5.2 Zona horaria local → zoneinfo
Para zonas con horario de verano, zoneinfo es la opción adecuada.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_now = datetime.now(timezone.utc)
ny_now = utc_now.astimezone(ZoneInfo("America/New_York"))
print(ny_now)
5.3 Diferencia entre replace(tzinfo=...) y astimezone(...)
replace(tzinfo=...): mantiene el valor de tiempo, solo cambia la etiqueta de zonaastimezone(...): convierte el mismo instante a la hora local de otra zona
Conocer esta distinción reduce muchos errores de zona horaria.
6. Puntos de atención frecuentes
6.1 Mezcla de naive/aware
- Para cálculos internos, usar aware (UTC) es más seguro.
- Para entradas externas, normaliza la zona horaria al momento de recibirla.
6.2 "Hora local" varía según el entorno
datetime.now() sigue la configuración local del entorno de ejecución. En contenedores o servidores, la hora local puede ser UTC o no. Usar datetime.now(timezone.utc) evita ambigüedades.
6.3 Inconsistencias en el análisis de cadenas
strptime falla si el formato difiere en un solo carácter. Si se reciben múltiples formatos, se necesita pre‑procesar o probar secuencialmente. Lo ideal es restringir el formato de entrada.
7. Tres patrones comunes
7.1 Guardar en formato ISO 8601
Para una representación estándar, isoformat() es útil.
from datetime import datetime, timezone
dt = datetime.now(timezone.utc)
print(dt.isoformat()) # ej.: 2026-01-30T05:12:34.567890+00:00
7.2 Nombrar archivos con la fecha de hoy
from datetime import datetime
stamp = datetime.now().strftime("%Y%m%d")
filename = f"report_{stamp}.json"
print(filename)
7.3 Calcular tiempo restante hasta una fecha objetivo
from datetime import datetime, timezone
target = datetime(2026, 2, 1, 0, 0, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
remaining = target - now
print(remaining)
print(remaining.total_seconds())
8. Conclusión
datetime va más allá de crear cadenas de fecha; convierte el tiempo en un valor que se puede manipular aritméticamente. Incluir zonas horarias (timezone, zoneinfo) garantiza que el código sea estable, independientemente del entorno de ejecución.
En la próxima entrega, abordaremos el módulo random por separado, cubriendo generación de números aleatorios, muestreo, mezcla, semillas y la generación segura con secrets.
Enlaces relacionados:
- Usar correctamente datetime y timezone en Django
- La magia de la gestión del tiempo en Django - Guía completa de 'django.utils.timezone'
Ver series anteriores
No hay comentarios.