Intl.RelativeTimeFormat fuer relative Zeitangaben: Native Alternative zu moment.js und dayjs
AI generated
JS
() =>
JavaScript · Internationalisierung · Zeitformatierung
Intl.RelativeTimeFormat fuer relative Zeitangaben
Native, lokalisierte relative Zeit ohne moment.js oder dayjs

Fuer Formulierungen wie 'vor 3 Tagen' oder 'in 2 Stunden' braucht es keine externe Bibliothek mehr: Intl.RelativeTimeFormat erledigt lokalisierte relative Zeitangaben nativ im Browser, inklusive Rundungslogik fuer alle gaengigen Zeiteinheiten und ohne zusaetzliches Bundle-Gewicht.

13 Min. Lesezeit Intl.RelativeTimeFormat Zeitformatierung Kein moment.js

1. Das Problem mit selbstgebauten Zeitangaben

Aktivitaets-Feeds, Kommentarlisten und Benachrichtigungen zeigen selten ein absolutes Datum, sondern relative Angaben wie 'vor 3 Tagen' oder 'vor einer Stunde', weil das fuer Menschen leichter einzuordnen ist als '2026-08-02T14:30:00Z'. Genau diese Formatierung war jahrelang eine Domaene von Bibliotheken: moment.js mit seiner fromNow()-Methode, spaeter dayjs mit dem relativeTime-Plugin oder das schlanke timeago.js.

Das Problem an diesen Loesungen: moment.js gilt inzwischen als eingefroren und bringt ohne Tree-Shaking ein betraechtliches Bundle-Gewicht mit, dayjs braucht ein zusaetzliches Plugin und eigene Locale-Dateien pro Sprache, timeago.js wiederum deckt Randfaelle wie Quartale oder narrow-Formate oft nicht ab. Intl.RelativeTimeFormat loest dieselbe Aufgabe nativ, ohne jede zusaetzliche Codezeile fuer Locale-Daten.

2. Grundlagen und Syntax

Der Konstruktor new Intl.RelativeTimeFormat(locale, options) erzeugt einen Formatter, dessen format()-Methode zwei Argumente erwartet: einen numerischen Wert (positiv fuer die Zukunft, negativ fuer die Vergangenheit) und eine Zeiteinheit als String ('day', 'hour', 'minute' und so weiter). format(-3, 'day') liefert im Deutschen 'vor 3 Tagen', format(2, 'hour') liefert 'in 2 Stunden'.

Wichtig ist, dass die Methode selbst keine Zeitdifferenz berechnet, sie formatiert nur einen bereits berechneten numerischen Wert. Die Berechnung der Differenz zwischen zwei Zeitpunkten und die Wahl der passenden Einheit bleibt Aufgabe des aufrufenden Codes, was zwar mehr Kontrolle gibt, aber auch eine kleine Hilfsfunktion erfordert.


const rtf = new Intl.RelativeTimeFormat('de', { numeric: 'auto' });
console.log(rtf.format(-3, 'day'));  // "vor 3 Tagen"
console.log(rtf.format(2, 'hour'));  // "in 2 Stunden"
console.log(rtf.format(0, 'day'));   // "heute"

3. Verfuegbare Zeiteinheiten

Als Einheit akzeptiert format() die Werte 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute' und 'second', jeweils auch im Plural als 'years' etc., beide Schreibweisen werden von der Engine gleich behandelt. Die Auswahl der passenden Einheit hat direkten Einfluss auf die Lesbarkeit: '3600 Sekunden' ist technisch korrekt, aber fuer Menschen deutlich schlechter zu erfassen als 'eine Stunde'.

Die Einheit 'quarter' wird oft uebersehen, ist aber fuer Business-Dashboards nuetzlich, etwa 'letztes Quartal' statt 'vor 3 Monaten'. Wichtig: RelativeTimeFormat rundet die uebergebene Zahl nicht selbst, ein Aufruf mit format(2.7, 'day') gibt trotzdem 'in 2,7 Tagen' aus, die Rundung auf ganze Einheiten muss vor dem Aufruf erfolgen. Bei der Wahl der Einheit lohnt sich ein Blick auf den tatsaechlichen Kontext: Ein Kommentarfeed profitiert von feingranularen Einheiten wie Minuten und Stunden, waehrend ein Aenderungsprotokoll fuer Vertragsdaten eher mit Tagen, Monaten oder Quartalen arbeitet, weil Sekundengenauigkeit dort keinen praktischen Mehrwert liefert.

