2 pontos por GN⁺ 2024-07-23 | 1 comentários | Compartilhar no WhatsApp
  • 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, hifitime e icu
    • 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" como Timestamp
    • 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"

Instalação e fluxo básico de execução

  • O Jiff está no crates.io e pode ser usado adicionando jiff às dependências do Cargo.toml de 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 com Zoned::now().round(Unit::Second)?
    • Executar com cargo run
  • 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 serde e 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

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
  • 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 ambiente TZDIR també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 GetDynamicTimeZoneInformation e o mapeia para um identificador de fuso horário IANA usando os dados XML CLDR do Unicode
  • 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-sys e portable-atomic nã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

 
GN⁺ 2024-07-23
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 é ruim

    • Depois de use jiff::{Timestamp, ToSpan};, fiquei encucado com a parte em que "2024-07-11T01:14:00Z".parse()? é parseado como Timestamp
      Pelo 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 objeto Timestamp
      També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 adicionarem parse() a strings
      Isso acaba levantando várias perguntas, até por que não usar uma sintaxe como Timestamp.parse(str)
    • Concordo. Pessoalmente, prefiro let span = 5.days() + 8.hours() + 1.minutes();
    • Gosto dessa versão porque parece consistente
      A sintaxe original parece algo que Ruby faria
    • Concordo. Pelo fluxo de raciocínio, eu gostaria de especificar primeiro o que estou fazendo e depois colocar os detalhes, mas essa sintaxe faz o contrário
      Ao ler o código, seria melhor ver primeiro que se está lidando com um span
    • Indo além, let span = Span::days(5).hours(8).minutes(1); parece melhor
  • Já 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

    • Gosto do ripgrep do BurntSushi e o uso o tempo todo chamando diretamente pelo Emacs
      Eu já usava ripgrep antes de o Debian fornecer rg por padrão, e também usei JodaTime na época em que havia gente que achava o Eclipse melhor que o IntelliJ IDEA
      Mas 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
    • Só ao ver “funciona mesmo passando por ser/de” é que percebi por que o crate Serde tem esse nome
    • Obrigado por indicar o documento de design. Na primeira passada pelo repositório eu tinha deixado passar, mas é um documento bem escrito e esclareceu muitas das minhas dúvidas
    • Se quiser ver a loucura de data/hora de um jeito divertido e acessível, a apresentação de Kip Cole na ElixirConf 2022 é boa: https://www.youtube.com/watch?v=4VfPvCI901c
    • Quem acha que data/hora é fácil não deve ser deixado perto de código de agendamento ou processamento de datas
  • 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

  • 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 isso
    A 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 para DateTime e depois de novo para DateTime, 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

    • Isso pode ser feito com 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 tracing
    Talvez seja pesado demais. Em termos de crates, ele tem quase metade do tamanho
    log obviamente 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

    • Vejo log como algo próximo do mínimo denominador comum
      Ele também se integra bem com tracing, então provavelmente foi por isso que o Jiff o usou
      També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 tracing também teria funcionado bem
  • BurntSushi também é a pessoa que criou todo o ecossistema de regex do Rust

    • Lembro de estar discutindo algo no Reddit e, quando percebi com quem eu estava falando, pensei “deixa pra lá”
  • 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

    • Fico curioso sobre por que isso é necessário. Qual é o caso de uso real, e por que uma biblioteca científica especializada como hifitime nã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”

    • Não sou falante nativo; o que exatamente significa hard G? Estou curioso se é o G de go ou o G de gin
  • “Jiff é pronunciado como gif com g suave, como em gem.”
    De jeito nenhum!

    • Tivemos um bom momento chegando ao 1º lugar no Hacker News por um instante, mas agora parece que uma falha fatal foi revelada
      Vou ficar no chrono †
      † Claro, pronunciado como “trono”, com ch como em chase
    • https://x.com/drgonzo123/status/527520297512345600
    • Estranho, achei que Jiff fosse pronunciado com “J” duro
    • Quer dizer que não se pronuncia GIF, e sim GIF?!
  • Então se pronuncia jiff, ou se pronuncia gif?

    • https://github.com/BurntSushi/jiff/blob/master/DESIGN.md#why...
      “Jiff se pronuncia gif, com o g suave como em gem.”
      Ah, entendi qual é a brincadeira…
    • Acho que quem decide a pronúncia é quem criou
      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”
    • Como é uma biblioteca de tratamento de tempo, eu vou simplesmente chamá-la de biff