Manipulação de datas com datetime e zoneinfo
1. Introdução ao módulo datetime
O módulo datetime é a biblioteca padrão do Python para manipulação de datas e horas. Suas principais classes são:
date: representa uma data (ano, mês, dia)time: representa um horário (hora, minuto, segundo, microssegundo)datetime: combina data e horatimedelta: representa uma diferença entre dois pontos no tempo
Para criar objetos datetime, temos duas abordagens principais:
from datetime import datetime, date, time
# Construtor explícito
data_hora = datetime(2024, 3, 15, 14, 30, 0)
print(data_hora) # 2024-03-15 14:30:00
# Data e hora atuais
agora = datetime.now()
print(agora) # 2024-03-15 14:30:45.123456 (exemplo)
# Apenas data
hoje = date.today()
print(hoje) # 2024-03-15
Os atributos podem ser acessados diretamente:
print(f"Ano: {agora.year}, Mês: {agora.month}, Dia: {agora.day}")
print(f"Hora: {agora.hour}, Minuto: {agora.minute}, Segundo: {agora.second}")
2. Operações com timedelta
O timedelta permite realizar operações aritméticas com datas:
from datetime import datetime, timedelta
hoje = datetime.now()
# Subtração de datas
ontem = hoje - timedelta(days=1)
print(f"Ontem: {ontem}")
# Adição de intervalos
daqui_uma_semana = hoje + timedelta(weeks=1)
print(f"Daqui uma semana: {daqui_uma_semana}")
# Criando timedelta personalizado
intervalo = timedelta(days=5, hours=3, minutes=30)
data_futura = hoje + intervalo
print(f"Daqui 5 dias, 3h e 30min: {data_futura}")
# Diferença entre duas datas
natal = datetime(hoje.year, 12, 25)
dias_restantes = natal - hoje
print(f"Faltam {dias_restantes.days} dias para o Natal")
Cuidado importante: timedelta não lida diretamente com meses ou anos, pois esses períodos não têm duração fixa. Para operações com meses, utilize bibliotecas como dateutil:
from dateutil.relativedelta import relativedelta
hoje = datetime.now()
proximo_mes = hoje + relativedelta(months=1)
print(f"Próximo mês: {proximo_mes}")
3. Formatação e parsing de datas
O método strftime() converte objetos datetime em strings formatadas:
from datetime import datetime
agora = datetime.now()
formatos = {
"completo": agora.strftime("%Y-%m-%d %H:%M:%S"),
"brasileiro": agora.strftime("%d/%m/%Y às %H:%M"),
"extenso": agora.strftime("%A, %d de %B de %Y"),
"apenas_data": agora.strftime("%d/%m/%Y"),
"hora_12h": agora.strftime("%I:%M %p"),
}
for nome, formato in formatos.items():
print(f"{nome}: {formato}")
O método strptime() faz o caminho inverso:
from datetime import datetime
# Conversão de string para datetime
data_str = "15/03/2024 14:30:00"
data_obj = datetime.strptime(data_str, "%d/%m/%Y %H:%M:%S")
print(f"Objeto datetime: {data_obj}")
# Tratamento de diferentes formatos
formatos_possiveis = [
"%Y-%m-%d",
"%d/%m/%Y",
"%d-%m-%Y",
]
def parse_data(texto):
for formato in formatos_possiveis:
try:
return datetime.strptime(texto, formato)
except ValueError:
continue
raise ValueError(f"Formato não reconhecido: {texto}")
print(parse_data("2024-03-15")) # 2024-03-15
print(parse_data("15/03/2024")) # 2024-03-15
4. Introdução ao módulo zoneinfo
O módulo zoneinfo, introduzido no Python 3.9, fornece suporte oficial para fusos horários usando o banco de dados IANA (Olson).
from zoneinfo import ZoneInfo
from datetime import datetime
# Criando datetime com fuso horário
sp = ZoneInfo("America/Sao_Paulo")
londres = ZoneInfo("Europe/London")
agora_sp = datetime.now(sp)
agora_londres = datetime.now(londres)
print(f"São Paulo: {agora_sp}")
print(f"Londres: {agora_londres}")
# Diferença entre UTC e horário local
utc = ZoneInfo("UTC")
agora_utc = datetime.now(utc)
print(f"UTC: {agora_utc}")
O banco de dados IANA contém milhares de fusos horários padronizados:
# Listando alguns fusos comuns
fusos = [
"America/Sao_Paulo",
"America/New_York",
"Europe/London",
"Asia/Tokyo",
"Australia/Sydney",
]
for fuso in fusos:
tz = ZoneInfo(fuso)
agora = datetime.now(tz)
print(f"{fuso}: {agora}")
5. Conversão entre fusos horários
O método astimezone() converte um datetime para outro fuso horário:
from zoneinfo import ZoneInfo
from datetime import datetime
# Criando datetime aware
sp = ZoneInfo("America/Sao_Paulo")
data_sp = datetime(2024, 3, 15, 14, 30, 0, tzinfo=sp)
print(f"Em SP: {data_sp}")
# Convertendo para Londres
londres = ZoneInfo("Europe/London")
data_londres = data_sp.astimezone(londres)
print(f"Em Londres: {data_londres}")
# Convertendo para Tóquio
toquio = ZoneInfo("Asia/Tokyo")
data_toquio = data_sp.astimezone(toquio)
print(f"Em Tóquio: {data_toquio}")
Diferença entre naive e aware:
from datetime import datetime, timezone
# Naive (sem fuso horário)
naive = datetime(2024, 3, 15, 14, 30)
print(f"Naive: {naive}, tzinfo: {naive.tzinfo}")
# Aware (com fuso horário)
aware = datetime(2024, 3, 15, 14, 30, tzinfo=timezone.utc)
print(f"Aware: {aware}, tzinfo: {aware.tzinfo}")
# Erro comum: tentar converter naive
try:
naive.astimezone(ZoneInfo("America/Sao_Paulo"))
except ValueError as e:
print(f"Erro: {e}")
Horário de verão:
from zoneinfo import ZoneInfo
from datetime import datetime
sp = ZoneInfo("America/Sao_Paulo")
# Durante horário de verão (outubro)
verao = datetime(2023, 10, 15, 14, 30, tzinfo=sp)
print(f"Verão: {verao}, UTC offset: {verao.utcoffset()}")
# Fora do horário de verão (junho)
inverno = datetime(2023, 6, 15, 14, 30, tzinfo=sp)
print(f"Inverno: {inverno}, UTC offset: {inverno.utcoffset()}")
6. Trabalhando com UTC e timestamps
Timestamps Unix representam segundos desde 01/01/1970:
from datetime import datetime, timezone
# Obtendo timestamp
agora = datetime.now(timezone.utc)
timestamp = agora.timestamp()
print(f"Timestamp Unix: {timestamp}")
# Convertendo timestamp para datetime
data_do_timestamp = datetime.fromtimestamp(timestamp, tz=timezone.utc)
print(f"De volta ao datetime: {data_do_timestamp}")
# Boa prática: armazenar em UTC
def salvar_data():
utc_now = datetime.now(timezone.utc)
return utc_now.isoformat()
def exibir_local(iso_string, fuso_local):
data_utc = datetime.fromisoformat(iso_string)
data_local = data_utc.astimezone(fuso_local)
return data_local.strftime("%d/%m/%Y %H:%M")
# Uso
from zoneinfo import ZoneInfo
data_para_banco = salvar_data()
print(f"Armazenado (UTC): {data_para_banco}")
print(f"Exibido (SP): {exibir_local(data_para_banco, ZoneInfo('America/Sao_Paulo'))}")
7. Casos especiais e boas práticas
Comparações entre objetos datetime:
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# Datas são imutáveis e comparáveis
data1 = datetime(2024, 3, 15, tzinfo=timezone.utc)
data2 = datetime(2024, 3, 16, tzinfo=timezone.utc)
print(f"data1 < data2: {data1 < data2}")
print(f"data1 == data2: {data1 == data2}")
# Comparação entre fusos diferentes funciona corretamente
sp = ZoneInfo("America/Sao_Paulo")
data_sp = datetime(2024, 3, 15, 20, 0, tzinfo=sp)
data_utc = datetime(2024, 3, 15, 23, 0, tzinfo=timezone.utc)
print(f"data_sp == data_utc: {data_sp == data_utc}") # True, mesmo horário
Dicas para projetos reais:
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# 1. Sempre armazene em UTC
def get_utc_now():
return datetime.now(timezone.utc)
# 2. Converta para fuso local apenas na exibição
def format_for_user(dt_utc, user_timezone="America/Sao_Paulo"):
tz = ZoneInfo(user_timezone)
local_dt = dt_utc.astimezone(tz)
return local_dt.strftime("%d/%m/%Y %H:%M:%S")
# 3. Para APIs, use ISO 8601
def to_iso_string(dt):
return dt.isoformat()
# 4. Valide datas recebidas
def parse_iso_date(iso_string):
try:
return datetime.fromisoformat(iso_string)
except ValueError:
raise ValueError(f"Data inválida: {iso_string}")
# Exemplo de uso
utc_now = get_utc_now()
print(f"UTC: {utc_now}")
print(f"Para usuário: {format_for_user(utc_now)}")
print(f"ISO: {to_iso_string(utc_now)}")
Performance: Para operações intensivas, considere usar datetime.timestamp() e trabalhar com números inteiros, que são mais rápidos que objetos datetime.
Referências
- Documentação oficial do módulo datetime — Referência completa de todas as classes e métodos do módulo datetime no Python.
- Documentação oficial do módulo zoneinfo — Guia completo sobre fusos horários com ZoneInfo e banco de dados IANA.
- PEP 615 – Support for the IANA Time Zone Database in the Standard Library — Proposta que introduziu o módulo zoneinfo na biblioteca padrão do Python.
- Real Python: Working with Dates and Times in Python — Tutorial abrangente sobre manipulação de datas com exemplos práticos e boas práticas.
- Python Timezone Conversion Guide — Guia prático sobre conversão entre fusos horários usando zoneinfo e pytz.
- Date and Time Format Codes (strftime) — Referência rápida e interativa de todos os códigos de formatação para strftime e strptime.
- IANA Time Zone Database — Banco de dados oficial de fusos horários mantido pela Internet Assigned Numbers Authority.