Comment gérer les fuseaux horaires dans TanStack Start v1
Afficher les heures dans le fuseau horaire local de l'utilisateur
Problème
Lorsque les applications affichent des heures, elles représentent un moment unique qui apparaît différemment selon la localisation de l'utilisateur. Si un serveur affiche « 20:00 » dans son propre fuseau horaire ou en UTC, les utilisateurs dans différents fuseaux horaires verront une heure locale incorrecte, ce qui entraînera une confusion quant au moment réel des événements. Les utilisateurs s'attendent à ce que les heures reflètent automatiquement leur fuseau horaire local, mais sans gestion appropriée, ils voient les heures dans le fuseau horaire utilisé par le serveur lors du rendu. Ce décalage amène les utilisateurs à manquer des rendez-vous, à mal comprendre les plannings et à perdre confiance dans la fiabilité de l'application.
Solution
Passez une option timeZone aux méthodes de formatage de date pour contrôler comment les dates sont interprétées et affichées. La fonction formatDate accepte Intl.DateTimeFormatOptions qui incluent la configuration du fuseau horaire. Stockez les horodatages dans un format universel comme les chaînes ISO 8601 ou les horodatages Unix, puis formatez-les côté client où le fuseau horaire du navigateur est automatiquement disponible. L'API Intl.DateTimeFormat utilise le fuseau horaire par défaut de l'utilisateur lorsqu'aucune option timeZone n'est spécifiée, garantissant que les heures s'affichent correctement pour la localisation de chaque utilisateur sans détection manuelle du fuseau horaire.
Étapes
1. Créer un composant de formatage de date tenant compte du fuseau horaire
Utilisez le hook useIntl pour accéder à l'objet intl et à sa méthode formatDate. Ce composant accepte un horodatage ISO et le formate pour le fuseau horaire local de l'utilisateur.
import { useIntl } from "react-intl";
interface LocalTimeProps {
timestamp: string;
showDate?: boolean;
showTime?: boolean;
}
export function LocalTime({
timestamp,
showDate = true,
showTime = true,
}: LocalTimeProps) {
const intl = useIntl();
const date = new Date(timestamp);
const formatted = intl.formatDate(date, {
...(showDate && {
year: "numeric",
month: "short",
day: "numeric",
}),
...(showTime && {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
}),
});
return <time dateTime={timestamp}>{formatted}</time>;
}
Le composant FormattedDate et la méthode formatDate utilisent les API Intl.DateTimeFormat avec DateTimeFormatOptions. Le navigateur applique automatiquement le fuseau horaire de l'utilisateur lorsqu'aucun timeZone explicite n'est fourni, convertissant l'horodatage UTC en heure locale.
2. Utiliser le composant pour afficher les heures d'événements
Importez et utilisez le composant LocalTime partout où vous devez afficher des horodatages. Le composant gère automatiquement la conversion de fuseau horaire en fonction des paramètres du navigateur de chaque utilisateur.
import { createFileRoute } from "@tanstack/react-router";
import { LocalTime } from "~/components/LocalTime";
export const Route = createFileRoute("/events/$eventId")({
component: EventDetail,
});
function EventDetail() {
const event = Route.useLoaderData();
return (
<div>
<h1>{event.title}</h1>
<p>
Starts: <LocalTime timestamp={event.startTime} />
</p>
<p>
Ends: <LocalTime timestamp={event.endTime} />
</p>
</div>
);
}
Chaque utilisateur voit les heures d'événements converties dans son fuseau horaire local. L'horodatage ISO provenant du serveur est analysé et formaté selon les préférences de locale et de fuseau horaire de l'utilisateur.
3. Formater les heures avec affichage explicite du fuseau horaire
Lorsque les utilisateurs doivent savoir quel fuseau horaire représente une heure, incluez le nom du fuseau horaire dans les options de formatage.
import { useIntl } from "react-intl";
interface ExplicitTimezoneProps {
timestamp: string;
timezone?: string;
}
export function ExplicitTimezone({
timestamp,
timezone,
}: ExplicitTimezoneProps) {
const intl = useIntl();
const date = new Date(timestamp);
const formatted = intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: timezone,
timeZoneName: "long",
});
return <time dateTime={timestamp}>{formatted}</time>;
}
Spécifier une option timeZone force le formateur à afficher l'heure dans ce fuseau horaire spécifique. Lorsque timezone est undefined, le navigateur utilise le fuseau horaire local de l'utilisateur. Ceci est utile pour afficher les heures dans un fuseau horaire spécifique, quel que soit l'endroit où se trouve l'utilisateur.
4. Créer un helper pour l'affichage de l'heure relative
Pour les événements récents, affichez des heures relatives comme « il y a 2 heures » tout en préservant l'heure absolue dans une info-bulle.
import { useIntl } from "react-intl";
interface RelativeTimeProps {
timestamp: string;
}
export function RelativeTime({ timestamp }: RelativeTimeProps) {
const intl = useIntl();
const date = new Date(timestamp);
const now = Date.now();
const diffInSeconds = Math.floor((now - date.getTime()) / 1000);
const absoluteTime = intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
});
let value: number;
let unit: Intl.RelativeTimeFormatUnit;
if (diffInSeconds < 60) {
value = -diffInSeconds;
unit = "second";
} else if (diffInSeconds < 3600) {
value = -Math.floor(diffInSeconds / 60);
unit = "minute";
} else if (diffInSeconds < 86400) {
value = -Math.floor(diffInSeconds / 3600);
unit = "hour";
} else {
value = -Math.floor(diffInSeconds / 86400);
unit = "day";
}
const relativeTime = intl.formatRelativeTime(value, unit);
return (
<time dateTime={timestamp} title={absoluteTime}>
{relativeTime}
</time>
);
}
L'heure relative met à jour l'affichage pour montrer des durées conviviales tandis que l'attribut title préserve l'heure locale exacte pour les utilisateurs qui survolent. Les deux formats respectent automatiquement le fuseau horaire et la locale de l'utilisateur.