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, während Inhalte altern.

Die Entwicklung dieser Funktionalität von Grund auf stellt Herausforderungen dar. Sie müssen Zeitdifferenzen berechnen, geeignete Einheiten auswählen, Pluralisierungsregeln über Sprachen hinweg handhaben und Übersetzungen für jede unterstützte Locale pflegen. Diese Komplexität erklärt, warum Entwickler traditionell zu Bibliotheken wie Moment.js griffen und damit erhebliche Bundle-Größe für eine scheinbar einfache Formatierung hinzufügten.

Die Intl.RelativeTimeFormat API bietet eine native Lösung. Sie formatiert relative Zeitangaben mit vollständiger Internationalisierungsunterstützung und handhabt Pluralisierungsregeln und kulturelle Konventionen automatisch. Die API funktioniert in allen gängigen Browsern mit 95 % globaler Abdeckung, eliminiert die Notwendigkeit externer Abhängigkeiten und erzeugt gleichzeitig natürlich klingende Ausgaben in Dutzenden von Sprachen.

Grundlegende Verwendung

Der Intl.RelativeTimeFormat Konstruktor erstellt eine Formatter-Instanz, die numerische Werte und Zeiteinheiten in lokalisierte Strings konvertiert.

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 angibt
  • unit: Ein String, der die Zeiteinheit spezifiziert

Negative Werte zeigen vergangene Zeiten an, positive Werte zeigen zukünftige Zeiten an. Die API handhabt die Pluralisierung automatisch und erzeugt „vor 1 Tag" oder „vor 2 Tagen" basierend auf dem Wert.

Unterstützte Zeiteinheiten

Die API unterstützt acht Zeiteinheiten, die jeweils sowohl Singular- als auch Pluralformen akzeptieren:

const rtf = new Intl.RelativeTimeFormat('en');

// These produce identical output
console.log(rtf.format(-5, 'second'));
// "5 seconds ago"

console.log(rtf.format(-5, 'seconds'));
// "5 seconds ago"

Verfügbare Einheiten von der kleinsten zur größten:

  • second oder seconds
  • minute oder minutes
  • hour oder hours
  • day oder days
  • week oder weeks
  • month oder months
  • quarter oder quarters
  • year oder years

Die Quartal-Einheit erweist sich als nützlich in Geschäftsanwendungen zur Verfolgung von Geschäftsperioden, während die anderen typische Anforderungen an die relative Zeitformatierung abdecken.

Natürlichsprachige Ausgabe

Die Option numeric steuert, ob der Formatter numerische Werte oder natürlichsprachige 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"

Wenn numeric auf auto gesetzt wird, werden idiomatischere Formulierungen für gängige Werte erzeugt:

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ürlichsprachige Ausgabe schafft dialogorientiertere Benutzeroberflächen. Die Option auto funktioniert über alle Zeiteinheiten hinweg, wobei 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 Oberflächenkontexte 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 Standardoberflächen, bei denen Lesbarkeit am wichtigsten ist. Verwenden Sie den Stil short für platzbeschränkte Layouts wie mobile Oberflächen oder Datentabellen. Verwenden Sie den Stil narrow für extrem kompakte Anzeigen, bei denen jedes Zeichen zählt.

Berechnung von Zeitdifferenzen

Die API Intl.RelativeTimeFormat formatiert Werte, berechnet sie jedoch nicht. Sie müssen Zeitdifferenzen berechnen und geeignete Einheiten selbst auswählen. Diese Trennung der Zuständigkeiten gibt Ihnen die Kontrolle über die Berechnungslogik, während die Formatierungskomplexität an die API delegiert wird.

Berechnung der grundlegenden Zeitdifferenz

Für eine bestimmte Zeiteinheit berechnen Sie die Differenz zwischen zwei Daten:

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. Kommentar-Zeitstempel verwenden möglicherweise immer Stunden oder Tage, während die Ereignisplanung sich auf Tage oder Wochen konzentrieren könnte.

Automatische Einheitenauswahl

Für die allgemeine relative Zeitformatierung wählen Sie die am besten geeignete Einheit basierend auf der Größe der Zeitdifferenz:

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 die Zeitdifferenz den Millisekundenwert 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 variable Monatslängen zu berücksichtigen. Diese Näherung funktioniert gut für relative Zeitanzeigen, bei denen Lesbarkeit wichtiger ist als Präzision.

