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

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

المشكلة

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

الحل

قم بتمرير خيار timeZone إلى طرق تنسيق التاريخ للتحكم في كيفية تفسير التواريخ وعرضها. تقبل دالة formatDate معاملات Intl.DateTimeFormatOptions التي تتضمن إعدادات المنطقة الزمنية. قم بتخزين الطوابع الزمنية بتنسيق عالمي مثل سلاسل ISO 8601 أو طوابع Unix الزمنية، ثم قم بتنسيقها على جانب العميل حيث تكون المنطقة الزمنية للمتصفح متاحة تلقائياً. تستخدم واجهة Intl.DateTimeFormat API المنطقة الزمنية الافتراضية للمستخدم عندما لا يتم تحديد خيار 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 APIs مع 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 على الوقت المحلي الدقيق للمستخدمين الذين يمررون المؤشر فوقه. يحترم كلا التنسيقين تلقائيًا المنطقة الزمنية واللغة المحلية للمستخدم.