Whenever – biblioteca para processamento de data e hora com segurança de tipos e segurança contra DST para Python
(github.com/ariebovenberg)- Whenever é uma biblioteca de data e hora para Python que evita, por meio de tipos, a mistura entre
datetimenaive/aware e erros de cálculo com DST, podendo ser usada com implementação em Rust ou em Python puro - A biblioteca padrão
datetimenem sempre reflete o DST, e é apresentado o exemplo em quedatetime(2023, 3, 25, 22, tzinfo=ZoneInfo("Europe/Paris")) + timedelta(hours=8)retorna 6h, e não 7h, mesmo com uma hora tendo sido pulada por causa do DST - O sistema de tipos não consegue distinguir naive e aware usando apenas um
datetime, mas o Whenever separa tipos por finalidade comoInstant,ZonedDateTimeePlainDateTime, permitindo que o verificador de tipos capture erros - Na tabela de comparação, Arrow e Pendulum não conseguem atender simultaneamente a segurança contra DST, aware/naive tipado e desempenho, enquanto o Whenever afirma oferecer DST-safe, aware/naive tipado e fast
- Ainda está antes da versão 1.0, então a API pode mudar em releases minor; as mudanças são explicadas no changelog, e verificadores de tipos ou IDEs podem ajudar na adaptação
O problema que o Whenever quer resolver
- Whenever é uma biblioteca que ajuda a escrever código de data e hora correto e verificável por tipos em Python
- Oferece uma implementação em Rust e também uma versão em Python puro para quem não quer usar Rust
- Foi projetada com base em conceitos comprovados usados em bibliotecas modernas de data e hora de outras linguagens
- É apresentada como muito mais rápida que outras bibliotecas de terceiros e, em geral, até mais rápida que a biblioteca padrão
- O lançamento da versão 1.0 está adiado para refinar melhor a API de longo prazo, especialmente com feedback sobre duration
Limitações da biblioteca padrão datetime
- O
datetimedo Python evoluiu por mais de 20 anos, mas tem aspectos que não condizem com o esperado de bibliotecas modernas de data e hora -
Problema nos cálculos com DST
datetimenem sempre considera Daylight Saving Time (DST)- No código de exemplo, ao somar 8 horas a 22h de 25 de março de 2023 no fuso
Europe/Paris, o resultado deveria ser 7h porque uma hora foi pulada pelo DST, mas retorna 6h - Isso não é um bug, mas uma decisão de design para considerar DST apenas em cálculos que envolvem dois fusos horários
-
Problema na distinção de tipos naive/aware
- Apenas o tipo
datetimenão permite distinguir naive datetime e aware datetime no sistema de tipos - Uma assinatura como
def schedule_meeting(at: datetime) -> None: ...não deixa claro se espera um valor naive ou aware
- Apenas o tipo
Comparação com Arrow e Pendulum
- Pela tabela de comparação, o Whenever atende a DST-safe, aware/naive tipado e fast ao mesmo tempo
datetimeatende apenas ao item fast, e não atende a DST-safe nem aware/naive tipado- Arrow tenta oferecer uma API mais amigável que a da biblioteca padrão, mas não resolve os problemas centrais
- Mantém as mesmas armadilhas
- A decisão de reduzir tudo a um único tipo
arrow.Arrowtorna mais difícil para o verificador de tipos capturar erros
- Pendulum apresentou em 2016 melhor tratamento de DST e ganhos de desempenho, mas corrigiu apenas parte das armadilhas relacionadas a DST
- O desempenho se deteriorou bastante com o tempo
- Está em um longo período de estagnação de manutenção, com apenas dois releases nos últimos 4 anos, e ainda restam muitos issues graves e antigos
Principais recursos
- Suporta DST-safe arithmetic
- API com segurança de tipos para evitar bugs comuns
- Corrige problemas que Arrow e Pendulum não conseguiram resolver
- Baseia-se em conceitos comprovados e familiares
- Oferece alto desempenho
- Tem testes e documentação robustos
- Suporta aritmética de datas
- Suporta precisão de nanossegundos
- Oferece suporte a SQLAlchemy
- O suporte a Pydantic está em beta
- Oferece implementação em Rust e opção em Python puro
- O suporte a free-threading está em beta
- Suporta per-interpreter GIL
Fluxo de uso de exemplo
- Importa tipos explícitos como
Instant,ZonedDateTimeePlainDateTimepara representar casos de uso diferentes Instant.now()identifica um instante específico sem complexidade de fuso horário ou calendário- Converte o fuso horário explicitamente com
now.to_tz("Europe/Paris") PlainDateTime("2023-10-28 22:00")não se mistura acidentalmente com outros tipos- Ao chamar
add(hours=6), é emitido umNaiveArithmeticWarninginformando que o ajuste de hora local ignora o DST - É preciso assumir explicitamente um fuso horário com
assume_tz("Europe/Amsterdam")
- Ao chamar
- Ao executar
add(hours=6)emZonedDateTime, o resultado reflete a transição de DST e retorna2023-10-29 03:00:00+01:00[Europe/Amsterdam] - Suporta comparação, arredondamento e truncamento, parsing de formatos ISO8601·RFC3339·RFC2822 e formatação com padrão personalizado
- Quando necessário, é possível converter para o
datetimeda biblioteca padrão ou fazer o caminho inverso - O uso detalhado pode ser consultado em feature overview e API reference
Restrições e política de estabilidade
- O intervalo suportado vai do ano 1 ao 9999 no calendário gregoriano proléptico
- O deslocamento de fuso horário é limitado a inteiros em segundos, em consistência com o IANA TZ DB
- O Whenever segue semantic versioning
- Antes da versão 1.0, a API pode mudar em releases minor
- Mudanças incompatíveis são detalhadas no changelog
- Como a API é totalmente tipada, verificadores de tipos e IDEs podem ajudar na adaptação a mudanças
Licença e influências de design
- O Whenever é distribuído sob a MIT License
- Os binary wheels incluem dependências Rust com licenças permissivas semelhantes, como MIT e Apache-2.0
- Noda Time e Joda Time foram pioneiros na abordagem de tipos por conceito, e a hierarquia de tipos do Noda Time inspirou diretamente o design do Whenever
- Temporal inspirou o tratamento de casos complexos em torno de ambiguidades de DST e arredondamento
- O módulo
datetimedo Python é amplamente usado no processamento de baixo nível de data e hora na implementação em Python puro do Whenever - Algumas ideias de Jiff também foram incorporadas
- O gráfico comparativo de benchmark foi adaptado do projeto Ruff
Ainda não há comentários.