Jiff, una biblioteca de fecha y hora para Rust
(github.com/BurntSushi)- Jiff es una biblioteca de fecha y hora para Rust, cuyo objetivo es ofrecer tipos básicos de fecha y hora de alto nivel difíciles de usar mal, junto con un rendimiento razonable
- Sus funciones principales son la integración automática con la Time Zone Database, aritmética y redondeo conscientes del DST, formateo y parseo sin pérdida de fechas y horas con zona horaria, y soporte opcional para Serde
- El diseño de la API recibió una fuerte influencia de Temporal, la propuesta de TC39 para mejorar el manejo de fechas y horas en JavaScript
- Jiff 1.0 originalmente tenía como objetivo lanzarse en el verano de 2025, pero para abril de 2026 el calendario ya se había retrasado y actualmente no hay una línea de tiempo para el lanzamiento 1.0
- El rendimiento es un objetivo importante, pero se priorizan la comprensibilidad de la API y la corrección; en Unix sigue una política conservadora de dependencias que mantiene intencionalmente 0 dependencias
El modelo de fecha y hora que ofrece Jiff
- Jiff es una biblioteca para manejar fechas y horas en Rust, enfocada en proporcionar tipos básicos de alto nivel difíciles de usar incorrectamente
- Funciones principales compatibles
- Integración automática y fluida con la Time Zone Database
- Aritmética y redondeo conscientes del DST
- Formateo y parseo sin pérdida de fechas y horas con zona horaria
- Soporte opcional para
serde
- El diseño de la API recibió una fuerte influencia de Temporal, la propuesta de TC39 para mejorar el manejo de fechas y horas en JavaScript
- La licencia es doble: MIT o UNLICENSE
Documentación y ejemplos de uso
- Documentación y materiales relacionados
- Documentación de la API en docs.rs
- Documentos comparativos con
chrono,time,hifitimeeicu - Documento de justificación del diseño de la API de Jiff
- Documento de soporte de plataformas
- Registro de cambios
- El ejemplo básico parsea una cadena de tiempo RFC 3339, la convierte a una fecha y hora con zona horaria, suma una duración y luego la imprime como una cadena sin pérdida
- Parsea
"2024-07-11T01:14:00Z"comoTimestamp - La convierte a la zona horaria
America/New_York - Suma
1.month().hours(2) - La cadena resultante es
"2024-08-10T23:14:00-04:00[America/New_York]" - La hora UTC en formato RFC 3339 es
"2024-08-11T03:14:00Z"
- Parsea
Instalación y flujo básico de ejecución
- Jiff está en crates.io y puede usarse agregando
jiffa las dependencias deCargo.tomlen un proyecto Rust - El comando simple para agregarlo es
cargo add jiff - Flujo completo del ejemplo en el README
- Crear un proyecto nuevo con
cargo new jiff-example - Agregar la dependencia con
cargo add jiff - En
src/main.rs, redondear la hora actual a segundos conZoned::now().round(Unit::Second)? - Ejecutar con
cargo run
- Crear un proyecto nuevo con
- La salida de ejemplo muestra una forma que incluye zona horaria:
2024-07-10T19:54:20-04:00[America/New_York] - En la primera ejecución aparece más salida de compilación, pero en ejecuciones posteriores no hace falta recompilar las dependencias
Feature flags y plan para 1.0
- Jiff ofrece varias features de crate para ajustar el soporte de la biblioteca estándar de Rust, el soporte para
serdey si se incrusta o no la Time Zone Database en el binario - La lista completa de feature flags compatibles está en la sección “crate features” de la documentación
- El plan original de lanzamiento de Jiff 1.0 era el verano de 2025, pero para abril de 2026 el calendario ya se había retrasado y actualmente no hay una línea de tiempo de lanzamiento
- Después del lanzamiento 1.0, el plan es mantener la API indefinidamente; el objetivo es que los usuarios puedan usar Jiff como una base estable de API pública
- Incluso después del lanzamiento de Jiff 1.0, Jiff 0.2 tiene previsto recibir actualizaciones de corrección de errores importantes durante 1 año
- No habrá desarrollo activo de funciones
- Como ya hay muchos usuarios usando Jiff 0.2, el plan es ofrecer un período de transición
Rendimiento y rutas avanzadas de formateo
- El objetivo de diseño más importante es ser una biblioteca de fecha y hora de alto nivel difícil de usar mal
- El rendimiento es el segundo objetivo; apunta a un rendimiento razonable, aunque puede haber áreas con margen de mejora
- Los benchmarks están en el directorio
bench - Algunas áreas ya fueron optimizadas, pero todavía hay muchas áreas sin optimizar
- Mejorar el rendimiento es un objetivo, pero en general la comprensibilidad de la API y la corrección tienen prioridad sobre el rendimiento
- Los submódulos de Jiff, especialmente
jiff::fmt, funcionan como una vía de escape hacia APIs más complejas cuando se necesita más control- La implementación de
std::fmt::DisplaydeZonedpuede dificultar escribir en un búfer reutilizable jiff::fmt::temporal::DateTimePrinter::print_zonedpermite escribir unZoneden unStringoVec<u8>existente
- La implementación de
Manejo de zonas horarias por plataforma
- Los problemas de soporte de plataformas en una biblioteca de fecha y hora están relacionados principalmente con el soporte de zonas horarias
- Interacciones con la plataforma que maneja Jiff
- Cómo encontrar los datos de transiciones de identificadores de zona horaria IANA como
Antarctica/Troll - Cómo encontrar la zona horaria predeterminada del sistema actual
- Cómo encontrar los datos de transiciones de identificadores de zona horaria IANA como
- Los datos de transiciones de zonas horarias dependen de la IANA Time Zone Database
- En Unix, normalmente se usa
/usr/share/zoneinfoy también se respeta la variable de entornoTZDIR - En Windows, se incrusta automáticamente una copia de la Time Zone Database en la biblioteca compilada
- Métodos de detección de la zona horaria del sistema
- En Unix, lee
/etc/localtime - En Windows, lee el identificador de zona horaria específico de Windows mediante
GetDynamicTimeZoneInformationy lo mapea a un identificador de zona horaria IANA mediante los datos XML CLDR de Unicode
- En Unix, lee
- Se espera que el soporte de otras plataformas aumente con el tiempo, pero las plataformas poco comunes y difíciles de probar probablemente dependan de pull requests de colaboradores
Dependencias y política de MSRV
- Que Jiff tenga 0 dependencias en Unix es una decisión de diseño intencional
- El criterio para agregar una nueva dependencia a Jiff, una crate del ecosistema, es conservador
- Cuando sea realmente necesaria para interactuar con la plataforma
- Cuando sea necesaria para interoperabilidad, por ejemplo
serde
- Incluso las dependencias con fines de interoperabilidad se tratan de forma conservadora y, en general, se busca depender solo de aquellas que tengan menos lanzamientos con breaking changes que Jiff
- Dividir Jiff en varias crates podría generar nuevas dependencias, pero actualmente no hay planes para hacerlo
- La orientación es mantener bajo el número de crates, ya que la experiencia muestra que gestionar muchos límites de API semver implica un alto costo de mantenimiento
- La versión mínima soportada del compilador de Rust para Jiff es MSRV 1.70.0
- Jiff en sí y las dependencias de Jiff dentro del repositorio no usan versiones de Rust que requieran un compilador de Rust lanzado durante el último año
- Las crates dentro del repositorio que dependen de Jiff, como
jiff-icu, pueden usar una MSRV más reciente, pero no exigirán una MSRV más nueva que la crate con la que se integran - Jiff no controla la MSRV de dependencias externas como
windows-sysyportable-atomic - Según la convención del ecosistema Rust, Jiff no considera un aumento de MSRV como un cambio incompatible con semver, pero limita los aumentos de MSRV a actualizaciones de minor version
1 comentarios
Comentarios en Hacker News
En general se ve bien, pero me hizo ruido la sintaxis de ToSpan
let span = 5.days().hours(8).minutes(1);Se siente raro que solo el primer número vaya al frente y que el resto entre como argumentos de función
Si no te gusta, también se puede escribir como
let span = Span::new().days(5).hours(8).minutes(1);, y como solo añade unas cuantas letras, tampoco está maluse jiff::{Timestamp, ToSpan};y luego"2024-07-11T01:14:00Z".parse()?: ahí me chocó la parte en la que se parsea como TimestampSegún recuerdo, Rust usa algo más parecido a interfaces que a clases, pero me intriga por qué al importar una librería de repente a un string le aparece un método
parse(), y por qué ese método con un nombre tan genérico termina creando un objetoTimestampMe pregunto si el tipo de la izquierda es lo que determina el significado de
str.parse(), y también qué pasa si tanto una librería de fechas como una librería de expresiones Lisp le agreganparse()a los stringsEso incluso me lleva a preguntar por qué no usan una sintaxis como
Timestamp.parse(str)let span = 5.days() + 8.hours() + 1.minutes();La sintaxis original parece algo que haría Ruby
Al leer el código, preferiría ver primero que se está trabajando con un span
let span = Span::days(5).hours(8).minutes(1);se ve todavía mejorHe visto muchas veces que se subestima la complejidad de las librerías de fecha/hora
Se repiten cosas como “la representación interna puede ser UTC/Unix time”, “los períodos pueden representarse en nanosegundos” o “en vez de zonas horarias basta con offsets”
Si piensas así, vale la pena leer el documento de diseño de Jiff: https://github.com/BurntSushi/jiff/blob/master/DESIGN.md
Como es trabajo de BurntSushi, es excelente y muy detallado
También vale la pena leer la comparación con chrono, que es la librería de fecha/hora estándar de facto en Rust: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
Cosas como la aritmética correcta con DST incluso después de serialización/deserialización, los períodos redondeables, la aritmética de calendario con reconocimiento de zona horaria y la detección de conflictos históricos de zonas horarias son las que hacen que una librería sea precisa, útil y agradable de usar
chrono es una librería amplia y “correcta”, pero era rígida y no muy fácil de usar
Ya usaba ripgrep desde antes de que Debian incluyera
rgpor defecto, y también usaba JodaTime en la época en que había gente que pensaba que Eclipse era mejor que IntelliJ IDEAPero en ninguna parte de esa documentación se rebate la idea de que “los períodos pueden representarse en nanosegundos”
El usuario necesita ver la hora correcta según la zona horaria y el DST, pero el programa por lo general no
A menos que el trabajo implique manejar directamente varias zonas horarias, representar los períodos en milisegundos/nanosegundos suele ser muy seguro
Que los humanos hayan inventado las zonas horarias y el DST no cambia el hecho físico de que el reloj interno de la CPU hace tic decenas de miles de millones de veces por segundo
Basta ver kernels de sistemas operativos que no se rompieron hace unos días en media Tierra: en muchos lugares el código expresa los timeouts en milisegundos
Esto puede dar la impresión de que, para implementar un “cooldown de 30 segundos”, hay que sumarle 30 segundos a la hora actual en la zona horaria vigente, guardarlo como string incluyendo zona horaria, DST y formato de 12/24 horas, y hasta revisar si hay segundos intercalares
Simplemente puedes usar un timeout/cooldown de 30 segundos expresado en milisegundos, y aunque para el usuario ocurra 29 segundos después, no hace falta preocuparse por los segundos intercalares
Parece una gran librería.
Me pregunto por qué BurntSushi está creando una librería nueva.
No he trabajado mucho con manejo de tiempo en Rust, pero me gustaría saber si las librerías existentes tienen problemas de rendimiento, si las APIs actuales son incómodas de usar, si lo hace solo por diversión o si hay alguna otra razón.
La implementación de la biblioteca estándar de Java no era buena, y para resolver eso apareció JodaTime.
Después, en Java 8 se incorporó una nueva API de DateTime muy influenciada por JodaTime, y al estar en la biblioteca estándar los autores de librerías pudieron adoptarla más ampliamente.
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
En resumen, es principalmente por la API y el soporte de zona horaria de IANA.
El estado de las librerías de calendario en Rust no es precisamente ideal.
En Pandas tienes
.tz_convert()y.tz_localize(), y la conversión de zona horaria prácticamente se resuelve con eso.La tarea que uso como referencia es “obtener en UTC la primera hora del día CET/CEST para una fecha dada”, y en Pandas es muy simple.
En Chrono hay que crear un
NaiveDate, convertirlo aDateTimey luego volver a convertirlo aDateTime, y no he encontrado un patrón consistente para ese tipo de conversiones.A veces es una función miembro y otras veces es un método estático del objeto de zona horaria.
Ojalá alguien lo arregle algún día. Jiff parece un paso en la dirección correcta, pero su sintaxis a veces se siente rara; me gustaría algo más predecible.
date(y, m, d).intz("Europe/Rome")?.Si de ahí quieres obtener UTC, puedes agregar
with_time_zone(TimeZone::UTC).Me gustaría que explicaras un poco más cómo podría mejorar Jiff y qué parte de la sintaxis te parece rara.
“Cariño, despierta, salió un nuevo BurntSushi”[0]
Hablando en serio, me pregunto si alguien aquí que use Rust sabe por qué este crate no usa algo como
tracing.Puede que sea porque es demasiado pesado. En tamaño del crate, casi lo duplica.
Claro que
logtambién está bien, y tampoco sé si rastrear hasta las llamadas de operaciones de zona horaria sea un caso de uso común, pero me da curiosidad si hubo una razón en particular.[0] https://knowyourmeme.com/memes/wake-up-babe
loges algo así como el mínimo común denominador.Además se integra bien con
tracing, así que probablemente Jiff lo usa por eso.También es algo que lleva usando desde los primeros días de crates.io.
Jiff no tiene más requisitos que emitir mensajes en ciertos niveles de registro.
De todos modos, creo que
tracingtambién habría funcionado bien.BurntSushi también es la persona que creó todo el ecosistema de expresiones regulares de Rust.
Tanto en Rust como en otros ecosistemas, lo que más echo de menos en las librerías de tiempo existentes es la falta de soporte para segundos intercalares.
Por lo general usan internamente timestamps UNIX en vez de TAI, y por desgracia esta librería no es diferente.
Parece haber alguna forma de darles cierto soporte mediante archivos tzif, pero no es soporte de primera clase.
Hay más detalles en este issue de Jiff: https://github.com/BurntSushi/jiff/issues/7
Como los timestamps UNIX no usan la definición de segundo del SI sino el 1/86000 del día actual, no todos los segundos UNIX tienen la misma duración y eso rompe los cálculos correctos de intervalos.
Entiendo que fue una concesión para heredar compatibilidad con formas antiguas de seguimiento del tiempo y para formatear calendarios rápido, pero no me parece que haya sido la concesión correcta.
Los timestamps UNIX mezclan preocupaciones de representación con los datos.
Creo que los segundos intercalares deberían tratarse exactamente igual que el 29 de febrero o las zonas horarias.
hifitimeno serviría.Cada vez que sale el tema de los segundos intercalares, casi siempre parece derivar en una conversación abstracta.
Muy rara vez se conecta con casos de uso del mundo real.
Entiendo los casos de uso científicos, y me parece que para eso las librerías especializadas lo resuelven bien.
Fuera de eso, no estoy seguro de qué más haría falta.
Jiffse pronuncia con G dura, o sea “Giff”.goo la G degin?“Jiff se pronuncia
gifcon una g suave, como engem.”¡De ninguna manera!
Yo me quedo con chrono †
† claro, pronunciado “trono”, como la
chdechaseEntonces, ¿se pronuncia jiff o gif?
“Jiff se pronuncia
gifcon una g suave, como engem.”Ah, ya entendí el chiste...
Según su creador, GIF siempre se pronunció “jif”, así que para evitar confusiones estaría bien que el autor de esta nueva herramienta pronunciara Jiff como “gif”.