Unterstützung für Internationalisierung

Der Formatter respektiert gebietsschemaspezifische Konventionen für die relative Zeitanzeige. Verschiedene Sprachen haben unterschiedliche Pluralisierungsregeln, unterschiedliche Wortreihenfolgen und unterschiedliche 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'));
// "昨日"

Pluralisierungsregeln variieren erheblich zwischen den Sprachen. Englisch unterscheidet zwischen eins 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 Formatter behandelt auch die Textrichtung für Rechts-nach-Links-Sprachen und wendet kulturell angemessene Formatierungskonventionen an. Diese automatische Lokalisierung eliminiert die Notwendigkeit, Übersetzungsdateien zu pflegen oder benutzerdefinierte Pluralisierungslogik zu implementieren.

Erweiterte Formatierung mit formatToParts

Die formatToParts()-Methode gibt die formatierte Zeichenkette als Array von Objekten zurück und ermöglicht benutzerdefiniertes Styling oder Manipulation einzelner Komponenten.

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 Part-Objekt enthält:

  • type: Entweder integer für numerische Werte oder literal für Text
  • value: Der String-Inhalt dieses Parts
  • unit: Die Zeiteinheit (vorhanden bei Integer-Parts)

Diese Struktur ermöglicht benutzerdefiniertes Rendering, bei dem Sie Zahlen anders als Text stylen oder spezifische Komponenten für die Anzeige extrahieren möchten:

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ürlichsprachige Alternativen haben, gibt formatToParts() einen einzelnen Literal-Part 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 Ihnen zu erkennen, wann natürliche Sprache verwendet wird im Vergleich zu numerischer Formatierung, sodass Sie basierend auf dem Output-Typ unterschiedliches Styling oder Verhalten anwenden können.

Performance-Optimierung

Das Erstellen von Intl.RelativeTimeFormat-Instanzen beinhaltet das Laden von Locale-Daten und die Initialisierung von Formatierungsregeln. Diese Operation ist aufwendig genug, um unnötige Wiederholungen zu vermeiden.

Formatter-Instanzen cachen

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);
}

// Reuse cached formatters
const rtf = getFormatter('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day'));
// "yesterday"

Diese Caching-Strategie wird wichtig, wenn viele Timestamps formatiert werden, wie z. B. beim Rendern von Activity-Feeds oder Kommentar-Threads.

Berechnungs-Overhead minimieren

Speichern Sie Timestamps, anstatt relative Zeiten wiederholt zu berechnen:

// Store the creation date
const comment = {
  text: "Great article!",
  createdAt: new Date('2025-10-14T10:30:00Z')
};

// Calculate relative time only when rendering
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 Datenspeicherung von Präsentation und ermöglicht es Ihnen, relative Zeiten neu zu berechnen, wenn sich die Locale 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 Utility-Funktion, 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);
      }
    }
  }
}

// Usage
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 Unit-Selection-Logik und bietet ein sauberes Interface, das Date-Objekte akzeptiert und formatierte Strings zurückgibt.

Integration mit Frameworks

Erstellen Sie in React-Anwendungen den Formatter einmal und übergeben Sie 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;
}

// Component usage
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 erfordern, 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 Content-Alters in Feeds und Timelines
  • Anzeige von Kommentar- oder Post-Zeitstempeln
  • Formatierung von Event-Zeitplanung 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äzises Time-Tracking, das exakte Millisekunden-Genauigkeit erfordert
  • Countdown-Timer, die jede Sekunde aktualisiert werden
  • Datumsberechnungen oder Kalenderkalkulationen

Für Anwendungen, die sowohl relative als auch absolute Zeitanzeigen erfordern, kombinieren Sie Intl.RelativeTimeFormat mit Intl.DateTimeFormat. Zeigen Sie relative Zeiten für aktuellen Content an und wechseln Sie zu absoluten Daten für älteren Content:

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));
// "yesterday"

const lastMonth = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
console.log(formatTimestamp(lastMonth));
// "Sep 14, 2025"

Dieser hybride Ansatz bietet die Vorteile natürlicher Sprache durch relative Zeiten für aktuellen Content und gewährleistet gleichzeitig Klarheit für ältere Zeitstempel.