Wie man relative Zeitangaben in TanStack Start v1 formatiert
Zeitstempel als Phrasen wie '2 Tage zuvor' formatieren
Problem
Die Anzeige von Zeitstempeln als relative Zeitangaben wie "vor 2 Tagen" oder "in 3 Stunden" macht zeitliche Informationen für Benutzer intuitiver. Diese Ausdrücke folgen jedoch komplexen grammatikalischen Regeln, die je nach Sprache variieren. Im Englischen steht "ago" nach Zeitangaben in der Vergangenheit und "in" vor Zeitangaben in der Zukunft, aber andere Sprachen verwenden möglicherweise andere Wortstellungen, beugen Zeiteinheiten oder nutzen völlig andere grammatikalische Strukturen. Die manuelle Konstruktion dieser Ausdrücke durch Verkettung von Zeichenketten erzeugt in jeder Sprache außer der, die man fest codiert hat, fehlerhafte Ausgaben.
Die zugrunde liegende Formatierungs-API benötigt einen numerischen Wert und eine Zeiteinheit, aber Zeitstempel werden als Date-Objekte oder Millisekunden-Werte übermittelt. Die Umwandlung eines Zeitstempels in die entsprechende Einheit und den entsprechenden Wert erfordert die Berechnung der Differenz zum aktuellen Zeitpunkt und die Auswahl der natürlichsten Einheit, um diese Differenz auszudrücken.
Lösung
Berechne die Zeitdifferenz zwischen einem Zeitstempel und dem aktuellen Moment, wähle die am besten geeignete Einheit für diese Differenz und formatiere das Ergebnis mit sprachspezifischer relativer Zeitformatierung. Dies wandelt rohe Zeitstempel in grammatikalisch korrekte Ausdrücke um, die sich an die Sprache des Benutzers anpassen.
Verwende die relative Zeitformatierung von react-intl mit einer Hilfsfunktion, die die beste Einheit bestimmt. Die Hilfsfunktion vergleicht die Zeitdifferenz mit Schwellenwerten für jede Einheit und gibt sowohl den numerischen Wert als auch den Namen der Einheit zurück. Der Formatierer wendet dann die korrekten grammatikalischen Regeln für die aktive Sprache an.
Schritte
1. Erstelle einen Einheiten-Auswahlhelfer
Baue eine Funktion, die die Zeitdifferenz berechnet und die natürlichste Einheit auswählt, um sie auszudrücken.
type RelativeTimeUnit =
| "second"
| "minute"
| "hour"
| "day"
| "week"
| "month"
| "year";
interface RelativeTimeValue {
value: number;
unit: RelativeTimeUnit;
}
export function selectRelativeTimeUnit(
timestamp: Date | number,
baseTime: Date | number = Date.now(),
): RelativeTimeValue {
const date = typeof timestamp === "number" ? timestamp : timestamp.getTime();
const base = typeof baseTime === "number" ? baseTime : baseTime.getTime();
const diffMs = date - base;
const absDiff = Math.abs(diffMs);
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
const week = 7 * day;
const month = 30 * day;
const year = 365 * day;
if (absDiff < minute) {
return { value: Math.round(diffMs / 1000), unit: "second" };
}
if (absDiff < hour) {
return { value: Math.round(diffMs / minute), unit: "minute" };
}
if (absDiff < day) {
return { value: Math.round(diffMs / hour), unit: "hour" };
}
if (absDiff < week) {
return { value: Math.round(diffMs / day), unit: "day" };
}
if (absDiff < month) {
return { value: Math.round(diffMs / week), unit: "week" };
}
if (absDiff < year) {
return { value: Math.round(diffMs / month), unit: "month" };
}
return { value: Math.round(diffMs / year), unit: "year" };
}
Diese Funktion konvertiert einen Zeitstempel in einen relativen Wert und eine Einheit, indem sie die Zeitdifferenz mit festen Schwellenwerten vergleicht. Sie gibt negative Werte für vergangene Zeiten und positive Werte für zukünftige Zeiten zurück, die der Formatierer korrekt interpretiert.
2. Erstelle eine Komponente für relative Zeitangaben
Baue eine Komponente, die den Unit-Selection-Helper mit der Formatierung von react-intl kombiniert.
import { FormattedRelativeTime } from "react-intl";
import { selectRelativeTimeUnit } from "./selectRelativeTimeUnit";
interface RelativeTimeProps {
date: Date | number;
numeric?: "always" | "auto";
style?: "long" | "short" | "narrow";
}
export function RelativeTime({
date,
numeric = "auto",
style = "long",
}: RelativeTimeProps) {
const { value, unit } = selectRelativeTimeUnit(date);
return (
<FormattedRelativeTime
value={value}
unit={unit}
numeric={numeric}
style={style}
/>
);
}
Diese Komponente akzeptiert einen Zeitstempel und Formatierungsoptionen, berechnet den relativen Wert und die Einheit und delegiert dann an die Komponente von react-intl für die sprachabhängige Darstellung. Die Option numeric="auto" erzeugt Phrasen wie "gestern" anstatt "vor 1 Tag", wenn es angemessen ist.
3. Verwende die Komponente in deinen Routen
Importiere und rendere die Komponente überall dort, wo du relative Zeitstempel anzeigen möchtest.
import { createFileRoute } from "@tanstack/react-router";
import { RelativeTime } from "../components/RelativeTime";
export const Route = createFileRoute("/posts/$postId")({
component: PostPage,
});
function PostPage() {
const post = {
title: "Understanding Relative Time",
publishedAt: new Date("2024-11-15T10:30:00Z"),
updatedAt: new Date(Date.now() - 2 * 60 * 60 * 1000),
};
return (
<article>
<h1>{post.title}</h1>
<p>
Veröffentlicht <RelativeTime date={post.publishedAt} />
</p>
<p>
Aktualisiert <RelativeTime date={post.updatedAt} style="short" />
</p>
</article>
);
}
Die Komponente funktioniert sowohl in server-gerenderten als auch in client-gerenderten Kontexten. Auf dem Server erzeugt sie die anfängliche relative Zeitphrase, und auf dem Client zeigt sie dieselbe Phrase unter Verwendung der Sprache des Benutzers aus dem IntlProvider an.
4. Füge eine imperative Formatierungsoption hinzu
Für Fälle, in denen du den formatierten String direkt benötigst, erstelle einen hook-basierten Helper.
import { useIntl } from "react-intl";
import { selectRelativeTimeUnit } from "./selectRelativeTimeUnit";
export function useRelativeTime() {
const intl = useIntl();
return (
date: Date | number,
options?: {
numeric?: "always" | "auto";
style?: "long" | "short" | "narrow";
},
) => {
const { value, unit } = selectRelativeTimeUnit(date);
return intl.formatRelativeTime(value, unit, options);
};
}
Dieser Hook gibt eine Funktion zurück, die Zeitstempel imperativ formatiert, was nützlich ist, um Textattribute zu setzen oder Werte außerhalb von JSX zu berechnen.
5. Verwenden Sie den Hook für Nicht-Komponenten-Kontexte
Rufen Sie den Hook in Komponenten auf, in denen Sie formatierte Zeichenketten für Attribute oder Logik benötigen.
import { createFileRoute } from "@tanstack/react-router";
import { useRelativeTime } from "../hooks/useRelativeTime";
export const Route = createFileRoute("/events/$eventId")({
component: EventPage,
});
function EventPage() {
const formatRelativeTime = useRelativeTime();
const event = {
name: "Product Launch",
startTime: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
};
const timeUntil = formatRelativeTime(event.startTime);
return (
<div>
<h1>{event.name}</h1>
<time dateTime={event.startTime.toISOString()} title={timeUntil}>
{timeUntil}
</time>
</div>
);
}
Der Hook stellt dieselbe Formatierungslogik in funktionaler Form bereit, sodass Sie relative Zeitangaben in Attributen, berechneten Werten oder überall dort verwenden können, wo eine Komponente nicht gerendert werden kann.