---
title: "Datas e horas em Python com datetime e zoneinfo — 2026"
url: "https://python.dev.br/blog/datas-e-horas-python-datetime-zoneinfo/"
markdown_url: "https://python.dev.br/blog/datas-e-horas-python-datetime-zoneinfo.MD"
description: "Aprenda a trabalhar com datas e horas em Python: criar, formatar, comparar e somar períodos com datetime e timedelta, e lidar com fuso horário brasileiro usando zoneinfo. Guia prático em português."
date: "2026-07-05"
author: "Equipe Python Brasil"
---

# Datas e horas em Python com datetime e zoneinfo — 2026

Aprenda a trabalhar com datas e horas em Python: criar, formatar, comparar e somar períodos com datetime e timedelta, e lidar com fuso horário brasileiro usando zoneinfo. Guia prático em português.


Trabalhar com **datas e horas** é uma das tarefas mais comuns em qualquer programa Python: registrar quando um pedido foi feito, calcular a validade de um produto, agendar o envio de um relatório, filtrar vendas do último mês ou exibir um carimbo de data/hora no fuso de Brasília. Apesar de universal, é também uma das fontes mais frequentes de bugs — geralmente por causa de fuso horário, formato ou de tentar misturar datas "ingênuas" com datas "conscientes".

O Python resolve boa parte disso na própria biblioteca padrão, com os módulos `datetime` e, a partir da versão 3.9, `zoneinfo`. Neste tutorial você vai aprender a criar, formatar, comparar e somar períodos, e — o mais importante para quem desenvolve para o mercado brasileiro — a lidar corretamente com o fuso horário de Brasília. Se você está começando agora, vale revisar antes o nosso guia de [Python para iniciantes](/blog/python-para-iniciantes-guia-completo/); aqui o foco é um tópico específico, mas fundamental, da linguagem.

## Os objetos básicos: date, time e datetime

O módulo `datetime` oferece três tipos principais. O `date` representa apenas uma data (dia, mês, ano); o `time` representa apenas um horário (hora, minuto, segundo, microssegundo); e o `datetime` junta os dois. Para a maioria das aplicações do dia a dia, o `datetime` é o que você vai usar:

```python
from datetime import date, time, datetime

hoje = date.today()               # 2026-07-05
agora = datetime.now()            # 2026-07-05 14:30:00.123456
horario = time(9, 0, 0)           # 09:00:00

print(hoje, agora, horario)
```

Repare que `date.today()` devolve só a data, enquanto `datetime.now()` traz data e hora. Escolha o tipo certo para o seu problema: guardar a data de nascimento de um cliente pede um `date`; registrar o instante exato de uma transação pede um `datetime`.

## Criando datas e horas manualmente

Você pode construir datas informando ano, mês e dia diretamente. É assim que você converte um valor vindo de um formulário ou de um banco de dados:

```python
from datetime import date, datetime

aniversario = date(2026, 12, 25)
pagamento = datetime(2026, 7, 5, 10, 0, 0)  # 5 de julho de 2026, 10h00

# A partir de uma string ISO 8601 (formato recomendado para troca de dados)
data_iso = date.fromisoformat("2026-07-05")
dt_iso = datetime.fromisoformat("2026-07-05T10:00:00")

# A partir de um timestamp Unix (segundos desde 01/01/1970)
import time as _time
ts = _time.time()
data_ts = datetime.fromtimestamp(ts)
```

O formato ISO 8601 (`AAAA-MM-DD`) é o padrão internacional e o mais seguro para gravar e trocar datas entre sistemas. Guarde sempre assim no banco de dados; deixe a formatação brasileira (`dd/mm/aaaa`) apenas para a tela.

## Formatando datas no padrão brasileiro

Para mostrar uma data ao usuário, use o método `strftime` (string format time) com os códigos de formatação. O padrão brasileiro usa dia, mês e ano com barras:

