2 Punkte von GN⁺ 2024-07-23 | 1 Kommentare | Auf WhatsApp teilen
  • 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, hifitime und icu
    • 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 als Timestamp geparst
    • 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 jiff zu den Dependencies in Cargo.toml eines 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-example erstellen
    • Dependency mit cargo add jiff hinzufügen
    • In src/main.rs die aktuelle Zeit mit Zoned::now().round(Unit::Second)? auf Sekunden runden
    • Mit cargo run ausführen
  • 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

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/Troll gefunden werden
    • Wie die Standardzeitzone des aktuellen Systems ermittelt wird
  • Zeitzonen-Übergangsdaten hängen von der IANA Time Zone Database ab
  • Unter Unix wird üblicherweise /usr/share/zoneinfo verwendet; auch die Umgebungsvariable TZDIR wird 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/localtime gelesen
    • Unter Windows wird mit GetDynamicTimeZoneInformation eine Windows-spezifische Zeitzonenkennung gelesen und über die CLDR-XML-Daten von Unicode auf eine IANA-Zeitzonenkennung abgebildet
  • 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-sys und portable-atomic kann 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

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

    • use 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 wird
      Soweit 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 ein Timestamp-Objekt erzeugt
      Bestimmt 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ücke parse() an Strings hängen?
      Dadurch kommt auch die Frage auf, warum man nicht eine Syntax wie Timestamp.parse(str) verwendet
    • Stimme zu. Ich persönlich finde let span = 5.days() + 8.hours() + 1.minutes(); besser
    • Diese Version gefällt mir, weil sie konsistent wirkt
      Die ursprüngliche Syntax sieht nach etwas aus, das Ruby machen würde
    • Stimme zu. Vom Denkablauf her möchte ich erst festlegen, was ich mache, und dann die Details ergänzen, aber diese Syntax ist umgekehrt
      Beim Lesen des Codes wäre es schöner, zuerst zu sehen, dass es um einen span geht
    • Noch besser fände ich 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 mag BurntSushis ripgrep und rufe es in Emacs ständig direkt auf
      Ich habe ripgrep schon benutzt, bevor Debian rg standardmäßig mitgeliefert hat, und ich habe auch JodaTime benutzt, damals als es noch Leute gab, die meinten, Eclipse sei besser als IntelliJ IDEA
      Aber 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
    • Erst als ich „funktioniert auch nach ser/de“ gelesen habe, wurde mir klar, warum das Crate Serde so heißt
    • Danke für den Hinweis auf das Design-Dokument. Beim ersten Überfliegen des Repositorys habe ich es übersehen, aber es ist gut geschrieben und hat viele meiner Fragen beantwortet
    • Wer den Wahnsinn von Datum/Zeit auf unterhaltsame und zugängliche Weise sehen möchte, dem sei Kip Coles Vortrag auf der ElixirConf 2022 empfohlen: https://www.youtube.com/watch?v=4VfPvCI901c
    • Wer glaubt, Datum/Zeit sei einfach, sollte nicht in die Nähe von Scheduling oder Code zur Datumsverarbeitung gelassen werden
  • 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.

  • 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 NaiveDate erstellen, es in DateTime umwandeln und dann noch einmal in DateTime, 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.

    • Das geht mit 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 tracing verwendet?
    Vielleicht ist das zu schwergewichtig. Gemessen am Crate ist es fast halb so groß.
    log ist 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

    • Ich denke, log ist so etwas wie der kleinste gemeinsame Nenner.
      Es arbeitet auch gut mit tracing zusammen, 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 tracing vermutlich auch gut funktioniert.
  • BurntSushi ist auch die Person, die praktisch das gesamte Regex-Ökosystem in Rust aufgebaut hat.

    • Ich erinnere mich, wie ich auf Reddit mit jemandem über etwas gestritten habe und dann gemerkt habe, wer mein Gegenüber war, und nur noch dachte: „Schon gut.“
  • 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.

    • Ich frage mich, warum das nötig ist. Was ist der tatsächliche Anwendungsfall, und warum wäre eine spezialisierte wissenschaftliche Bibliothek wie hifitime dafü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.
  • Jiff wird mit hartem G ausgesprochen, also wie „Giff“.

    • Ich bin kein Muttersprachler, aber was bedeutet hard G hier genau? Das G wie in go oder wie in gin?
  • „Jiff wird wie gif mit einem weichen g wie in gem ausgesprochen.“
    Auf keinen Fall!

    • Es hatte kurz eine gute Zeit auf Platz 1 von Hacker News, aber jetzt scheint ein fataler Fehler entdeckt worden zu sein.
      Ich bleibe bei chrono †
      † natürlich ausgesprochen wie „trono“, mit dem ch aus chase
    • https://x.com/drgonzo123/status/527520297512345600
    • Komisch, ich dachte, Jiff würde mit einem harten „J“ ausgesprochen.
    • Nicht GIF, sondern GIF?!
  • Also, spricht man es jiff aus oder gif?

    • https://github.com/BurntSushi/jiff/blob/master/DESIGN.md#why...
      „Jiff wird als gif mit weichem g ausgesprochen, wie in gem.“
      Ah, ich verstehe, worauf der Witz hinauswill …
    • Ich finde, die Aussprache legt die Person fest, die es gemacht hat
      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
    • Da es eine Bibliothek zur Zeitverarbeitung ist, nenne ich es einfach biff