Wie man Zeitzonen in React Router v7 handhabt

Zeiten in der lokalen Zeitzone des Benutzers anzeigen

Problem

Wenn Anwendungen Uhrzeiten anzeigen, ohne den Standort des Benutzers zu berücksichtigen, führt dies zu Verwirrung und Fehlern. Ein Server könnte eine Ereigniszeit als "2024-03-15T20:00:00Z" speichern und "8:00 PM" direkt aus diesem UTC-Wert rendern. Benutzer in verschiedenen Zeitzonen sehen dieselbe Uhrzeit, interpretieren sie aber als ihre lokale Zeit, was zu verpassten Terminen und Terminplanungskonflikten führt. Das grundlegende Problem besteht darin, dass ein einzelner Zeitpunkt je nach geografischem Standort unterschiedliche Darstellungen hat, und die Anzeige von Uhrzeiten in der falschen Zone macht sie bedeutungslos oder irreführend.

Dieses Problem verschärft sich, wenn Benutzer über Regionen hinweg zusammenarbeiten. Ein Ereignis, das für 15:00 Uhr auf dem Server geplant ist, wird in der Benutzeroberfläche überall als 15:00 Uhr angezeigt, obwohl es für denselben tatsächlichen Moment in Tokio als 6:00 Uhr, in London als 14:00 Uhr und in New York als 9:00 Uhr erscheinen sollte.

Lösung

Speichere alle Zeitstempel in einem universellen Format wie ISO 8601-Strings mit UTC-Indikatoren oder Unix-Zeitstempeln. Bei der Anzeige dieser Zeiten für Benutzer wandle sie in JavaScript Date-Objekte um und formatiere sie mit Internationalisierungs-APIs, die die Zeitzone des Browsers berücksichtigen. Moderne Browser erkennen automatisch die lokale Zeitzone des Benutzers, und Formatierungsbibliotheken nutzen dies, um UTC-Zeitstempel ohne manuelle Offset-Berechnungen in die korrekte lokale Darstellung umzuwandeln.

Dieser Ansatz trennt Speicherung von Präsentation. Der Server sendet einen kanonischen Zeitstempel, und jeder Client rendert ihn entsprechend seiner eigenen Zeitzone, wodurch sichergestellt wird, dass jeder die korrekte lokale Zeit für denselben globalen Moment sieht.

Schritte

1. Sende Zeitstempel im ISO 8601-Format von deiner Datenquelle

Stellen Sie sicher, dass Ihre API oder Ihr Datenloader Zeitstempel als ISO 8601-Strings mit UTC-Indikatoren oder als Unix-Zeitstempel in Millisekunden zurückgibt.

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

Das Suffix Z kennzeichnet die UTC-Zeit. Wenn dieser String in ein JavaScript Date-Objekt geparst wird, speichert der Browser ihn intern als UTC-Zeitstempel.

2. Erstellen Sie eine Komponente, die Zeiten mit react-intl formatiert

Importieren Sie Formatierungskomponenten aus react-intl und übergeben Sie Date-Objekte, die aus Ihren ISO-Strings erstellt wurden.

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"
        />
        {" um "}
        <FormattedTime value={startTime} />
        {" bis "}
        <FormattedTime value={endTime} />
      </p>
    </div>
  );
}

Die Komponenten FormattedDate und FormattedTime verwenden automatisch die vom Browser erkannte Zeitzone, um die korrekte lokale Zeit anzuzeigen. Ein Benutzer in New York sieht "15. März 2024 um 15:00 Uhr", während ein Benutzer in Tokio "16. März 2024 um 5:00 Uhr" für denselben UTC-Zeitstempel sieht.

3. Verwenden Sie die imperative API für dynamische Formatierungsanforderungen

Wenn Sie formatierte Zeiten in Attributen, berechneten Werten oder Nicht-JSX-Kontexten benötigen, verwenden Sie den useIntl-Hook.

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} um {formattedTime}
      </time>
    </div>
  );
}

Die Methoden formatDate und formatTime geben Strings zurück, die gemäß der Sprache und Zeitzone des Benutzers formatiert sind. Das Attribut dateTime bewahrt den ursprünglichen ISO-String für die maschinelle Lesbarkeit, während der angezeigte Text die benutzerfreundliche lokale Zeit zeigt.