Trabajo avanzado con fechas y horas en Rust: de NaiveDateTime a Timezone
Trabajar con fechas y horas es una de las tareas más comunes en el desarrollo backend, registro de eventos, sistemas financieros y procesamiento de datos. En Rust, la biblioteca estándar proporciona el módulo básico std::time, pero para proyectos serios no es suficiente. A nivel avanzado, recurrimos a la crate chrono, el estándar de facto para trabajar con fechas y horas. En este artículo, analizaremos escenarios complejos: trabajo con zonas horarias, análisis sintáctico de formatos no estándar, manipulaciones con duraciones y optimización del rendimiento.
1. Instalación y estructura básica de chrono
Añada la dependencia en Cargo.toml:
[dependencies]chrono = { version = "0.4", features = ["serde"] }Tipos principales de chrono para trabajo avanzado:
NaiveDateTime— fecha-hora sin zona horaria (local).DateTime<Tz>— fecha-hora vinculada a una zona horaria (por ejemplo,Utc,Local,FixedOffset).Duration— intervalo de tiempo (diferente destd::time::Duration, admite valores negativos).TimeDelta— sinónimo deDurationen versiones nuevas.
Ejemplo de creación del momento actual en UTC y 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. Trabajo con zonas horarias (Timezone)
Una de las capacidades potentes de chrono es el soporte de la base de datos de zonas horarias Olson a través de la crate chrono-tz. Esto permite trabajar con nombres de zonas como "Europe/Moscow" o "America/New_York".
Instale:
[dependencies]chrono = "0.4"chrono-tz = "0.8"Ejemplo de conversión de hora 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);}Punto importante: siempre almacene la hora en UTC en la base de datos, y conviértala a hora local solo al mostrarla al usuario. Esto evita confusiones con el horario de verano/invierno.
2.1. Trabajo con FixedOffset
Si conoce un desplazamiento fijo (por ejemplo, +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!("Con desplazamiento +03:00: {}", now_offset);}3. Análisis sintáctico y formateo de fechas (técnicas avanzadas)
El análisis sintáctico estándar mediante parse_from_str requiere un formato exacto. Pero en proyectos reales, los formatos pueden variar (por ejemplo, registros de diferentes sistemas). Consideremos varios enfoques.
3.1. Múltiples formatos con try_parse
Use una iteración 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