Jiff, biblioteca de data e hora para Rust
(github.com/BurntSushi)- Jiff é uma biblioteca de data e hora para Rust, com o objetivo de oferecer tipos primitivos de data e hora de alto nível, difíceis de usar incorretamente, e desempenho razoável
- Seus principais recursos são integração automática com a Time Zone Database, aritmética e arredondamento conscientes de DST, formatação e parsing sem perdas de datas e horas com fuso horário, além de suporte opcional a Serde
- O design da API foi fortemente influenciado pelo Temporal, proposta do TC39 para melhorar o tratamento de datas e horas em JavaScript
- O Jiff 1.0 originalmente tinha lançamento previsto para o verão de 2025, mas o cronograma já atrasou em relação a abril de 2026, e atualmente não há uma linha do tempo para o lançamento 1.0
- Desempenho é uma meta importante, mas compreensibilidade da API e correção têm prioridade; no Unix, segue uma política conservadora de dependências, mantendo intencionalmente 0 dependências
O modelo de data e hora oferecido pelo Jiff
- Jiff é uma biblioteca para lidar com datas e horas em Rust, focada em fornecer tipos primitivos de alto nível difíceis de usar incorretamente
- Principais recursos suportados
- Integração automática e fluida com a Time Zone Database
- Aritmética e arredondamento conscientes de DST
- Formatação e parsing sem perdas de datas e horas com fuso horário
- Suporte opcional a
serde
- O design da API foi fortemente influenciado pelo Temporal, proposta do TC39 para melhorar o tratamento de datas e horas em JavaScript
- A licença é dupla, MIT ou UNLICENSE
Documentação e exemplos de uso
- Documentação e materiais relacionados
- Documentação da API no docs.rs
- Documentos de comparação com
chrono,time,hifitimeeicu - Documento sobre a fundamentação do design da API do Jiff
- Documento de suporte a plataformas
- Changelog
- O exemplo básico faz parsing de uma string de horário em RFC 3339, converte para uma data e hora com fuso horário, soma uma duração e então imprime como string sem perdas
- Faz parsing de
"2024-07-11T01:14:00Z"comoTimestamp - Converte para o fuso horário
America/New_York - Soma
1.month().hours(2) - A string resultante é
"2024-08-10T23:14:00-04:00[America/New_York]" - O horário UTC em formato RFC 3339 é
"2024-08-11T03:14:00Z"
- Faz parsing de
Instalação e fluxo básico de execução
- O Jiff está no crates.io e pode ser usado adicionando
jiffàs dependências doCargo.tomlde um projeto Rust - O comando simples para adicionar é
cargo add jiff - Fluxo completo do exemplo no README
- Criar um novo projeto com
cargo new jiff-example - Adicionar a dependência com
cargo add jiff - Em
src/main.rs, arredondar o horário atual para segundos comZoned::now().round(Unit::Second)? - Executar com
cargo run
- Criar um novo projeto com
- A saída de exemplo mostra um formato com fuso horário incluído:
2024-07-10T19:54:20-04:00[America/New_York] - Na primeira execução, aparecem mais mensagens de compilação, mas em execuções posteriores não é preciso recompilar as dependências
Feature flags e planos para a versão 1.0
- O Jiff oferece várias features de crate para ajustar o suporte à biblioteca padrão do Rust, suporte a
serdee a inclusão ou não da Time Zone Database no binário - A lista completa de feature flags compatíveis está na seção “crate features” da documentação
- O plano original de lançamento do Jiff 1.0 era o verão de 2025, mas em abril de 2026 o cronograma já havia atrasado, e atualmente não há uma linha do tempo de lançamento
- Após o lançamento da 1.0, a intenção é manter a API por tempo indeterminado, com o objetivo de permitir que usuários usem o Jiff como uma base estável de API pública
- Mesmo após o lançamento do Jiff 1.0, o Jiff 0.2 deve receber atualizações de correção de bugs importantes por 1 ano
- Sem desenvolvimento ativo de novos recursos
- O plano é oferecer um período de transição porque muitos usuários já usam o Jiff 0.2
Desempenho e caminho avançado de formatação
- A meta de design mais importante é ser uma biblioteca de data e hora de alto nível difícil de usar incorretamente
- Desempenho é a segunda meta; busca-se desempenho razoável, embora possa haver áreas com espaço para melhorias
- Os benchmarks ficam no diretório
bench - Algumas áreas foram otimizadas, mas muitas ainda não foram
- Melhorias de desempenho são uma meta, mas em geral compreensibilidade da API e correção têm prioridade sobre desempenho
- Os submódulos do Jiff, especialmente
jiff::fmt, funcionam como uma rota de escape para APIs complexas que exigem mais controle- A implementação de
std::fmt::DisplaydeZonedpode dificultar a escrita em um buffer reutilizável jiff::fmt::temporal::DateTimePrinter::print_zonedpermite escrever umZonedem umaStringouVec<u8>existente
- A implementação de
Tratamento de fusos horários por plataforma
- O problema de suporte a plataformas em bibliotecas de data e hora está relacionado principalmente ao suporte a fusos horários
- Interações com plataformas tratadas pelo Jiff
- Como encontrar dados de transição para identificadores de fuso horário IANA, como
Antarctica/Troll - Como encontrar o fuso horário padrão do sistema atual
- Como encontrar dados de transição para identificadores de fuso horário IANA, como
- Os dados de transição de fusos horários dependem da IANA Time Zone Database
- No Unix, normalmente é usado
/usr/share/zoneinfo, e a configuração da variável de ambienteTZDIRtambém é respeitada - No Windows, uma cópia da Time Zone Database é automaticamente embutida na biblioteca compilada
- Métodos de detecção do fuso horário do sistema
- No Unix, lê
/etc/localtime - No Windows, lê o identificador de fuso horário específico do Windows com
GetDynamicTimeZoneInformatione o mapeia para um identificador de fuso horário IANA usando os dados XML CLDR do Unicode
- No Unix, lê
- Espera-se que o suporte a outras plataformas aumente com o tempo, mas plataformas raras difíceis de testar provavelmente dependerão de pull requests de contribuidores
Dependências e política de MSRV
- O fato de o Jiff ter 0 dependências no Unix é uma decisão de design intencional
- O critério para adicionar novas dependências ao Jiff, uma crate do ecossistema, é conservador
- Quando for de fato necessário para interagir com a plataforma
- Quando for necessário para interoperabilidade, por exemplo
serde
- Dependências para fins de interoperabilidade também são tratadas de forma conservadora; em geral, a intenção é depender apenas daquelas que tenham menos lançamentos com breaking changes que o Jiff
- Dividir o Jiff em várias crates poderia criar novas dependências, mas atualmente não há planos para isso
- A direção é manter pequeno o número de crates, e é citada a experiência de que há grande sobrecarga de manutenção ao gerenciar muitas fronteiras de API com semver
- A versão mínima suportada do compilador Rust do Jiff é MSRV 1.70.0
- O próprio Jiff e as dependências do Jiff dentro do repositório não usam uma versão de Rust que exija um compilador Rust lançado no último ano
- Crates dentro do repositório que dependem do Jiff, como
jiff-icu, podem usar uma MSRV mais recente, mas não exigirão uma MSRV mais recente que a crate com a qual se integram - A MSRV de dependências externas como
windows-syseportable-atomicnão é controlada pelo Jiff - Seguindo a prática do ecossistema Rust, o Jiff não considera aumento de MSRV uma alteração incompatível com semver, mas limita aumentos de MSRV a atualizações de minor version
1 comentários
Opiniões no Hacker News
No geral parece bom, mas a sintaxe ToSpan me incomodou um pouco
let span = 5.days().hours(8).minutes(1);Achei estranho só o primeiro número vir antes, e os demais entrarem como argumentos de função
Se não gostar, dá para escrever como
let span = Span::new().days(5).hours(8).minutes(1);, e como são só alguns caracteres a mais, não é ruimuse jiff::{Timestamp, ToSpan};, fiquei encucado com a parte em que"2024-07-11T01:14:00Z".parse()?é parseado como TimestampPelo que lembro, Rust usa algo parecido com interfaces em vez de classes, então fiquei curioso por que, ao importar a biblioteca, de repente a string ganha um método
parse(), e por que esse método de nome genérico cria um objetoTimestampTambém fiquei curioso se o tipo à esquerda determina o significado de
str.parse(), e o que acontece se uma biblioteca de datas e uma biblioteca de expressões Lisp ambas adicionaremparse()a stringsIsso acaba levantando várias perguntas, até por que não usar uma sintaxe como
Timestamp.parse(str)let span = 5.days() + 8.hours() + 1.minutes();A sintaxe original parece algo que Ruby faria
Ao ler o código, seria melhor ver primeiro que se está lidando com um span
let span = Span::days(5).hours(8).minutes(1);parece melhorJá vi muita gente subestimar a complexidade de bibliotecas de data/hora
Frases como “basta usar UTC/tempo Unix como representação interna”, “durações podem ser representadas em nanossegundos” e “basta usar offsets em vez de fusos horários” se repetem
Se você pensa assim, vale ler o documento de design do Jiff: https://github.com/BurntSushi/jiff/blob/master/DESIGN.md
Como é típico do trabalho do BurntSushi, é excelente e detalhado
A comparação com chrono, a biblioteca de data/hora de fato padrão em Rust, também vale a leitura: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
Elementos como aritmética de DST que continua correta mesmo após serialização/desserialização, durações arredondáveis, aritmética de calendário ciente de fuso horário e detecção de conflitos de fusos horários históricos tornam a biblioteca correta, útil e agradável de usar
chrono é uma biblioteca abrangente e “correta”, mas era rígida e não era fácil de usar
Eu já usava ripgrep antes de o Debian fornecer
rgpor padrão, e também usei JodaTime na época em que havia gente que achava o Eclipse melhor que o IntelliJ IDEAMas em nenhum lugar daquele documento há algo que refute “durações podem ser representadas em nanossegundos”
O usuário precisa ver o horário correto de acordo com fuso horário e DST, mas o programa normalmente não
Se a tarefa não envolve lidar diretamente com vários fusos horários, representar durações em milissegundos/nanossegundos é, em geral, muito seguro
O fato de humanos terem criado fusos horários e DST não muda o fenômeno físico de um relógio interno de CPU ticar bilhões de vezes por segundo
Basta olhar para os kernels de sistemas operacionais que não explodiram em metade do planeta alguns dias atrás para ver que há muitos lugares em que timeouts no código são expressos em milissegundos
Ao implementar um “cooldown de 30 segundos”, isso pode levar ao mal-entendido de que é preciso somar 30 segundos ao horário atual no fuso horário local, armazenar como uma string incluindo fuso, DST e formato 12/24 horas, e ainda verificar segundos intercalares
Basta usar um timeout/cooldown de 30 segundos expresso em milissegundos, e, mesmo que do ponto de vista do usuário ele ocorra em 29 segundos, não é preciso se preocupar com segundos intercalares
Parece uma biblioteca ótima
Fiquei curioso sobre por que a BurntSushi está criando uma nova biblioteca
Não mexi muito com tratamento de tempo em Rust, então queria saber se as bibliotecas existentes têm problemas de desempenho, se as APIs atuais são difíceis de usar, se é só por diversão ou se há algum outro motivo
A implementação da biblioteca padrão do Java não era boa, e o JodaTime surgiu para resolver isso
Depois, no Java 8, entrou uma nova API DateTime muito influenciada pelo JodaTime e, por estar na biblioteca padrão, pôde ser adotada de forma mais ampla por autores de bibliotecas
https://www.joda.org/joda-time/
https://www.baeldung.com/java-8-date-time-intro
https://github.com/BurntSushi/jiff/blob/master/DESIGN.md
https://github.com/BurntSushi/jiff/blob/master/COMPARE.md
Em resumo, é principalmente por causa da API e do suporte a fusos horários IANA
O estado das bibliotecas de calendário em Rust não é lá muito ideal
No Pandas existem
.tz_convert()e.tz_localize(), e conversão de fuso horário basicamente se resolve com issoA operação que tomo como referência é “obter em UTC a primeira hora de um dia CET/CEST para uma determinada data”, e no Pandas isso é muito simples
No Chrono, é preciso criar um
NaiveDate, convertê-lo paraDateTimee depois de novo paraDateTime, e não consegui encontrar um padrão consistente nessas conversõesÀs vezes é uma função membro, às vezes é um método estático do objeto de fuso horário
Espero que alguém corrija isso algum dia. O Jiff parece um passo na direção certa, mas a sintaxe às vezes é estranha, então eu gostaria de algo mais previsível
date(y, m, d).intz("Europe/Rome")?Se você quiser obter UTC a partir daí, basta adicionar
with_time_zone(TimeZone::UTC)Seria bom se você explicasse melhor como acha que o Jiff poderia melhorar e o que considera estranho na sintaxe
“Amor, acorda, saiu um novo BurntSushi”[0]
Falando sério, queria saber se alguém aqui que usa Rust sabe por que este crate não usa algo como
tracingTalvez seja pesado demais. Em termos de crates, ele tem quase metade do tamanho
logobviamente também é bom, e nem sei se rastrear até chamadas de operações de fuso horário é um caso de uso comum, mas fiquei curioso se havia algum motivo específico[0] https://knowyourmeme.com/memes/wake-up-babe
logcomo algo próximo do mínimo denominador comumEle também se integra bem com
tracing, então provavelmente foi por isso que o Jiff o usouTambém é algo que vem sendo usado desde os primórdios do crates.io
O Jiff não tem requisitos além de emitir mensagens em certos níveis de log
Claro que acho que
tracingtambém teria funcionado bemBurntSushi também é a pessoa que criou todo o ecossistema de regex do Rust
Seja em Rust ou em outros ecossistemas, o que mais sinto falta nas bibliotecas de tempo existentes é a falta de suporte a segundos intercalares
Normalmente é porque internamente elas usam timestamp UNIX em vez de TAI, e infelizmente esta biblioteca não é diferente
Parece haver uma forma de dar algum suporte via arquivos tzif, mas não é suporte de primeira classe
Há mais detalhes nesta issue relacionada do Jiff: https://github.com/BurntSushi/jiff/issues/7
Como timestamps UNIX não usam a definição de segundo do SI, mas sim 1/86000 do dia atual, nem todos os segundos UNIX têm a mesma duração, o que quebra cálculos corretos de duração
Entendo que tenha sido um compromisso para herdar compatibilidade com formas antigas de rastrear o tempo e tornar a formatação de calendário rápida, mas não acho que tenha sido o compromisso certo
Timestamps UNIX misturam preocupações de representação com os dados
Acho que segundos intercalares deveriam ser tratados exatamente da mesma forma que 29 de fevereiro ou fusos horários
hifitimenão serviria?Sempre que segundos intercalares entram na conversa, parece que ela geralmente fica no abstrato
É muito raro que isso se conecte a casos de uso do mundo real
Entendo os casos de uso científicos, e acho que bibliotecas especializadas lidam bem com isso
Fora isso, não sei muito bem se há necessidade de mais alguma coisa
Jiffé pronunciado com G duro, como “Giff”goou o G degin“Jiff é pronunciado como
gifcom g suave, como emgem.”De jeito nenhum!
Vou ficar no chrono †
† Claro, pronunciado como “trono”, com
chcomo emchaseEntão se pronuncia jiff, ou se pronuncia gif?
“Jiff se pronuncia
gif, com o g suave como emgem.”Ah, entendi qual é a brincadeira…
Segundo o criador, GIF sempre foi pronunciado “jif”, então, para evitar confusão, seria bom se o criador dessa nova ferramenta pronunciasse Jiff como “gif”