El módulo datetime lleva en Python desde siempre, y desde la 3.9 trae zoneinfo, que da acceso a la base de datos de husos del sistema sin instalar nada. Con eso está todo.
También te puede interesar
Las clases, y la distinción que importa
from datetime import date, time, datetime, timedelta, UTC
from zoneinfo import ZoneInfo
d = date(2026, 10, 7)
t = time(14, 30)
dt = datetime(2026, 10, 7, 14, 30) # «naive»: SIN zona
mad = dt.replace(tzinfo=ZoneInfo("Europe/Madrid")) # «aware»: con zona
salidadate 2026-10-07 time 14:30:00 datetime 2026-10-07 14:30:00 <- SIN zona: «naive» con zona 2026-10-07 14:30:00+02:00 en UTC 2026-10-07 12:30:00+00:00 marca de tiempo 1791376200
Un datetime sin zona no identifica un instante: «el 7 de octubre a las 14:30» ocurre a horas distintas en Madrid y en Tokio. Para registrar cuándo pasó algo, siempre con zona, y lo habitual es guardarlo en UTC.
Aritmética
d + timedelta(days=1) d - timedelta(weeks=3) (date(2027, 3, 1) - d).days d.replace(day=1)
salida+1 día 2026-10-08 -3 semanas 2026-09-16 diferencia 145 días primer día 2026-10-01 último día 2026-10-31
Restar dos fechas da un timedelta, que se mide en días, segundos y microsegundos. Fíjate en que no hay timedelta(months=1): un mes no dura siempre lo mismo. Para eso está dateutil.relativedelta, o se calcula a mano con calendar.monthrange, que es lo que hace ese «último día».
Formatear y analizar
dt.isoformat() # 2026-10-07T14:30:00
dt.strftime("%d/%m/%Y %H:%M") # 07/10/2026 14:30
datetime.strptime("07/10/2026 14:30", "%d/%m/%Y %H:%M")
datetime.fromisoformat("2026-10-07T14:30:00+02:00")
salidaisoformat 2026-10-07T14:30:00 strftime 07/10/2026 14:30 strptime 2026-10-07 14:30:00 fromisoformat 2026-10-07 14:30:00+02:00
Para intercambiar datos, isoformat y fromisoformat: son rápidas, no dependen del idioma y lo que escribe una lo lee la otra. strftime y strptime, solo para hablar con personas o con formatos ajenos.
date.fromisoformat("2026-02-30")ValueError: day 30 must be in range 1..28 for month 2 in year 2026
Husos horarios
for z in ("Europe/Madrid", "America/Mexico_City", "Asia/Tokyo"):
print(mad.astimezone(ZoneInfo(z)))
salidaEurope/Madrid 2026-10-07 14:30:00+02:00 America/Mexico_City 2026-10-07 06:30:00-06:00 Asia/Tokyo 2026-10-07 21:30:00+09:00
El mismo instante, tres relojes. Y el horario de verano sale solo:
salidaMadrid en enero: 1:00:00 en octubre: 2:00:00
Europe/Madrid— y nunca desplazamientos fijos como +02:00: la región sabe cuándo cambia la hora y el desplazamiento no. En Windows, zoneinfo necesita el paquete tzdata, porque el sistema no trae la base de datos.El error clásico
salidaTypeError: can't subtract offset-naive and offset-aware datetimes
Mezclar un datetime con zona y otro sin ella no se puede, y es el fallo que más aparece al leer fechas de una base de datos o de una API. La regla que lo evita: con zona en cuanto entra, sin zona nunca. Y para el ahora, datetime.now(UTC), no datetime.utcnow(), que está obsoleto justo porque devolvía un naive.
| Quieres… | Usa |
|---|---|
| Hoy | date.today() |
| El instante actual | datetime.now(UTC) |
| Sumar días u horas | + timedelta(days=1) |
| Sumar meses | relativedelta, no timedelta |
| Intercambiar datos | isoformat / fromisoformat |
| Mostrar a una persona | strftime |
| Cambiar de huso | astimezone(ZoneInfo(...)) |
Todo el código se ejecutó con Python 3.14 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.