Jiff, eine Datums- und Zeitbibliothek für Rust
(github.com/BurntSushi)- Jiff ist eine Datums- und Zeitbibliothek für Rust, die auf schwer falsch zu verwendende, hochabstrakte Basisdatentypen für Datum und Zeit sowie angemessene Performance abzielt
- Zu den Kernfunktionen gehören die automatische Integration der Time Zone Database, DST-bewusste Arithmetik und Rundung, verlustfreies Formatieren und Parsen von Datum/Zeit mit Zeitzone sowie optionale Serde-Unterstützung
- Das API-Design wurde stark von Temporal beeinflusst, einem TC39-Vorschlag zur Verbesserung der Datums- und Zeitverarbeitung in JavaScript
- Jiff 1.0 sollte ursprünglich im Sommer 2025 erscheinen, der Zeitplan hat sich jedoch mit Stand April 2026 verschoben; derzeit gibt es keine Timeline für ein 1.0-Release
- Performance ist ein wichtiges Ziel, aber Verständlichkeit der API und Korrektheit haben Vorrang; unter Unix verfolgt Jiff bewusst eine konservative Dependency-Politik mit 0 Abhängigkeiten
Das Datums- und Zeitmodell von Jiff
- Jiff ist eine Bibliothek für den Umgang mit Datum und Zeit in Rust und konzentriert sich darauf, hochabstrakte Basisdatentypen bereitzustellen, die schwer falsch zu verwenden sind
- Wichtige unterstützte Funktionen
- Automatische und nahtlose Integration mit der Time Zone Database
- DST-bewusste Arithmetik und Rundung
- Verlustfreies Formatieren und Parsen von Datum/Zeit mit Zeitzone
- Optionale Unterstützung für
serde
- Das API-Design wurde stark von Temporal beeinflusst, einem TC39-Vorschlag zur Verbesserung der Datums- und Zeitverarbeitung in JavaScript
- Die Lizenzierung erfolgt dual unter MIT oder UNLICENSE
Dokumentation und Nutzungsbeispiele
- Dokumentation und zugehörige Materialien
- API-Dokumentation auf docs.rs
- Vergleichsdokumente mit
chrono,time,hifitimeundicu - Dokumentation zur Begründung des Jiff-API-Designs
- Dokumentation zur Plattformunterstützung
- Changelog
- Das Basisbeispiel parst einen RFC-3339-Zeitstring, wandelt ihn in ein Datum/Zeit-Objekt mit Zeitzone um, addiert eine Dauer und gibt anschließend einen verlustfreien String aus
"2024-07-11T01:14:00Z"wird alsTimestampgeparst- Umwandlung in die Zeitzone
America/New_York - Addition von
1.month().hours(2) - Der Ergebnisstring ist
"2024-08-10T23:14:00-04:00[America/New_York]" - Die UTC-Zeit im RFC-3339-Format ist
"2024-08-11T03:14:00Z"
Installation und grundlegender Ablauf
- Jiff ist auf crates.io verfügbar und kann genutzt werden, indem
jiffzu den Dependencies inCargo.tomleines Rust-Projekts hinzugefügt wird - Der einfache Befehl zum Hinzufügen lautet
cargo add jiff - Vollständiger Beispielablauf aus dem README
- Neues Projekt mit
cargo new jiff-exampleerstellen - Dependency mit
cargo add jiffhinzufügen - In
src/main.rsdie aktuelle Zeit mitZoned::now().round(Unit::Second)?auf Sekunden runden - Mit
cargo runausführen
- Neues Projekt mit
- Die Beispielausgabe zeigt eine Form mit Zeitzone:
2024-07-10T19:54:20-04:00[America/New_York] - Beim ersten Lauf erscheint mehr Compiler-Ausgabe, bei späteren Läufen müssen die Dependencies jedoch nicht erneut kompiliert werden
Feature-Flags und Pläne für 1.0
- Jiff bietet mehrere Crate-Features, mit denen sich Unterstützung für die Rust-Standardbibliothek,
serde-Unterstützung und das Einbetten der Time Zone Database in die Binärdatei steuern lassen - Die vollständige Liste der unterstützten Feature-Flags steht im Abschnitt „crate features“ der Dokumentation
- Der ursprüngliche Release-Plan für Jiff 1.0 war Sommer 2025, doch mit Stand April 2026 hat sich der Zeitplan bereits verschoben; derzeit gibt es keine Release-Timeline
- Nach dem 1.0-Release ist geplant, die API auf unbestimmte Zeit beizubehalten; Ziel ist, dass Nutzer Jiff als stabile Grundlage für öffentliche APIs verwenden können
- Auch nach dem Release von Jiff 1.0 soll Jiff 0.2 noch ein Jahr lang Updates für wichtige Bugfixes erhalten
- Keine aktive Feature-Entwicklung
- Da bereits viele Nutzer Jiff 0.2 verwenden, soll eine Übergangsfrist geboten werden
Performance und fortgeschrittene Formatierungspfade
- Das wichtigste Designziel ist, eine hochabstrakte Datums- und Zeitbibliothek zu sein, die schwer falsch zu verwenden ist
- Performance ist das zweite Ziel; angestrebt wird angemessene Performance, es kann aber Bereiche mit Verbesserungspotenzial geben
- Benchmarks befinden sich im Verzeichnis
bench - Einige Bereiche wurden optimiert, viele andere jedoch noch nicht
- Performance-Verbesserungen sind ein Ziel, aber im Allgemeinen haben Verständlichkeit der API und Korrektheit Vorrang vor Performance
- Die Untermodule von Jiff, insbesondere
jiff::fmt, dienen als Escape Hatch für komplexere APIs, wenn mehr Kontrolle nötig ist- Die Implementierung von
std::fmt::DisplayfürZonedkann das Schreiben in wiederverwendbare Buffer erschweren jiff::fmt::temporal::DateTimePrinter::print_zonedermöglicht es,Zonedin einen bestehendenStringoderVec<u8>zu schreiben
- Die Implementierung von
Plattformspezifische Zeitzonenverarbeitung
- Fragen der Plattformunterstützung bei Datums- und Zeitbibliotheken betreffen vor allem die Zeitzonenunterstützung
- Plattforminteraktionen, die Jiff behandelt
- Wie Übergangsdaten für IANA-Zeitzonenkennungen wie
Antarctica/Trollgefunden werden - Wie die Standardzeitzone des aktuellen Systems ermittelt wird
- Wie Übergangsdaten für IANA-Zeitzonenkennungen wie
- Zeitzonen-Übergangsdaten hängen von der IANA Time Zone Database ab
- Unter Unix wird üblicherweise
/usr/share/zoneinfoverwendet; auch die UmgebungsvariableTZDIRwird berücksichtigt - Unter Windows wird automatisch eine Kopie der Time Zone Database in die kompilierte Bibliothek eingebettet
- Methoden zur Erkennung der Systemzeitzone
- Unter Unix wird
/etc/localtimegelesen - Unter Windows wird mit
GetDynamicTimeZoneInformationeine Windows-spezifische Zeitzonenkennung gelesen und über die CLDR-XML-Daten von Unicode auf eine IANA-Zeitzonenkennung abgebildet
- Unter Unix wird
- Die Unterstützung weiterer Plattformen dürfte mit der Zeit zunehmen; seltene Plattformen, die schwer zu testen sind, werden aber wahrscheinlich von Pull Requests durch Beitragende abhängen
Dependencies und MSRV-Policy
- Dass Jiff unter Unix 0 Dependencies hat, ist eine bewusste Designentscheidung
- Die Kriterien für das Hinzufügen neuer Dependencies zu Jiff als Ökosystem-Crate sind konservativ
- Wenn sie praktisch notwendig sind, um mit einer Plattform zu interagieren
- Wenn sie für Interoperabilität erforderlich sind, etwa
serde
- Auch Dependencies für Interoperabilität werden konservativ behandelt; im Allgemeinen soll nur von Projekten abgehangen werden, die seltener Breaking-Change-Releases als Jiff haben
- Eine Aufteilung von Jiff in mehrere Crates könnte neue Dependencies mit sich bringen, ist derzeit aber nicht geplant
- Die Anzahl der Crates soll klein gehalten werden; als Grund wird die Erfahrung genannt, dass die Pflege vieler semver-API-Grenzen erheblichen Wartungsaufwand verursacht
- Die minimal unterstützte Rust-Compiler-Version von Jiff ist MSRV 1.70.0
- Jiff selbst und Jiff-Dependencies innerhalb des Repositories verwenden keine Rust-Version, die einen innerhalb des letzten Jahres veröffentlichten Rust-Compiler erfordert
- Crates innerhalb des Repositories, die von Jiff abhängen, wie
jiff-icu, können eine neuere MSRV verwenden, verlangen aber keine neuere MSRV als die Crate, in die integriert wird - Die MSRV externer Dependencies wie
windows-sysundportable-atomickann Jiff nicht kontrollieren - Gemäß der Praxis im Rust-Ökosystem betrachtet Jiff eine Erhöhung der MSRV nicht als semver-inkompatible Änderung, beschränkt MSRV-Erhöhungen jedoch auf Minor-Version-Updates
1 Kommentare
Hacker-News-Kommentare
Insgesamt sieht es gut aus, aber an der ToSpan-Syntax bin ich etwas hängengeblieben
let span = 5.days().hours(8).minutes(1);Es wirkt etwas befremdlich, dass nur die erste Zahl vorne steht und der Rest als Funktionsargumente hineingeht
Wenn es einem nicht gefällt, kann man auch so etwas wie
let span = Span::new().days(5).hours(8).minutes(1);schreiben; das sind nur ein paar Zeichen mehr und nicht weiter schlimmuse jiff::{Timestamp, ToSpan};und danach"2024-07-11T01:14:00Z".parse()?– ich bin an der Stelle hängen geblieben, an der das zu einem Timestamp geparst wirdSoweit ich mich erinnere, verwendet Rust eher so etwas wie Interfaces statt Klassen, daher frage ich mich, warum ein String plötzlich eine
parse()-Methode bekommt, wenn man eine Library importiert, und warum eine Methode mit diesem allgemeinen Namen einTimestamp-Objekt erzeugtBestimmt der Typ auf der linken Seite die Bedeutung von
str.parse(), und was passiert, wenn sowohl eine Datums-Library als auch eine Library für Lisp-Ausdrückeparse()an Strings hängen?Dadurch kommt auch die Frage auf, warum man nicht eine Syntax wie
Timestamp.parse(str)verwendetlet span = 5.days() + 8.hours() + 1.minutes();besserDie ursprüngliche Syntax sieht nach etwas aus, das Ruby machen würde
Beim Lesen des Codes wäre es schöner, zuerst zu sehen, dass es um einen span geht
let span = Span::days(5).hours(8).minutes(1);Ich habe oft gesehen, dass die Komplexität von Datums-/Zeit-Libraries unterschätzt wird
Aussagen wie „Die interne Darstellung kann einfach UTC/Unix-Zeit sein“, „Dauern kann man als Nanosekunden darstellen“ oder „Statt Zeitzonen reichen Offsets“ tauchen immer wieder auf
Wenn man so denkt, lohnt sich ein Blick in das Design-Dokument von Jiff: https://github.com/BurntSushi/jiff/blob/master/DESIGN.md
Wie man es von BurntSushi kennt, ist es hervorragend und detailliert
Auch der Vergleich mit chrono, der de-facto-Standardbibliothek für Datum/Zeit in Rust, ist lesenswert: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
Dinge wie korrekte DST-Arithmetik auch nach Serialisierung/Deserialisierung, rundbare Dauern, kalenderbasierte Arithmetik mit Zeitzonenbewusstsein und die Erkennung historischer Zeitzonenkonflikte machen eine Library präzise, nützlich und gut benutzbar
chrono ist eine umfassende und „korrekte“ Library, war aber starr und nicht besonders leicht zu verwenden
Ich habe ripgrep schon benutzt, bevor Debian
rgstandardmäßig mitgeliefert hat, und ich habe auch JodaTime benutzt, damals als es noch Leute gab, die meinten, Eclipse sei besser als IntelliJ IDEAAber in keinem dieser Dokumente steht etwas, das die Aussage „Dauern kann man als Nanosekunden darstellen“ widerlegt
Der Nutzer muss die korrekte Uhrzeit unter Berücksichtigung von Zeitzonen und DST sehen, Programme müssen das normalerweise nicht
Wenn man nicht direkt mit mehreren Zeitzonen arbeitet, ist es im Allgemeinen sehr sicher, Dauern als Millisekunden/Nanosekunden darzustellen
Nur weil Menschen Zeitzonen und DST erfunden haben, ändert das nichts an dem physikalischen Vorgang, dass die interne CPU-Uhr Milliarden Male pro Sekunde tickt
Man muss sich nur Betriebssystem-Kernel ansehen, die vor ein paar Tagen nicht auf der halben Erde abgestürzt sind: Dort werden Timeouts im Code oft in Millisekunden angegeben
Sonst könnte man missverstehen, dass man für einen „30-Sekunden-Cooldown“ 30 Sekunden zur aktuellen Zeitzone addieren, das Ergebnis als String inklusive Zeitzone, DST und 12/24-Stunden-Format speichern und sogar Schaltsekunden prüfen müsse
Man verwendet einfach ein in Millisekunden ausgedrücktes 30-Sekunden-Timeout/Cooldown, und selbst wenn es aus Sicht des Nutzers nach 29 Sekunden ausgelöst wird, muss man sich um Schaltsekunden nicht kümmern
Sieht nach einer großartigen Bibliothek aus
Ich frage mich, warum BurntSushi eine neue Bibliothek baut.
Ich habe in Rust nicht viel mit Zeitverarbeitung gemacht, würde aber gern wissen, ob die bestehenden Bibliotheken Performance-Probleme haben, ob die vorhandenen APIs unhandlich sind, ob es einfach nur aus Spaß passiert oder ob es andere Gründe gibt.
Die Implementierung der Java-Standardbibliothek war nicht besonders gut, und um das zu lösen, entstand JodaTime.
Später wurde in Java 8 eine neue DateTime-API eingeführt, die stark von JodaTime beeinflusst war, und weil sie Teil der Standardbibliothek war, konnten Bibliotheksautoren sie breiter übernehmen.
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
Kurz gesagt geht es vor allem um die API und die IANA-Zeitzonenunterstützung.
Der Zustand der Kalenderbibliotheken in Rust ist nicht gerade ideal.
In Pandas gibt es
.tz_convert()und.tz_localize(), und bei Zeitzonenumrechnungen ist das praktisch schon alles.Als Referenzaufgabe nehme ich: „Für ein gegebenes Datum die erste Stunde eines CET/CEST-Tages in UTC ermitteln“, und in Pandas ist das sehr einfach.
In Chrono muss man ein
NaiveDateerstellen, es inDateTimeumwandeln und dann noch einmal inDateTime, und ich habe kein konsistentes Muster für solche Umwandlungen gefunden.Mal ist es eine Member-Funktion, mal eine statische Methode des Zeitzonenobjekts.
Hoffentlich bringt das irgendwann jemand in Ordnung. Jiff wirkt wie ein Schritt in die richtige Richtung, aber die Syntax ist manchmal seltsam, und ich hätte gern etwas Vorhersehbareres.
date(y, m, d).intz("Europe/Rome")?.Wenn du von dort UTC haben willst, kannst du einfach
with_time_zone(TimeZone::UTC)anhängen.Es wäre schön, wenn du genauer erklären könntest, wie Jiff besser werden könnte bzw. was du daran meinst, dass die Syntax seltsam ist.
„Schatz, wach auf, ein neuer BurntSushi ist da“[0]
Im Ernst: Weiß jemand hier unter den Rust-Nutzern, warum dieses Crate nicht etwas wie
tracingverwendet?Vielleicht ist das zu schwergewichtig. Gemessen am Crate ist es fast halb so groß.
logist natürlich auch völlig in Ordnung, und ich weiß nicht einmal, ob das Nachverfolgen bis hinunter zu Aufrufen von Zeitzonenoperationen ein typischer Anwendungsfall ist, aber mich würde interessieren, ob es dafür einen besonderen Grund gab.[0] https://knowyourmeme.com/memes/wake-up-babe
logist so etwas wie der kleinste gemeinsame Nenner.Es arbeitet auch gut mit
tracingzusammen, deshalb wurde es in Jiff wahrscheinlich deswegen verwendet.Außerdem wird es schon seit den frühen Tagen von crates.io benutzt.
Jiff hat außer dem Ausgeben von Nachrichten auf bestimmten Log-Levels keine weiteren Anforderungen.
Natürlich hätte
tracingvermutlich auch gut funktioniert.BurntSushi ist auch die Person, die praktisch das gesamte Regex-Ökosystem in Rust aufgebaut hat.
Das größte Manko bestehender Zeitbibliotheken, in Rust wie auch in anderen Ökosystemen, ist die fehlende Schaltsekunden-Unterstützung.
Das liegt meist daran, dass intern UNIX-Timestamps statt TAI verwendet werden, und diese Bibliothek ist da leider keine Ausnahme.
Über tzif-Dateien scheint es zwar eine gewisse Unterstützung zu geben, aber keine erstklassige.
Im zugehörigen Jiff-Issue stehen Details: https://github.com/BurntSushi/jiff/issues/7
UNIX-Timestamps verwenden nicht die Definition der SI-Sekunde, sondern 1/86000 des aktuellen Tages, wodurch nicht alle UNIX-Sekunden gleich lang sind und korrekte Dauerberechnungen kaputtgehen.
Ich verstehe, dass das ein Kompromiss war, um Kompatibilität mit älteren Verfahren der Zeitmessung zu erben und Kalenderformatierung schnell zu machen, aber ich halte ihn nicht für den richtigen Kompromiss.
UNIX-Timestamps vermischen Darstellungsaspekte mit den Daten selbst.
Ich finde, Schaltsekunden sollten exakt genauso behandelt werden wie der 29. Februar oder Zeitzonen.
hifitimedafür nicht passend?Immer wenn das Thema Schaltsekunden aufkommt, scheint es meistens in abstrakte Diskussionen abzugleiten.
Mit realen Anwendungsfällen ist das nur sehr selten verbunden.
Wissenschaftliche Anwendungsfälle verstehe ich, und dafür können spezialisierte Bibliotheken das meiner Ansicht nach gut abdecken.
Ich bin mir nicht sicher, ob darüber hinaus noch mehr nötig ist.
Jiffwird mit hartem G ausgesprochen, also wie „Giff“.gooder wie ingin?„Jiff wird wie
gifmit einem weichen g wie ingemausgesprochen.“Auf keinen Fall!
Ich bleibe bei chrono †
† natürlich ausgesprochen wie „trono“, mit dem
chauschaseAlso, spricht man es jiff aus oder gif?
„Jiff wird als
gifmit weichem g ausgesprochen, wie ingem.“Ah, ich verstehe, worauf der Witz hinauswill …
Laut seinem Erfinder wurde GIF schon immer als „jif“ ausgesprochen, also wäre es zur Vermeidung von Verwechslungen gut, wenn der Ersteller dieses neuen Tools Jiff als „gif“ aussprechen würde