Comment gérer les fuseaux horaires dans React Router v7

Afficher les heures dans le fuseau horaire local de l'utilisateur

Problème

Lorsque les applications affichent des heures sans tenir compte de la localisation de l'utilisateur, cela entraîne confusion et erreurs. Un serveur peut stocker l'heure d'un événement sous la forme "2024-03-15T20:00:00Z" et afficher directement "20:00" à partir de cette valeur UTC. Les utilisateurs dans différents fuseaux horaires voient la même heure mais l'interprètent comme locale, ce qui entraîne des rendez-vous manqués et des conflits de planification. Le problème fondamental est qu'un même instant a différentes représentations selon la localisation géographique, et afficher les heures dans le mauvais fuseau les rend dénuées de sens ou trompeuses.

Ce problème s'aggrave lorsque les utilisateurs collaborent entre différentes régions. Un événement planifié pour 15:00 sur le serveur devient 15:00 partout dans l'interface, alors qu'il devrait apparaître comme 6:00 à Tokyo, 14:00 à Londres et 9:00 à New York pour le même instant réel.

Solution

Stockez tous les horodatages dans un format universel tel que les chaînes ISO 8601 avec indicateurs UTC ou les horodatages Unix. Lors de l'affichage de ces heures aux utilisateurs, convertissez-les en objets Date JavaScript et formatez-les à l'aide d'API d'internationalisation qui respectent le fuseau horaire du navigateur. Les navigateurs modernes détectent automatiquement le fuseau horaire local de l'utilisateur, et les bibliothèques de formatage exploitent cela pour convertir les horodatages UTC en représentation locale correcte sans calculs manuels de décalage.

Cette approche sépare le stockage de la présentation. Le serveur envoie un horodatage canonique unique, et chaque client l'affiche selon son propre fuseau horaire, garantissant que tout le monde voit l'heure locale correcte pour le même instant global.

Étapes

1. Envoyer les horodatages au format ISO 8601 depuis votre source de données

Assurez-vous que votre API ou votre chargeur de données renvoie les horodatages sous forme de chaînes ISO 8601 avec des indicateurs UTC ou sous forme d'horodatages Unix en millisecondes.

export async function loader() {
  const event = await fetchEvent();
  return {
    title: event.title,
    startTime: "2024-03-15T20:00:00Z",
    endTime: "2024-03-15T22:00:00Z",
  };
}

Le suffixe Z indique l'heure UTC. Lorsque cette chaîne est analysée en objet Date JavaScript, le navigateur la stocke en interne sous forme d'horodatage UTC.

2. Créer un composant qui formate les heures en utilisant react-intl

Importez les composants de formatage depuis react-intl et passez les objets Date créés à partir de vos chaînes ISO.

import { FormattedDate, FormattedTime } from "react-intl";
import type { Route } from "./+types/event";

export default function EventDetails({ loaderData }: Route.ComponentProps) {
  const startTime = new Date(loaderData.startTime);
  const endTime = new Date(loaderData.endTime);

  return (
    <div>
      <h1>{loaderData.title}</h1>
      <p>
        <FormattedDate
          value={startTime}
          year="numeric"
          month="long"
          day="numeric"
        />
        {" at "}
        <FormattedTime value={startTime} />
        {" to "}
        <FormattedTime value={endTime} />
      </p>
    </div>
  );
}

Les composants FormattedDate et FormattedTime utilisent automatiquement le fuseau horaire détecté par le navigateur pour afficher l'heure locale correcte. Un utilisateur à New York voit « 15 mars 2024 à 15:00 » tandis qu'un utilisateur à Tokyo voit « 16 mars 2024 à 05:00 » pour le même horodatage UTC.

3. Utiliser l'API impérative pour les besoins de formatage dynamique

Lorsque vous avez besoin d'heures formatées dans des attributs, des valeurs calculées ou des contextes non-JSX, utilisez le hook useIntl.

import { useIntl } from "react-intl";
import type { Route } from "./+types/event";

export default function EventCard({ loaderData }: Route.ComponentProps) {
  const intl = useIntl();
  const startTime = new Date(loaderData.startTime);

  const formattedDate = intl.formatDate(startTime, {
    year: "numeric",
    month: "short",
    day: "numeric",
  });

  const formattedTime = intl.formatTime(startTime, {
    hour: "numeric",
    minute: "2-digit",
  });

  return (
    <div>
      <h2>{loaderData.title}</h2>
      <time dateTime={loaderData.startTime}>
        {formattedDate} at {formattedTime}
      </time>
    </div>
  );
}

Les méthodes formatDate et formatTime renvoient des chaînes formatées selon les paramètres régionaux et le fuseau horaire de l'utilisateur. L'attribut dateTime préserve la chaîne ISO d'origine pour la lisibilité machine tandis que le texte affiché montre l'heure locale conviviale pour l'utilisateur.