```python
from datetime import datetime

agora = datetime.now()

print(agora.strftime("%d/%m/%Y"))        # 05/07/2026
print(agora.strftime("%d/%m/%Y %H:%M"))  # 05/07/2026 14:30
print(agora.strftime("%d de %B de %Y"))  # 05 de July de 2026 (problema!)
```

Atenção ao último exemplo: por padrão, o nome do mês sai em inglês (`July`). Para ter `05 de julho de 2026` você precisa definir o locale do sistema, o que é frágil entre sistemas operacionais. A abordagem mais robusta, e a que recomendamos para sites brasileiros, é montar o texto à mão usando uma lista de meses em português:

```python
meses = ["janeiro", "fevereiro", "março", "abril", "maio", "junho",
         "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"]

texto = f"{agora.day} de {meses[agora.month - 1]} de {agora.year}"
print(texto)  # 5 de julho de 2026
```

Assim o resultado não depende do sistema operacional nem de variáveis de ambiente — útil em containers Docker, onde o locale quase nunca está configurado.

## Convertendo strings em datas com strptime

O caminho inverso — transformar uma string em data — usa `strptime` (string parse time). É essencial ao ler planilhas, [arquivos CSV](/blog/automatizacao-com-python/) ou respostas de APIs em que a data vem como texto:

```python
from datetime import datetime

texto = "05/07/2026 14:30"
data = datetime.strptime(texto, "%d/%m/%Y %H:%M")
print(data)  # 2026-07-05 14:30:00

# Apenas data
data_pura = datetime.strptime("25/12/2026", "%d/%m/%Y").date()
```

Os códigos precisam casar exatamente com o formato da string, incluindo barras, espaços e dois-pontos. Se a string não bater, o Python levanta `ValueError` — por isso envolva essa conversão em um bloco `try`/`except`, conforme mostramos no nosso guia de [tratamento de erros em Python](/blog/tratamento-de-erros-python/).

## Operações com datas usando timedelta

Para somar ou subtrair períodos, use `timedelta`, que representa uma duração (dias, segundos, microssegundos). Subtrair duas datas também devolve um `timedelta`:

```python
from datetime import date, timedelta

hoje = date.today()
vencimento = date(2026, 7, 20)

dias_restantes = vencimento - hoje       # timedelta
print(dias_restantes.days)               # número de dias até o vencimento

# Adicionar e remover períodos
daqui_uma_semana = hoje + timedelta(weeks=1)
ha_30_dias = hoje - timedelta(days=30)
print(daqui_uma_semana, ha_30_dias)
```

`timedelta` cobre a maioria dos cálculos de prazo. Para operações mais complexas — como "o mesmo dia do mês que vem" ou "o último dia útil" — a biblioteca de terceiros `python-dateutil` oferece o `relativedelta`, que entende meses e anos (unidades que o `timedelta` não suporta diretamente).

## Fuso horário com zoneinfo

Até aqui, todas as nossas datas eram **ingênuas** (naive): não carregam informação de fuso horário. Em aplicações que rodam em servidores na nuvem, recebem webhooks internacionais ou atendem usuários em fusos diferentes, datas ingênuas causam bugs difíceis de rastrear. A partir do Python 3.9, o módulo `zoneinfo` resolve isso de forma nativa:

```python
from datetime import datetime
from zoneinfo import ZoneInfo

# Horário atual no fuso de Brasília (UTC-3)
agora_sp = datetime.now(ZoneInfo("America/Sao_Paulo"))
print(agora_sp)  # 2026-07-05 14:30:00-03:00

# O sufixo -03:00 indica que a data é "consciente" (aware)
# Converter entre fusos é direto:
agora_noronha = agora_sp.astimezone(ZoneInfo("America/Noronha"))
print(agora_noronha)  # 2026-07-05 14:30:00-02:00 (UTC-2)
```

