Intl.RelativeTimeFormat API
Formatieren Sie relative Zeitangaben in JavaScript mit vollständiger Internationalisierungsunterstützung
Einführung
Die Anzeige relativer Zeitstempel wie "vor 3 Stunden" oder "in 2 Tagen" ist eine häufige Anforderung in Webanwendungen. Social-Media-Feeds, Kommentarbereiche, Benachrichtigungssysteme und Aktivitätsprotokolle benötigen alle menschenlesbare Zeitanzeigen, die sich aktualisieren, wenn Inhalte älter werden.
Diese Funktionalität von Grund auf neu zu entwickeln, stellt Herausforderungen dar. Man muss Zeitdifferenzen berechnen, geeignete Einheiten auswählen, Pluralisierungsregeln in verschiedenen Sprachen berücksichtigen und Übersetzungen für jede unterstützte Sprache pflegen. Diese Komplexität erklärt, warum Entwickler traditionell auf Bibliotheken wie Moment.js zurückgegriffen haben, wodurch die Bundle-Größe für scheinbar einfache Formatierungen erheblich zunimmt.
Die Intl.RelativeTimeFormat-API bietet eine native Lösung. Sie formatiert relative Zeitangaben mit vollständiger Internationalisierungsunterstützung und behandelt Pluralisierungsregeln und kulturelle Konventionen automatisch. Die API funktioniert in allen wichtigen Browsern mit 95% globaler Abdeckung und eliminiert die Notwendigkeit externer Abhängigkeiten, während sie natürlich klingende Ausgaben in Dutzenden von Sprachen erzeugt.
Grundlegende Verwendung
Der Intl.RelativeTimeFormat-Konstruktor erstellt eine Formatter-Instanz, die numerische Werte und Zeiteinheiten in lokalisierte Zeichenketten umwandelt.
const rtf = new Intl.RelativeTimeFormat('en');
console.log(rtf.format(-1, 'day'));
// "1 day ago"
console.log(rtf.format(2, 'hour'));
// "in 2 hours"
console.log(rtf.format(-3, 'month'));
// "3 months ago"
Die format()-Methode nimmt zwei Parameter entgegen:
value: Eine Zahl, die die Zeitdauer angibtunit: Ein String, der die Zeiteinheit spezifiziert
Negative Werte zeigen vergangene Zeiten an, positive Werte zukünftige Zeiten. Die API behandelt die Pluralisierung automatisch und erzeugt je nach Wert "vor 1 Tag" oder "vor 2 Tagen".
Unterstützte Zeiteinheiten
Die API unterstützt acht Zeiteinheiten, die jeweils sowohl in Singular- als auch in Pluralform akzeptiert werden:
const rtf = new Intl.RelativeTimeFormat('en');
// Diese erzeugen identische Ausgaben
console.log(rtf.format(-5, 'second'));
// "5 seconds ago"
console.log(rtf.format(-5, 'seconds'));
// "5 seconds ago"
Verfügbare Einheiten von der kleinsten bis zur größten:
secondodersecondsminuteoderminuteshouroderhoursdayoderdaysweekoderweeksmonthodermonthsquarteroderquartersyearoderyears
Die Quartalseinheit erweist sich als nützlich in Geschäftsanwendungen, die Geschäftsperioden verfolgen, während die anderen typische Anforderungen an die Formatierung relativer Zeiten abdecken.
Natürliche Sprachausgabe
Die Option numeric steuert, ob der Formatierer numerische Werte oder natürlichsprachliche Alternativen verwendet.
const rtfNumeric = new Intl.RelativeTimeFormat('en', {
numeric: 'always'
});
console.log(rtfNumeric.format(-1, 'day'));
// "1 day ago"
console.log(rtfNumeric.format(0, 'day'));
// "in 0 days"
console.log(rtfNumeric.format(1, 'day'));
// "in 1 day"
Die Einstellung von numeric auf auto erzeugt idiomatischere Formulierungen für gängige Werte:
const rtfAuto = new Intl.RelativeTimeFormat('en', {
numeric: 'auto'
});
console.log(rtfAuto.format(-1, 'day'));
// "yesterday"
console.log(rtfAuto.format(0, 'day'));
// "today"
console.log(rtfAuto.format(1, 'day'));
// "tomorrow"
Diese natürlichsprachliche Ausgabe schafft konversationellere Schnittstellen. Die Option auto funktioniert für alle Zeiteinheiten, obwohl der Effekt bei Tagen am deutlichsten ist. Andere Sprachen haben ihre eigenen idiomatischen Alternativen, die die API automatisch verarbeitet.
Formatierungsstile
Die Option style passt die Ausführlichkeit der Ausgabe für verschiedene Schnittstellenkontexte an:
const rtfLong = new Intl.RelativeTimeFormat('en', {
style: 'long'
});
console.log(rtfLong.format(-2, 'hour'));
// "2 hours ago"
const rtfShort = new Intl.RelativeTimeFormat('en', {
style: 'short'
});
console.log(rtfShort.format(-2, 'hour'));
// "2 hr. ago"
const rtfNarrow = new Intl.RelativeTimeFormat('en', {
style: 'narrow'
});
console.log(rtfNarrow.format(-2, 'hour'));
// "2h ago"
Verwenden Sie den Stil long (Standard) für standardmäßige Schnittstellen, bei denen die Lesbarkeit am wichtigsten ist. Verwenden Sie den Stil short für platzbeschränkte Layouts wie mobile Schnittstellen oder Datentabellen. Verwenden Sie den Stil narrow für extrem kompakte Anzeigen, bei denen jedes Zeichen zählt.
Berechnung von Zeitunterschieden
Die API Intl.RelativeTimeFormat formatiert Werte, berechnet sie aber nicht. Sie müssen Zeitunterschiede selbst berechnen und geeignete Einheiten auswählen. Diese Trennung der Zuständigkeiten gibt Ihnen Kontrolle über die Berechnungslogik, während die Formatierungskomplexität an die API delegiert wird.
Grundlegende Berechnung von Zeitunterschieden
Für eine bestimmte Zeiteinheit berechnen Sie den Unterschied zwischen zwei Datumsangaben:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
function formatDaysAgo(date) {
const now = new Date();
const diffInMs = date - now;
const diffInDays = Math.round(diffInMs / (1000 * 60 * 60 * 24));
return rtf.format(diffInDays, 'day');
}
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
console.log(formatDaysAgo(yesterday));
// "yesterday"
const nextWeek = new Date();
nextWeek.setDate(nextWeek.getDate() + 7);
console.log(formatDaysAgo(nextWeek));
// "in 7 days"
Dieser Ansatz funktioniert, wenn Sie wissen, welche Einheit für Ihren Anwendungsfall sinnvoll ist. Zeitstempel für Kommentare könnten immer Stunden oder Tage verwenden, während bei der Terminplanung der Fokus auf Tagen oder Wochen liegen könnte.
Automatische Einheitenauswahl
Für die allgemeine relative Zeitformatierung wählen Sie die am besten geeignete Einheit basierend auf der Größenordnung des Zeitunterschieds:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: 24 * 60 * 60 * 1000 * 365 / 12,
week: 24 * 60 * 60 * 1000 * 7,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
};
function formatRelativeTime(date) {
const now = new Date();
const diffInMs = date - now;
const absDiff = Math.abs(diffInMs);
for (const [unit, msValue] of Object.entries(units)) {
if (absDiff >= msValue || unit === 'second') {
const value = Math.round(diffInMs / msValue);
return rtf.format(value, unit);
}
}
}
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
console.log(formatRelativeTime(fiveMinutesAgo));
// "5 minutes ago"
const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
console.log(formatRelativeTime(threeDaysAgo));
// "3 days ago"
Diese Implementierung iteriert durch Einheiten von der größten zur kleinsten und wählt die erste Einheit aus, bei der der Zeitunterschied den Millisekunden-Wert der Einheit überschreitet. Der Fallback auf Sekunden stellt sicher, dass die Funktion immer ein Ergebnis zurückgibt.
Die Einheitenschwellenwerte verwenden Näherungswerte. Monate werden als 1/12 eines Jahres berechnet, anstatt die variablen Monatslängen zu berücksichtigen. Diese Annäherung funktioniert gut für relative Zeitanzeigen, bei denen die Lesbarkeit wichtiger ist als die Präzision.
Internationalisierungsunterstützung
Der Formatierer berücksichtigt lokale Konventionen für die Anzeige relativer Zeitangaben. Verschiedene Sprachen haben unterschiedliche Pluralisierungsregeln, Wortstellungen und idiomatische Ausdrücke.
const rtfEnglish = new Intl.RelativeTimeFormat('en', {
numeric: 'auto'
});
console.log(rtfEnglish.format(-1, 'day'));
// "yesterday"
const rtfSpanish = new Intl.RelativeTimeFormat('es', {
numeric: 'auto'
});
console.log(rtfSpanish.format(-1, 'day'));
// "ayer"
const rtfJapanese = new Intl.RelativeTimeFormat('ja', {
numeric: 'auto'
});
console.log(rtfJapanese.format(-1, 'day'));
// "昨日"
Pluralregeln variieren erheblich zwischen den Sprachen. Englisch unterscheidet zwischen einem und vielen (1 day vs 2 days). Arabisch hat sechs Pluralformen, abhängig von der Anzahl. Japanisch verwendet unabhängig von der Menge dieselbe Form. Die API behandelt diese Komplexitäten automatisch.
const rtfArabic = new Intl.RelativeTimeFormat('ar');
console.log(rtfArabic.format(-1, 'day'));
// "قبل يوم واحد"
console.log(rtfArabic.format(-2, 'day'));
// "قبل يومين"
console.log(rtfArabic.format(-3, 'day'));
// "قبل 3 أيام"
console.log(rtfArabic.format(-11, 'day'));
// "قبل 11 يومًا"
Der Formatierer behandelt auch die Textrichtung für Rechts-nach-Links-Sprachen und wendet kulturell angemessene Formatierungskonventionen an. Diese automatische Lokalisierung macht die Pflege von Übersetzungsdateien oder die Implementierung benutzerdefinierter Pluralisierungslogik überflüssig.
Erweiterte Formatierung mit formatToParts
Die Methode formatToParts() gibt die formatierte Zeichenfolge als Array von Objekten zurück, was eine benutzerdefinierte Gestaltung oder Manipulation einzelner Komponenten ermöglicht.
const rtf = new Intl.RelativeTimeFormat('en');
const parts = rtf.formatToParts(-5, 'second');
console.log(parts);
// [
// { type: 'integer', value: '5', unit: 'second' },
// { type: 'literal', value: ' seconds ago' }
// ]
Jedes Teil-Objekt enthält:
type: Entwederintegerfür numerische Werte oderliteralfür Textvalue: Der Zeichenketteninhalt dieses Teilsunit: Die Zeiteinheit (bei Integer-Teilen vorhanden)
Diese Struktur ermöglicht benutzerdefiniertes Rendering, bei dem Zahlen möglicherweise anders als Text gestaltet werden sollen, oder spezifische Komponenten für die Anzeige extrahiert werden:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
function formatWithStyledNumber(value, unit) {
const parts = rtf.formatToParts(value, unit);
return parts.map(part => {
if (part.type === 'integer') {
return `<strong>${part.value}</strong>`;
}
return part.value;
}).join('');
}
console.log(formatWithStyledNumber(-5, 'hour'));
// "<strong>5</strong> hours ago"
Bei Verwendung von numeric: 'auto' mit Werten, die natürlichsprachliche Alternativen haben, gibt formatToParts() ein einzelnes Literal-Teil zurück:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const parts = rtf.formatToParts(-1, 'day');
console.log(parts);
// [
// { type: 'literal', value: 'yesterday' }
// ]
Dieses Verhalten ermöglicht es zu erkennen, wann natürliche Sprache im Gegensatz zur numerischen Formatierung verwendet wird, sodass je nach Ausgabetyp unterschiedliche Formatierungen oder Verhaltensweisen angewendet werden können.
Leistungsoptimierung
Das Erstellen von Intl.RelativeTimeFormat-Instanzen beinhaltet das Laden von Gebietsschema-Daten und die Initialisierung von Formatierungsregeln. Dieser Vorgang ist kostspielig genug, um unnötige Wiederholungen zu vermeiden.
Formatter-Instanzen zwischenspeichern
Erstellen Sie Formatter einmal und verwenden Sie sie wieder:
const formatterCache = new Map();
function getFormatter(locale, options = {}) {
const cacheKey = `${locale}-${JSON.stringify(options)}`;
if (!formatterCache.has(cacheKey)) {
formatterCache.set(
cacheKey,
new Intl.RelativeTimeFormat(locale, options)
);
}
return formatterCache.get(cacheKey);
}
// Wiederverwendung zwischengespeicherter Formatter
const rtf = getFormatter('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day'));
// "yesterday"
Diese Caching-Strategie wird wichtig, wenn viele Zeitstempel formatiert werden, wie beim Rendern von Aktivitätsfeeds oder Kommentar-Threads.
Berechnungsaufwand minimieren
Speichern Sie Zeitstempel, anstatt relative Zeiten wiederholt zu berechnen:
// Speichern des Erstellungsdatums
const comment = {
text: "Großartiger Artikel!",
createdAt: new Date('2025-10-14T10:30:00Z')
};
// Berechnung der relativen Zeit nur beim Rendern
function renderComment(comment, locale) {
const rtf = getFormatter(locale, { numeric: 'auto' });
const units = {
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000
};
const diffInMs = comment.createdAt - new Date();
const absDiff = Math.abs(diffInMs);
for (const [unit, msValue] of Object.entries(units)) {
if (absDiff >= msValue || unit === 'second') {
const value = Math.round(diffInMs / msValue);
return rtf.format(value, unit);
}
}
}
Dieser Ansatz trennt die Datenspeicherung von der Darstellung und ermöglicht es, relative Zeiten neu zu berechnen, wenn sich das Gebietsschema des Benutzers ändert oder wenn Rendering-Updates erfolgen, ohne die zugrunde liegenden Daten zu modifizieren.
Praktische Implementierung
Die Kombination von Berechnungslogik mit Formatierung erzeugt eine wiederverwendbare Hilfsfunktion, die für Produktionsanwendungen geeignet ist:
class RelativeTimeFormatter {
constructor(locale = 'en', options = { numeric: 'auto' }) {
this.formatter = new Intl.RelativeTimeFormat(locale, options);
this.units = [
{ name: 'year', ms: 24 * 60 * 60 * 1000 * 365 },
{ name: 'month', ms: 24 * 60 * 60 * 1000 * 365 / 12 },
{ name: 'week', ms: 24 * 60 * 60 * 1000 * 7 },
{ name: 'day', ms: 24 * 60 * 60 * 1000 },
{ name: 'hour', ms: 60 * 60 * 1000 },
{ name: 'minute', ms: 60 * 1000 },
{ name: 'second', ms: 1000 }
];
}
format(date) {
const now = new Date();
const diffInMs = date - now;
const absDiff = Math.abs(diffInMs);
for (const unit of this.units) {
if (absDiff >= unit.ms || unit.name === 'second') {
const value = Math.round(diffInMs / unit.ms);
return this.formatter.format(value, unit.name);
}
}
}
}
// Verwendung
const formatter = new RelativeTimeFormatter('en');
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
console.log(formatter.format(fiveMinutesAgo));
// "5 minutes ago"
const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000);
console.log(formatter.format(tomorrow));
// "tomorrow"
Diese Klasse kapselt sowohl den Formatter als auch die Logik zur Auswahl der Einheiten ein und bietet eine saubere Schnittstelle, die Date-Objekte akzeptiert und formatierte Zeichenketten zurückgibt.
Integration mit Frameworks
In React-Anwendungen erstellen Sie den Formatter einmal und übergeben ihn über Context oder Props:
import { createContext, useContext } from 'react';
const RelativeTimeContext = createContext(null);
export function RelativeTimeProvider({ locale, children }) {
const formatter = new RelativeTimeFormatter(locale);
return (
<RelativeTimeContext.Provider value={formatter}>
{children}
</RelativeTimeContext.Provider>
);
}
export function useRelativeTime() {
const formatter = useContext(RelativeTimeContext);
if (!formatter) {
throw new Error('useRelativeTime must be used within RelativeTimeProvider');
}
return formatter;
}
// Komponenten-Verwendung
function CommentTimestamp({ date }) {
const formatter = useRelativeTime();
return <time>{formatter.format(date)}</time>;
}
Dieses Muster stellt sicher, dass Formatter einmal pro Locale erstellt und über alle Komponenten hinweg geteilt werden, die relative Zeitformatierung benötigen.
Browser-Unterstützung
Intl.RelativeTimeFormat funktioniert in allen modernen Browsern mit 95% globaler Abdeckung:
- Chrome 71+
- Firefox 65+
- Safari 14+
- Edge 79+
Internet Explorer unterstützt diese API nicht. Für Anwendungen, die IE-Unterstützung benötigen, sind Polyfills verfügbar, obwohl die native Implementierung bessere Performance und kleinere Bundle-Größen bietet.
Wann diese API verwendet werden sollte
Intl.RelativeTimeFormat eignet sich am besten für:
- Anzeige des Alters von Inhalten in Feeds und Timelines
- Anzeige von Kommentar- oder Beitragszeitstempeln
- Formatierung der Ereignisplanung relativ zur aktuellen Zeit
- Aufbau von Benachrichtigungssystemen mit relativen Zeitstempeln
- Erstellung von Aktivitätsprotokollen mit menschenlesbaren Zeiten
Die API ist nicht geeignet für:
- Absolute Datums- und Zeitformatierung (verwenden Sie
Intl.DateTimeFormat) - Präzise Zeiterfassung, die Millisekunden-Genauigkeit erfordert
- Countdown-Timer, die jede Sekunde aktualisiert werden
- Datumsarithmetik oder Kalenderberechnungen
Für Anwendungen, die sowohl relative als auch absolute Zeitanzeigen erfordern, kombinieren Sie Intl.RelativeTimeFormat mit Intl.DateTimeFormat. Zeigen Sie relative Zeiten für aktuelle Inhalte an und wechseln Sie zu absoluten Datumsangaben für ältere Inhalte:
function formatTimestamp(date, locale = 'en') {
const now = new Date();
const diffInMs = Math.abs(date - now);
const sevenDaysInMs = 7 * 24 * 60 * 60 * 1000;
if (diffInMs < sevenDaysInMs) {
const rtf = new RelativeTimeFormatter(locale);
return rtf.format(date);
} else {
const dtf = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric'
});
return dtf.format(date);
}
}
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
console.log(formatTimestamp(yesterday));
// "gestern"
const lastMonth = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
console.log(formatTimestamp(lastMonth));
// "14. Sep. 2025"
Dieser hybride Ansatz bietet die natürlichsprachlichen Vorteile relativer Zeiten für aktuelle Inhalte und behält gleichzeitig die Klarheit für ältere Zeitstempel bei.