Introducción
Manejar zonas horarias en Python puede ser complicado, pero es esencial para aplicaciones que trabajan con usuarios de todo el mundo. Este tutorial te guiará a través de las mejores prácticas para trabajar con zonas horarias en Python.
Lo que Aprenderás
- ✅ Diferencia entre datetime naive y aware
- ✅ Usar pytz para zonas horarias
- ✅ Usar zoneinfo (Python 3.9+)
- ✅ Convertir entre zonas horarias
- ✅ Manejar horario de verano (DST)
- ✅ Mejores prácticas y errores comunes
Prerrequisitos
- Python 3.6 o superior
- Conocimiento básico de datetime
Paso 1: Entender Naive vs Aware
Datetime Naive (Sin Zona Horaria)
PYTHON1from datetime import datetime 2 3# Naive - sin información de zona horaria 4dt_naive = datetime(2024, 1, 15, 12, 30, 0) 5print(dt_naive.tzinfo) # None
Datetime Aware (Con Zona Horaria)
PYTHON1from datetime import datetime, timezone 2 3# Aware - incluye zona horaria 4dt_aware = datetime(2024, 1, 15, 12, 30, 0, tzinfo=timezone.utc) 5print(dt_aware.tzinfo) # UTC
Regla de oro: Siempre trabaja con datetimes aware.
Paso 2: Trabajar con UTC
PYTHON1from datetime import datetime, timezone 2 3# Hora actual en UTC 4ahora_utc = datetime.now(timezone.utc) 5print(ahora_utc) 6# Salida: 2024-01-15 12:30:00+00:00 7 8# Desde timestamp Unix 9timestamp = 1705321800 10dt = datetime.fromtimestamp(timestamp, tz=timezone.utc) 11print(dt) 12# Salida: 2024-01-15 12:30:00+00:00
Paso 3: Usar pytz
PYTHON1import pytz 2from datetime import datetime 3 4# Crear zona horaria 5madrid = pytz.timezone('Europe/Madrid') 6nueva_york = pytz.timezone('America/New_York') 7 8# Hora actual en Madrid 9ahora_madrid = datetime.now(madrid) 10print(f"Madrid: {ahora_madrid}") 11 12# Convertir UTC a Madrid 13utc = pytz.UTC 14ahora_utc = datetime.now(utc) 15ahora_madrid = ahora_utc.astimezone(madrid) 16print(f"Madrid desde UTC: {ahora_madrid}")
Advertencia con pytz
PYTHON1# ❌ INCORRECTO - no usar replace con pytz 2dt = datetime(2024, 1, 15, 12, 0) 3dt_mal = dt.replace(tzinfo=madrid) # ¡No funciona correctamente! 4 5# ✅ CORRECTO - usar localize 6dt = datetime(2024, 1, 15, 12, 0) 7dt_bien = madrid.localize(dt)
Paso 4: Usar zoneinfo (Python 3.9+)
PYTHON1from datetime import datetime 2from zoneinfo import ZoneInfo 3 4# Crear zona horaria 5madrid = ZoneInfo('Europe/Madrid') 6tokio = ZoneInfo('Asia/Tokyo') 7 8# Hora actual en Madrid 9ahora_madrid = datetime.now(madrid) 10print(ahora_madrid) 11 12# Convertir entre zonas 13ahora_tokio = ahora_madrid.astimezone(tokio) 14print(ahora_tokio)
Paso 5: Convertir Entre Zonas Horarias
PYTHON1from datetime import datetime 2from zoneinfo import ZoneInfo 3 4# Crear datetime en una zona 5madrid = ZoneInfo('Europe/Madrid') 6nueva_york = ZoneInfo('America/New_York') 7tokio = ZoneInfo('Asia/Tokyo') 8 9dt_madrid = datetime(2024, 1, 15, 14, 30, tzinfo=madrid) 10 11# Convertir a otras zonas 12dt_ny = dt_madrid.astimezone(nueva_york) 13dt_tokio = dt_madrid.astimezone(tokio) 14 15print(f"Madrid: {dt_madrid}") 16print(f"Nueva York: {dt_ny}") 17print(f"Tokio: {dt_tokio}")
Paso 6: Manejar DST (Horario de Verano)
PYTHON1from datetime import datetime 2from zoneinfo import ZoneInfo 3 4madrid = ZoneInfo('Europe/Madrid') 5 6# Invierno (CET, UTC+1) 7invierno = datetime(2024, 1, 15, 12, 0, tzinfo=madrid) 8print(f"Invierno: {invierno} (offset: {invierno.strftime('%z')})") 9# Salida: 2024-01-15 12:00:00+01:00 10 11# Verano (CEST, UTC+2) 12verano = datetime(2024, 7, 15, 12, 0, tzinfo=madrid) 13print(f"Verano: {verano} (offset: {verano.strftime('%z')})") 14# Salida: 2024-07-15 12:00:00+02:00
Paso 7: Almacenar y Recuperar Timestamps
PYTHON1from datetime import datetime, timezone 2from zoneinfo import ZoneInfo 3 4# Siempre almacenar en UTC 5def guardar_evento(dt_local, zona_usuario): 6 """Convierte hora local a UTC para almacenar""" 7 if dt_local.tzinfo is None: 8 zona = ZoneInfo(zona_usuario) 9 dt_local = dt_local.replace(tzinfo=zona) 10 return dt_local.astimezone(timezone.utc) 11 12# Mostrar en zona local del usuario 13def mostrar_evento(dt_utc, zona_usuario): 14 """Convierte UTC a hora local para mostrar""" 15 zona = ZoneInfo(zona_usuario) 16 return dt_utc.astimezone(zona) 17 18# Ejemplo 19hora_local = datetime(2024, 1, 15, 14, 30) 20hora_utc = guardar_evento(hora_local, 'Europe/Madrid') 21print(f"Almacenado: {hora_utc}") 22 23hora_mostrar = mostrar_evento(hora_utc, 'America/New_York') 24print(f"Mostrado en NY: {hora_mostrar}")
Errores Comunes a Evitar
1. Usar datetime.now() sin zona
PYTHON1# ❌ Incorrecto 2ahora = datetime.now() 3 4# ✅ Correcto 5ahora = datetime.now(timezone.utc)
2. Comparar naive con aware
PYTHON1# ❌ Esto causa TypeError 2naive = datetime(2024, 1, 15, 12, 0) 3aware = datetime(2024, 1, 15, 12, 0, tzinfo=timezone.utc) 4# naive < aware # TypeError! 5 6# ✅ Convertir primero 7naive_utc = naive.replace(tzinfo=timezone.utc) 8# naive_utc < aware # Funciona
3. Ignorar DST en cálculos
PYTHON1from datetime import timedelta 2from zoneinfo import ZoneInfo 3 4# Durante cambio de DST, usar timedelta puede fallar 5madrid = ZoneInfo('Europe/Madrid') 6 7# ✅ Mejor: trabajar en UTC y luego convertir
Resumen
Has aprendido cómo:
- Diferenciar entre datetime naive y aware
- Usar pytz y zoneinfo para zonas horarias
- Convertir entre diferentes zonas horarias
- Manejar DST correctamente
- Almacenar siempre en UTC y mostrar en hora local
¡Prueba nuestro Convertidor de Zona Horaria para practicar conversiones!