4. numeric: 'always' vs. 'auto'

Die Option numeric steuert, ob idiomatische Kurzformen wie 'gestern', 'heute' oder 'morgen' verwendet werden. Mit numeric: 'auto' liefert format(-1, 'day') den natuerlicheren String 'gestern' statt 'vor 1 Tag'. Mit numeric: 'always' erzwingt die Engine dagegen konsequent die numerische Form, auch dort, wo eine idiomatische Alternative existieren wuerde.

In der Praxis ist 'auto' fast immer die bessere Wahl fuer UI-Text, weil es natuerlicher klingt, waehrend 'always' sich eignet, wenn eine konsistente, vorhersagbare Formatierung wichtiger ist als natuerliche Sprache, etwa in tabellarischen Uebersichten, in denen unterschiedliche Formulierungslaengen das Layout stoeren wuerden.


const auto = new Intl.RelativeTimeFormat('de', { numeric: 'auto' });
console.log(auto.format(-1, 'day')); // "gestern"

const always = new Intl.RelativeTimeFormat('de', { numeric: 'always' });
console.log(always.format(-1, 'day')); // "vor 1 Tag"

5. Differenz berechnen und passende Einheit waehlen

Da die API selbst keine Zeitdifferenzen berechnet, braucht fast jedes Projekt eine kleine Hilfsfunktion, die aus zwei Zeitpunkten die passende Einheit und den gerundeten Wert ermittelt. Ueblich ist eine Schwellenwert-Kaskade: Unterschreitet die Differenz 60 Sekunden, wird in Sekunden formatiert, unterschreitet sie 60 Minuten, wird in Minuten formatiert, und so weiter bis zu Jahren.

Wichtig ist, konsequent mit demselben Referenzpunkt zu runden, meist Math.round() oder Math.trunc(), damit die Anzeige nicht bei jedem Re-Render minimal springt. Fuer Live-Anzeigen, die sich per Intervall aktualisieren, empfiehlt sich Math.floor() fuer Vergangenheitswerte, damit 'vor wenigen Sekunden' nicht schon nach der Haelfte der Sekunde zu 'vor 1 Minute' springt.


function formatRelative(date, locale = 'de') {
  const diffSeconds = (date.getTime() - Date.now()) / 1000;
  const units = [
    ['year', 31536000], ['month', 2592000], ['week', 604800],
    ['day', 86400], ['hour', 3600], ['minute', 60], ['second', 1],
  ];
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
  for (const [unit, secondsInUnit] of units) {
    if (Math.abs(diffSeconds) >= secondsInUnit || unit === 'second') {
      return rtf.format(Math.round(diffSeconds / secondsInUnit), unit);
    }
  }
}

6. formatToParts fuer individuelles Styling

Wie viele Intl-APIs bietet auch RelativeTimeFormat eine formatToParts()-Methode, die statt eines fertigen Strings ein Array von Teilobjekten liefert, jeweils mit type ('literal' oder 'integer') und value. Damit laesst sich beispielsweise nur die Zahl fett darstellen, waehrend der umgebende Text normal bleibt, ohne den formatierten String mit Regex zu zerlegen.

Das ist besonders in Komponenten-Frameworks nuetzlich, in denen einzelne Teile eines Strings unterschiedliche Elemente werden sollen, etwa {zahl} {einheit}. Ohne formatToParts muesste man entweder den kompletten Satz raten oder mit sprachabhaengigen Regex-Mustern operieren, was bei jeder neuen unterstuetzten Sprache erneut bricht.


const rtf = new Intl.RelativeTimeFormat('de', { numeric: 'always', style: 'long' });
console.log(rtf.formatToParts(-3, 'day'));
// [{type:'literal',value:'vor '}, {type:'integer',value:'3',unit:'day'}, {type:'literal',value:' Tagen'}]

7. Praxisbeispiel: Live-Aktivitaets-Feed

