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 l'emplacement de l'utilisateur, confusion et erreurs s'ensuivent. Un serveur peut stocker l'heure d'un événement comme "2024-03-15T20:00:00Z" et afficher "20h00" directement à partir de cette valeur UTC. Les utilisateurs dans différents fuseaux horaires voient la même heure mais l'interprètent comme étant locale, ce qui entraîne des rendez-vous manqués et des conflits d'horaire. Le problème fondamental est qu'un même moment dans le temps a différentes représentations selon l'emplacement géographique, et afficher des heures dans le mauvais fuseau les rend sans signification ou trompeuses.

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

Solution

Stockez tous les horodatages dans un format universel comme les chaînes ISO 8601 avec indicateurs UTC ou les timestamps Unix. Lors de l'affichage de ces heures aux utilisateurs, convertissez-les en objets Date JavaScript et formatez-les à l'aide d'APIs 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 utilisent cette information 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, et chaque client le rend selon son propre fuseau horaire, garantissant que chacun voit l'heure locale correcte pour le même moment global.

Étapes

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

Assurez-vous que votre API ou 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 dans un objet Date JavaScript, le navigateur la stockera en interne comme un 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"
        />
        {" à "}
        <FormattedTime value={startTime} />
        {" jusqu'à "}
        <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 à 5: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} à {formattedTime}
      </time>
    </div>
  );
}

Les méthodes formatDate et formatTime renvoient des chaînes formatées selon la locale et le fuseau horaire de l'utilisateur. L'attribut dateTime conserve la chaîne ISO originale pour la lisibilité machine tandis que le texte affiché montre l'heure locale conviviale pour l'utilisateur.