Trabalho Avançado com Datas e Horas em Rust: de NaiveDateTime a Timezone
Trabalhar com datas e horas é uma das tarefas mais comuns no desenvolvimento backend, logging, sistemas financeiros e processamento de dados. Em Rust, a biblioteca padrão fornece o módulo básico std::time, mas para projetos sérios ele não é suficiente. Em um nível avançado, recorremos à crate chrono — o padrão de facto para trabalhar com datas e horas. Neste artigo, abordaremos cenários complexos: trabalho com fusos horários, parsing de formatos não padronizados, manipulações de durações e otimização de desempenho.
1. Instalação e estrutura básica do chrono
Adicione a dependência no Cargo.toml:
[dependencies]chrono = { version = "0.4", features = ["serde"] }Principais tipos do chrono para trabalho avançado:
NaiveDateTime— data-hora sem fuso horário (local).DateTime<Tz>— data-hora vinculada a um fuso horário (ex.:Utc,Local,FixedOffset).Duration— intervalo de tempo (diferente destd::time::Duration, suporta valores negativos).TimeDelta— sinônimo paraDurationem versões mais recentes.
Exemplo de criação do momento atual em UTC e hora local:
use chrono::{Utc, Local, DateTime};
fn main() { let utc_now: DateTime<Utc> = Utc::now(); let local_now: DateTime<Local> = Local::now(); println!("UTC: {}", utc_now); println!("Local: {}", local_now);}2. Trabalho com fusos horários (Timezone)
Um dos recursos poderosos do chrono é o suporte ao banco de dados de fusos horários Olson através da crate chrono-tz. Isso permite trabalhar com nomes de zonas como "Europe/Moscow" ou "America/New_York".
Instale:
[dependencies]chrono = "0.4"chrono-tz = "0.8"Exemplo de conversão de horário entre zonas:
use chrono::{Utc, DateTime, TimeZone};use chrono_tz::Europe::Moscow;
fn main() { let utc_time: DateTime<Utc> = Utc::now(); let moscow_time = utc_time.with_timezone(&Moscow); println!("UTC: {}", utc_time); println!("Moscow: {}", moscow_time);}Ponto importante: sempre armazene o horário em UTC no banco de dados e converta para o horário local apenas ao exibir para o usuário. Isso evita confusão com horário de verão/inverno.
2.1. Trabalho com FixedOffset
Se você conhece um deslocamento fixo (ex.: +03:00), use FixedOffset:
use chrono::{FixedOffset, DateTime, Utc};
fn main() { let offset = FixedOffset::east_opt(3 * 3600).unwrap(); // +03:00 let now_utc: DateTime<Utc> = Utc::now(); let now_offset = now_utc.with_timezone(&offset); println!("Com offset +03:00: {}", now_offset);}3. Parsing e formatação de datas (técnicas avançadas)
O parsing padrão via parse_from_str exige um formato exato. Mas em projetos reais, os formatos podem variar (ex.: logs de sistemas diferentes). Vamos considerar algumas abordagens.
3.1. Múltiplos formatos com try_parse
Use a iteração sobre formatos:
use chrono::NaiveDateTime;
fn parse_flexible(date_str: &str) -> Option<NaiveDateTime> { let formats = [ "%Y-%m-%d %H:%M:%S", "%d/%m/%Y %H:%M", "%Y%m%dT%H%M%S", "%b %d %Y %I:%M:%S%p", ];
for fmt in &formats { if let Ok(dt) = NaiveDateTime::parse_from_str(date_str, fmt) { return Some(dt); } } None}
fn main() { let dates = vec![ "2024-03-15 14:30:00", "15/03/2024 14:30", "2