Ein klassischer Einsatz ist ein Aktivitaets-Feed, dessen Zeitstempel sich ohne Seiten-Reload aktualisieren sollen, etwa von 'vor 30 Sekunden' zu 'vor 1 Minute'. Dafuer wird die formatRelative-Hilfsfunktion aus Abschnitt 5 mit setInterval kombiniert, das die Anzeige in einem sinnvollen Rhythmus neu berechnet, ohne die komplette Seite neu zu rendern.

Ein Detail, das leicht uebersehen wird: Das Intervall sollte an die aktuell angezeigte Einheit angepasst werden. Fuer Sekundenangaben reicht ein Intervall von einer Sekunde, fuer Tagesangaben waere ein minuetliches Update unnoetig, ein stuendliches oder sogar nur ein Update beim Tab-Fokus genuegt und spart Rechenzeit sowie Akku auf mobilen Geraeten.


function bindLiveTimestamp(element, date) {
  const update = () => { element.textContent = formatRelative(date); };
  update();
  const interval = setInterval(update, 30000);
  return () => clearInterval(interval); // Cleanup
}

8. Vergleich zu moment.js, dayjs und timeago.js

moment.js liefert mit fromNow() eine sehr aehnliche Ausgabe, gilt aber offiziell als 'legacy' und bringt ohne Tree-Shaking je nach Locale-Set mehrere hundert Kilobyte mit. dayjs ist mit rund 2 KB Kern deutlich schlanker, benoetigt fuer relative Zeit aber ein zusaetzliches Plugin plus eigene Locale-Importe pro unterstuetzter Sprache, die synchron zur UI-Sprache geladen werden muessen.

Intl.RelativeTimeFormat bringt naturgemaess null Bundle-Gewicht mit, da es Teil der JavaScript-Engine ist, deckt dafuer aber ausschliesslich Formatierung ab, keine Datumsarithmetik, kein Parsing, keine Zeitzonen-Konvertierung. Fuer reine 'vor X'-Anzeigen ist die native API die schlankere Wahl, fuer komplexe Datumsberechnungen bleibt eine Bibliothek wie date-fns oder Temporal (sobald stabil) sinnvoll. In gemischten Projekten ist es durchaus ueblich, beide Ansaetze parallel zu nutzen: date-fns oder Temporal fuer Berechnungen und Zeitzonen, Intl.RelativeTimeFormat ausschliesslich fuer die letzte Formatierungsstufe kurz vor der Anzeige im UI.

9. Best Practices und Zusammenfassung

Intl.RelativeTimeFormat ersetzt fuer den haeufigsten i18n-Anwendungsfall, relative Zeitangaben in der UI, eine ganze Bibliotheksklasse, ohne einen einzigen Kilobyte Bundle-Gewicht hinzuzufuegen. Die API uebernimmt ausschliesslich die Formatierung, Berechnung und Rundung der Zeitdifferenz bleiben Aufgabe einer kleinen, gut testbaren Hilfsfunktion im eigenen Code.

In der Praxis empfiehlt sich numeric: 'auto' fuer natuerlichere UI-Texte, eine Schwellenwert-Kaskade fuer die Einheitenwahl und, bei Live-Updates, ein an die Einheit angepasstes Intervall statt eines fixen Ein-Sekunden-Takts. Kombiniert mit Instanz-Caching wie bei den anderen Intl-APIs ist das Ergebnis eine performante, vollstaendig native Loesung.

Ansatz Bundle-Groesse Lokalisierung Datumsarithmetik
Intl.RelativeTimeFormat 0 KB (nativ) Vollstaendig ueber CLDR Nein, nur Formatierung
moment.js ~ 300 KB (ohne Tree-Shaking) Ueber Locale-Dateien Ja
dayjs + Plugin ~ 5 KB + Plugin/Locales Ueber Locale-Imports Ja
timeago.js ~ 2 KB Eingeschraenkt Nein

Mironsoft

Moderne Browser-APIs, Performance und wartbares JavaScript

JavaScript, das im echten Browser robust bleibt, nicht nur im Tutorial?

Wir prüfen bestehenden Frontend-Code auf veraltete Patterns, unnötige Bibliotheken und Performance-Fallen und ersetzen sie durch moderne, native Browser-APIs, die weniger Bundle-Gewicht und weniger Wartungslast bedeuten.

