Comment formater le temps relatif dans TanStack Start v1
Formater les horodatages en expressions du type « il y a 2 jours »
Problème
Afficher les horodatages sous forme d'expressions de temps relatif comme « il y a 2 jours » ou « dans 3 heures » rend l'information temporelle plus intuitive pour les utilisateurs. Cependant, ces expressions suivent des règles grammaticales complexes qui varient selon la langue. L'anglais place « ago » après les quantités passées et « in » avant les quantités futures, mais d'autres langues peuvent utiliser des ordres de mots différents, fléchir les unités de temps ou employer des structures grammaticales entièrement différentes. Construire manuellement ces expressions par concaténation de chaînes produit un résultat incorrect dans toutes les langues sauf celle que vous avez codée en dur.
L'API de formatage sous-jacente nécessite une valeur numérique et une unité de temps, mais les horodatages arrivent sous forme d'objets Date ou de valeurs en millisecondes. Convertir un horodatage dans l'unité et la valeur appropriées nécessite de calculer la différence par rapport au moment actuel et de sélectionner l'unité la plus naturelle pour exprimer cette différence.
Solution
Calculer la différence de temps entre un horodatage et le moment actuel, sélectionner l'unité la plus appropriée pour cette différence, puis formater le résultat en utilisant un formatage de temps relatif spécifique à la locale. Cela transforme les horodatages bruts en expressions grammaticalement correctes qui s'adaptent à la langue de l'utilisateur.
Utiliser le formatage de temps relatif de react-intl avec une fonction auxiliaire qui détermine la meilleure unité. La fonction auxiliaire compare la différence de temps par rapport à des seuils pour chaque unité et renvoie à la fois la valeur numérique et le nom de l'unité. Le formateur applique ensuite les règles grammaticales correctes pour la locale active.
Étapes
1. Créer une fonction auxiliaire de sélection d'unité
Construire une fonction qui calcule la différence de temps et sélectionne l'unité la plus naturelle pour l'exprimer.
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" };
}
Cette fonction convertit un horodatage en une valeur et une unité relatives en comparant la différence de temps à des seuils fixes. Elle renvoie des valeurs négatives pour les temps passés et des valeurs positives pour les temps futurs, que le formateur interprète correctement.
2. Créer un composant de temps relatif
Construisez un composant qui combine l'assistant de sélection d'unité avec le formatage de react-intl.
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}
/>
);
}
Ce composant accepte un horodatage et des options de formatage, calcule la valeur et l'unité relatives, puis délègue au composant de react-intl pour un rendu adapté à la locale. L'option numeric="auto" produit des phrases comme "hier" au lieu de "il y a 1 jour" lorsque cela est approprié.
3. Utiliser le composant dans vos routes
Importez et affichez le composant partout où vous devez afficher des horodatages relatifs.
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>
Published <RelativeTime date={post.publishedAt} />
</p>
<p>
Updated <RelativeTime date={post.updatedAt} style="short" />
</p>
</article>
);
}
Le composant fonctionne dans les contextes de rendu côté serveur et côté client. Sur le serveur, il produit la phrase de temps relatif initiale, et sur le client, il affiche la même phrase en utilisant la locale de l'utilisateur depuis l'IntlProvider.
4. Ajouter une option de formatage impératif
Pour les cas où vous avez besoin de la chaîne formatée directement, créez un assistant basé sur un hook.
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);
};
}
Ce hook renvoie une fonction qui formate les horodatages de manière impérative, utile pour définir des attributs de texte ou calculer des valeurs en dehors du JSX.
5. Utiliser le hook pour les contextes hors composant
Appelez le hook dans les composants où vous avez besoin de chaînes formatées pour les attributs ou la logique.
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>
);
}
Le hook fournit la même logique de formatage sous forme fonctionnelle, vous permettant d'utiliser des chaînes de temps relatif dans les attributs, les valeurs calculées ou partout où un composant ne peut pas être affiché.