Dominar el tiempo con la biblioteca estándar de Python: datetime al máximo

Serie 03 – Operaciones de fecha y hora, zonas horarias y conversiones de formato todo en uno

El tiempo no es simplemente una "cadena de texto". Los días cambian, los meses transcurren, 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 al manejarlas.

El laboratorio mágico de un relojero

En este artículo, nos centraremos en la explicación del módulo datetime y abordaremos:

  • Crear la fecha y hora actuales
  • Calcular periodos (timedelta)
  • Formatear y analizar cadenas (strftime, strptime)
  • Gestionar zonas horarias (timezone, zoneinfo)
  • Puntos clave a considerar y patrones de uso recomendados

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ía
  • time : cuando solo necesitas hora:minuto:segundo
  • datetime : fecha + hora (el más usado)
  • timedelta : la diferencia (periodo) entre dos instantes
  • timezone : zona horaria con desplazamiento fijo (ej.: UTC+9)

A partir de Python 3.9, la biblioteca estándar incorpora zoneinfo, facilitando 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 generar errores o resultados inesperados.

2.2 Configuración predeterminada recomendada: comenzar con UTC aware

Unificar la base de cálculo y almacenamiento en UTC suele simplificar considerablemente 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 visualizació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 y horas: 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 requiere un enfoque diferente, 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 establecer 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 asigna la zona horaria. Para convertir, usa astimezone() (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 zona
  • astimezone(...): convierte el mismo instante a la hora local de otra zona

Conocer esta distinción ayuda a evitar muchos errores relacionados con zonas horarias.


6. Aspectos clave a considerar

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 su recepción.

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, es necesario 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 texto con fechas; 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, abarcando la generación de números aleatorios, el muestreo, la mezcla, las semillas y la generación segura con secrets.


Enlaces relacionados:


Consulta las entregas anteriores