Code-Review

Veraltete Patterns, unnötige Dependencies und Memory Leaks systematisch aufspüren.

Performance-Optimierung

Bundle-Größe, Ladezeit und Runtime-Performance mit modernen APIs verbessern.

Modernisierung

Native Browser-APIs statt schwerer Bibliotheken gezielt einführen.

10. Zusammenfassung

Intl.RelativeTimeFormat: Das Wichtigste auf einen Blick

Grundprinzip

format(wert, einheit) formatiert einen bereits berechneten numerischen Abstand, berechnet aber selbst keine Zeitdifferenzen.

numeric: auto

Erzeugt natuerlichere Formulierungen wie 'gestern' statt 'vor 1 Tag', empfehlenswert fuer UI-Text.

Eigene Hilfsfunktion

Eine Schwellenwert-Kaskade waehlt aus der Zeitdifferenz die passende Einheit und rundet konsistent.

Bundle-Vorteil

Null zusaetzliches Gewicht gegenueber moment.js oder dayjs, da Teil der JavaScript-Engine selbst.

11. FAQ: Intl.RelativeTimeFormat: Das Wichtigste auf einen Blick

1Berechnet Intl.RelativeTimeFormat selbst die Zeitdifferenz?
Nein, die API formatiert nur einen bereits berechneten numerischen Wert und eine Einheit. Die Differenzberechnung zwischen zwei Zeitpunkten bleibt Aufgabe des eigenen Codes.
2Was bewirkt numeric: 'auto' im Unterschied zu 'always'?
'auto' erlaubt idiomatische Kurzformen wie 'gestern' oder 'heute', 'always' erzwingt durchgehend die numerische Form wie 'vor 1 Tag', auch wenn eine natuerlichere Alternative existiert.
3Welche Zeiteinheiten werden unterstuetzt?
year, quarter, month, week, day, hour, minute und second, jeweils auch in der Pluralform. Quarter wird oft uebersehen, ist aber fuer Business-Kontexte nuetzlich.
4Rundet format() die uebergebene Zahl automatisch?
Nein, format(2.7, 'day') gibt 'in 2,7 Tagen' aus. Die Rundung auf ganze Zahlen muss vor dem Aufruf in der eigenen Hilfsfunktion erfolgen, etwa mit Math.round().
5Ist Intl.RelativeTimeFormat ein Ersatz fuer moment.js oder dayjs?
Nur fuer die Formatierung relativer Zeitangaben. Fuer Datumsarithmetik, Parsing oder Zeitzonen-Konvertierung braucht es weiterhin eine Bibliothek wie date-fns oder das kommende Temporal.
6Wie oft sollte ein Live-Zeitstempel aktualisiert werden?
Am besten an die angezeigte Einheit angepasst: Sekundenangaben brauchen ein kurzes Intervall, Tages- oder Wochenangaben reichen stuendliche oder noch seltenere Updates.
7Was liefert formatToParts() zusaetzlich zu format()?
Ein Array von Teilobjekten mit type und value statt eines fertigen Strings, wodurch sich einzelne Bestandteile wie die Zahl separat stylen lassen, etwa fett in einer UI-Komponente.
8Funktioniert die API auch fuer zukuenftige Zeitpunkte?
Ja, positive Werte erzeugen Zukunftsformulierungen wie 'in 2 Stunden', negative Werte Vergangenheitsformulierungen wie 'vor 2 Stunden', gesteuert allein ueber das Vorzeichen des ersten Arguments.
9Muss ich fuer jede Locale eigene Daten laden?
Nein, die Locale-Daten sind Teil der JavaScript-Engine beziehungsweise des Betriebssystems und muessen nicht separat als Bundle-Asset ausgeliefert werden, anders als bei den meisten Bibliotheks-Locales.
10Gibt es Browser oder Node-Versionen ohne Unterstuetzung?
Alle aktuellen evergreen Browser und Node.js ab Version 13 unterstuetzen Intl.RelativeTimeFormat vollstaendig, aeltere Umgebungen benoetigen gegebenenfalls einen Polyfill von formatjs.