Tutoriales

Trabajando con Zonas Horarias en Python: Tutorial Completo

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)

from datetime import datetime

# Naive - sin información de zona horaria
dt_naive = datetime(2024, 1, 15, 12, 30, 0)
print(dt_naive.tzinfo)  # None

Datetime Aware (Con Zona Horaria)

from datetime import datetime, timezone

# Aware - incluye zona horaria
dt_aware = datetime(2024, 1, 15, 12, 30, 0, tzinfo=timezone.utc)
print(dt_aware.tzinfo)  # UTC

Regla de oro: Siempre trabaja con datetimes aware.

Paso 2: Trabajar con UTC

from datetime import datetime, timezone

# Hora actual en UTC
ahora_utc = datetime.now(timezone.utc)
print(ahora_utc)
# Salida: 2024-01-15 12:30:00+00:00

# Desde timestamp Unix
timestamp = 1705321800
dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
print(dt)
# Salida: 2024-01-15 12:30:00+00:00

Paso 3: Usar pytz

import pytz
from datetime import datetime

# Crear zona horaria
madrid = pytz.timezone('Europe/Madrid')
nueva_york = pytz.timezone('America/New_York')

# Hora actual en Madrid
ahora_madrid = datetime.now(madrid)
print(f"Madrid: {ahora_madrid}")

# Convertir UTC a Madrid
utc = pytz.UTC
ahora_utc = datetime.now(utc)
ahora_madrid = ahora_utc.astimezone(madrid)
print(f"Madrid desde UTC: {ahora_madrid}")

Advertencia con pytz

# ❌ INCORRECTO - no usar replace con pytz
dt = datetime(2024, 1, 15, 12, 0)
dt_mal = dt.replace(tzinfo=madrid)  # ¡No funciona correctamente!

# ✅ CORRECTO - usar localize
dt = datetime(2024, 1, 15, 12, 0)
dt_bien = madrid.localize(dt)

Paso 4: Usar zoneinfo (Python 3.9+)

from datetime import datetime
from zoneinfo import ZoneInfo

# Crear zona horaria
madrid = ZoneInfo('Europe/Madrid')
tokio = ZoneInfo('Asia/Tokyo')

# Hora actual en Madrid
ahora_madrid = datetime.now(madrid)
print(ahora_madrid)

# Convertir entre zonas
ahora_tokio = ahora_madrid.astimezone(tokio)
print(ahora_tokio)

Paso 5: Convertir Entre Zonas Horarias

from datetime import datetime
from zoneinfo import ZoneInfo

# Crear datetime en una zona
madrid = ZoneInfo('Europe/Madrid')
nueva_york = ZoneInfo('America/New_York')
tokio = ZoneInfo('Asia/Tokyo')

dt_madrid = datetime(2024, 1, 15, 14, 30, tzinfo=madrid)

# Convertir a otras zonas
dt_ny = dt_madrid.astimezone(nueva_york)
dt_tokio = dt_madrid.astimezone(tokio)

print(f"Madrid:     {dt_madrid}")
print(f"Nueva York: {dt_ny}")
print(f"Tokio:      {dt_tokio}")

Paso 6: Manejar DST (Horario de Verano)

from datetime import datetime
from zoneinfo import ZoneInfo

madrid = ZoneInfo('Europe/Madrid')

# Invierno (CET, UTC+1)
invierno = datetime(2024, 1, 15, 12, 0, tzinfo=madrid)
print(f"Invierno: {invierno} (offset: {invierno.strftime('%z')})")
# Salida: 2024-01-15 12:00:00+01:00

# Verano (CEST, UTC+2)
verano = datetime(2024, 7, 15, 12, 0, tzinfo=madrid)
print(f"Verano: {verano} (offset: {verano.strftime('%z')})")
# Salida: 2024-07-15 12:00:00+02:00

Paso 7: Almacenar y Recuperar Timestamps

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Siempre almacenar en UTC
def guardar_evento(dt_local, zona_usuario):
    """Convierte hora local a UTC para almacenar"""
    if dt_local.tzinfo is None:
        zona = ZoneInfo(zona_usuario)
        dt_local = dt_local.replace(tzinfo=zona)
    return dt_local.astimezone(timezone.utc)

# Mostrar en zona local del usuario
def mostrar_evento(dt_utc, zona_usuario):
    """Convierte UTC a hora local para mostrar"""
    zona = ZoneInfo(zona_usuario)
    return dt_utc.astimezone(zona)

# Ejemplo
hora_local = datetime(2024, 1, 15, 14, 30)
hora_utc = guardar_evento(hora_local, 'Europe/Madrid')
print(f"Almacenado: {hora_utc}")

hora_mostrar = mostrar_evento(hora_utc, 'America/New_York')
print(f"Mostrado en NY: {hora_mostrar}")

Errores Comunes a Evitar

1. Usar datetime.now() sin zona

# ❌ Incorrecto
ahora = datetime.now()

# ✅ Correcto
ahora = datetime.now(timezone.utc)

2. Comparar naive con aware

# ❌ Esto causa TypeError
naive = datetime(2024, 1, 15, 12, 0)
aware = datetime(2024, 1, 15, 12, 0, tzinfo=timezone.utc)
# naive < aware  # TypeError!

# ✅ Convertir primero
naive_utc = naive.replace(tzinfo=timezone.utc)
# naive_utc < aware  # Funciona

3. Ignorar DST en cálculos

from datetime import timedelta
from zoneinfo import ZoneInfo

# Durante cambio de DST, usar timedelta puede fallar
madrid = ZoneInfo('Europe/Madrid')

# ✅ 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!

Pruébalo

Prueba la conversión de timestamps

Resultado de fecha

¿Necesitas más opciones? Convertidor de zona horaria