كيفية التعامل مع المناطق الزمنية في TanStack Start v1

عرض الأوقات في المنطقة الزمنية المحلية للمستخدم

المشكلة

عندما تعرض التطبيقات الأوقات، فإنها تمثل لحظة واحدة تظهر بشكل مختلف اعتمادًا على موقع المشاهد. إذا قام الخادم بعرض "8:00 مساءً" في المنطقة الزمنية الخاصة به أو بتوقيت UTC، فسيرى المستخدمون في مناطق زمنية مختلفة وقتًا محليًا غير صحيح، مما يؤدي إلى الارتباك حول وقت حدوث الأحداث فعليًا. يتوقع المستخدمون أن تعكس الأوقات تلقائيًا المنطقة الزمنية المحلية الخاصة بهم، ولكن بدون المعالجة المناسبة، يرون الأوقات في أي منطقة زمنية استخدمها الخادم أثناء العرض. يتسبب هذا التناقض في تفويت المستخدمين للمواعيد، وسوء فهم الجداول الزمنية، وفقدان الثقة في موثوقية التطبيق.

الحل

قم بتمرير خيار timeZone إلى طرق تنسيق التاريخ للتحكم في كيفية تفسير التواريخ وعرضها. تقبل وظيفة formatDate خيارات Intl.DateTimeFormatOptions التي تتضمن تكوين المنطقة الزمنية. قم بتخزين الطوابع الزمنية بتنسيق عالمي مثل سلاسل ISO 8601 أو طوابع زمنية Unix، ثم قم بتنسيقها على العميل حيث تكون المنطقة الزمنية للمتصفح متاحة تلقائيًا. تستخدم واجهة برمجة تطبيقات Intl.DateTimeFormat المنطقة الزمنية الافتراضية للمستخدم عندما لا يتم تحديد خيار timeZone، مما يضمن عرض الأوقات بشكل صحيح لموقع كل مستخدم دون الحاجة إلى اكتشاف المنطقة الزمنية يدويًا.

الخطوات

1. إنشاء مكون لتنسيق التاريخ مع مراعاة المنطقة الزمنية

استخدم خطاف useIntl للوصول إلى كائن intl وطريقة formatDate الخاصة به. يقبل هذا المكون طابعًا زمنيًا بتنسيق ISO ويقوم بتنسيقه للمنطقة الزمنية المحلية للمستخدم.

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>;
}

يستخدم مكون FormattedDate وطريقة formatDate واجهات برمجة تطبيقات Intl.DateTimeFormat مع DateTimeFormatOptions. يقوم المتصفح تلقائيًا بتطبيق المنطقة الزمنية للمستخدم عندما لا يتم توفير timeZone صريح، مما يحول الطابع الزمني UTC إلى الوقت المحلي.

2. استخدم المكون لعرض أوقات الأحداث

قم باستيراد واستخدام مكون LocalTime أينما تحتاج إلى عرض الطوابع الزمنية. يتعامل المكون مع تحويل المنطقة الزمنية تلقائيًا بناءً على إعدادات متصفح كل مستخدم.

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>
  );
}

يرى كل مستخدم أوقات الحدث محولة إلى منطقتهم الزمنية المحلية. يتم تحليل الطابع الزمني بصيغة ISO من الخادم وتنسيقه وفقًا لتفضيلات اللغة والمنطقة الزمنية للمستخدم.

3. تنسيق الأوقات مع عرض المنطقة الزمنية بشكل صريح

عندما يحتاج المستخدمون إلى معرفة المنطقة الزمنية التي يمثلها الوقت، قم بتضمين اسم المنطقة الزمنية في خيارات التنسيق.

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>;
}

تحديد خيار timeZone يجبر المنسق على عرض الوقت في تلك المنطقة الزمنية المحددة. عندما تكون المنطقة الزمنية غير محددة، يستخدم المتصفح المنطقة الزمنية المحلية للمستخدم. هذا مفيد لعرض الأوقات في منطقة زمنية محددة بغض النظر عن موقع المستخدم.

4. إنشاء مساعد لعرض الوقت النسبي

للأحداث الحديثة، اعرض الأوقات النسبية مثل "قبل ساعتين" مع الحفاظ على الوقت المطلق في تلميح عند التحويم.

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>
  );
}

يقوم الوقت النسبي بتحديث العرض لإظهار المدد الزمنية بطريقة مألوفة للمستخدم بينما تحتفظ خاصية العنوان (title) بالوقت المحلي الدقيق للمستخدمين الذين يحومون بالمؤشر. كلا التنسيقين يحترمان تلقائيًا المنطقة الزمنية واللغة المحلية للمستخدم.