A regra de ouro do fuso brasileiro: o Brasil usa vários fusos, mas o principal é o de Brasília, representado por `America/Sao_Paulo` (UTC-3). Outros comuns são `America/Manaus` (UTC-4), `America/Rio_Branco` (UTC-5, o Acre) e `America/Noronha` (UTC-2). **O horário de verão brasileiro foi extinto em 2019**, então o país mantém o horário padrão o ano inteiro — a base de dados de fusos já reflete isso, e você não precisa se preocupar com ajustes sazonais.

> **Dica de produção:** em Windows, instale o pacote `tzdata` (`pip install tzdata`) para garantir regras de fuso atualizadas, já que o sistema não fornece a base tz por padrão. Em Linux e macOS, as regras vêm do próprio sistema operacional.

## O erro mais comum: misturar datas ingênuas e conscientes

O bug clássico de datas em Python acontece quando você compara ou subtrai uma data com fuso e outra sem:

```python
from datetime import datetime
from zoneinfo import ZoneInfo

ingenua = datetime.now()                              # sem fuso
consciente = datetime.now(ZoneInfo("America/Sao_Paulo"))  # com fuso

# A linha abaixo levanta TypeError: can't compare offset-naive
# and offset-aware datetimes
# if ingenua > consciente:
#     print("maior")
```

A correção é padronizar todo o código em um dos dois modos. A recomendação moderna é **trabalhar sempre com datas conscientes** em código de produção, criando-as com `datetime.now(ZoneInfo("America/Sao_Paulo"))`. Se precisar converter uma data ingênua para consciente, use `.replace(tzinfo=...)` ou `.astimezone()`:

```python
# Localizar uma data ingênua assumindo o fuso de Brasília
ingenua_localizada = ingenua.replace(tzinfo=ZoneInfo("America/Sao_Paulo"))
print(ingenua_localizada > consciente)  # agora funciona
```

## Mini-projeto: calculando dias até o vencimento de uma conta

Vamos juntar tudo em um exemplo realista — uma função que recebe a data de vencimento de uma conta e devolve quantos dias faltam, com aviso de atraso:

```python
from datetime import date

def status_vencimento(vencimento: date) -> str:
    hoje = date.today()
    diff = (vencimento - hoje).days
    if diff > 0:
        return f"Faltam {diff} dias para o vencimento."
    elif diff == 0:
        return "O vencimento é hoje."
    else:
        return f"Conta vencida há {abs(diff)} dias."

print(status_vencimento(date(2026, 7, 20)))  # "Faltam N dias..."
print(status_vencimento(date(2026, 6, 30)))  # "Conta vencida há N dias..."
```

Repare em três decisões importantes: usei `date` (e não `datetime`) porque o vencimento não tem hora; subtrair datas já devolve um `timedelta`, do qual extraio `.days`; e usei `abs()` para inverter o sinal na mensagem de atraso. Esse é o tipo de lógica que aparece em [automações com Python](/blog/automatizacao-com-python/) para finanças, relatórios e cobranças.

## Próximos passos

Para evoluir depois deste tutorial:

1. **Automatize com datas** — combine `datetime` com agendamento para gerar relatórios diários ou enviar lembretes de vencimento.
2. **Valide entradas** — use `try`/`except` em volta de `strptime` e gere datas com `pydantic` quando os dados vêm de APIs; veja nosso guia de [validação de dados com Pydantic](/blog/pydantic-validacao-dados-python/).
3. **Teste fusos** — escreva testes que cobrem a conversão entre `America/Sao_Paulo` e UTC, seguindo as práticas do nosso guia de [testes unitários em Python](/blog/testes-unitarios-python/).

Datas e horas parecem triviais até a primeira vez que um fuso horário quebra uma cobrança no fim do mês. Trabalhe sempre com datas conscientes, guarde tudo em ISO 8601 e formate para o padrão brasileiro apenas na hora de exibir. Com esses três hábitos, a maioria dos bugs de data desaparece. Para acompanhar as oportunidades do mercado de desenvolvimento Python no Brasil, confira nossas [vagas de Python](/vagas/) e o [guia de primeiro emprego como programador Python](/carreira/primeiro-emprego-python/).
