# 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. ![El laboratorio mágico de un relojero](/media/editor_temp/6/5843a4cb-f3a6-4d8d-9a13-89c1cbb0ef6c.png) 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`? {#sec-5f3cbdfb3d40} 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`, lo que facilita el manejo de zonas horarias locales (ej.: Asia/Tokyo). --- ## 2. Obtener el "ahora": desde naive hasta aware {#sec-80466bd4f739} ### 2.1 ¿Qué son naive y aware? {#sec-893b0793d467} 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 {#sec-f876fdec8f2b} Unificar la base de cálculo y almacenamiento en UTC suele simplificar mucho las cosas. ```python 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. ```python 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` {#sec-5497d843fd3e} ### 3.1 Sumar y restar {#sec-0288780aa297} ```python 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 {#sec-c781f333910b} ```python 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" {#sec-c81173d2a4f5} `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` {#sec-a17a8e259a48} ### 4.1 `datetime` → cadena (`strftime`) {#sec-8d6f34025b2e} ```python 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`) {#sec-f2bbc1672485} ```python 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`. ```python 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, usa `astimezone()` (ver siguiente sección). --- ## 5. Zonas horarias: diferenciar `timezone` y `zoneinfo` {#sec-05b1d0f7ef82} ### 5.1 Desplazamiento fijo → `timezone` {#sec-67fec65074ab} Ej.: UTC, UTC+9 con desplazamiento constante. ```python 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` {#sec-834b663bce83} Para zonas con horario de verano, `zoneinfo` es la opción adecuada. ```python 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(...)` {#sec-35bf0fbaa9df} * `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 reduce muchos errores de zona horaria. --- ## 6. Puntos de atención frecuentes {#sec-552ca3933648} ### 6.1 Mezcla de naive/aware {#sec-21a4c96b9022} * 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 {#sec-e2bd08b413c3} `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 {#sec-e52b753858ac} `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 {#sec-b244af674a6a} ### 7.1 Guardar en formato ISO 8601 {#sec-4b1979b29466} Para una representación estándar, `isoformat()` es útil. ```python 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 {#sec-ae13a5dac423} ```python 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 {#sec-b509d1188ff7} ```python 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 {#sec-9fa53305f95a} `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](/ko/whitedec/2025/11/10/django-datetime-timezone/) - [Manejo de tiempo en Django – guía completa de `django.utils.timezone`](/ko/whitedec/2025/11/14/django-utils-timezone-guide/) --- **Ver series anteriores** - [[Python Standard Library – 0] ¿Qué es la biblioteca estándar de Python? Guía para principiantes](/ko/whitedec/2026/1/29/python-standard-library/) - [[Python Standard Library –1] Dominando el sistema de archivos y entorno OS: pathlib vs os](/ko/whitedec/2026/1/29/file-system-os-environment-master-pathlib-vs-os/) - [[Python Standard Library – 2] Almacenamiento y serialización de datos: json, pickle, csv](/ko/whitedec/2026/1/30/python-json-pickle-csv/)