كيفية تنسيق التواريخ في مناطق زمنية مختلفة

عرض التواريخ والأوقات لأي منطقة زمنية باستخدام واجهة برمجة التطبيقات Intl.DateTimeFormat في JavaScript

مقدمة

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

مستخدم في كاليفورنيا يحجز رحلة طيران تغادر في الساعة 2:00 مساءً يتوقع أن يرى 2:00 مساءً معروضة. إذا كان تطبيقك يُنسق جميع الأوقات باستخدام المنطقة الزمنية للخادم في فيرجينيا، سيرى المستخدم 5:00 مساءً ويصل إلى المطار متأخراً ثلاث ساعات. يتفاقم هذا الالتباس عبر كل وقت معروض في تطبيقك.

توفر واجهة برمجة التطبيقات Intl.DateTimeFormat في JavaScript خيار timeZone لتنسيق التواريخ والأوقات لأي منطقة زمنية. يشرح هذا الدرس سبب وجود المناطق الزمنية، وكيف تتعامل JavaScript معها داخلياً، وكيفية تنسيق التواريخ بشكل صحيح للمستخدمين في أي مكان في العالم.

لماذا توجد المناطق الزمنية

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

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

كل منطقة زمنية لها إزاحة قياسية عن التوقيت العالمي المنسق، المختصر UTC. تستخدم نيويورك UTC-5 أو UTC-4 حسب التوقيت الصيفي. تستخدم لندن UTC+0 أو UTC+1. تستخدم طوكيو UTC+9 على مدار العام.

عندما تعرض الأوقات للمستخدمين، تحتاج إلى التحويل من اللحظة العالمية إلى الوقت المحلي الذي يتوقعون رؤيته.

كيف يخزن JavaScript الأوقات داخلياً

تمثل كائنات Date في JavaScript لحظة واحدة في الوقت كعدد الميلي ثانية منذ 1 يناير 1970 في منتصف الليل UTC. هذا التمثيل الداخلي ليس له منطقة زمنية.

const date = new Date('2025-03-15T20:00:00Z');
console.log(date.getTime());
// Output: 1742331600000

يشير Z في نهاية سلسلة ISO إلى توقيت UTC. يخزن كائن Date الطابع الزمني 1742331600000، والذي يمثل نفس اللحظة للجميع في العالم، بغض النظر عن منطقتهم الزمنية.

عندما تستدعي دوال مثل toString() على كائن Date، يحول JavaScript الطابع الزمني UTC إلى منطقتك الزمنية المحلية للعرض. يمكن أن يخلق هذا التحويل التلقائي ارتباكاً عندما تريد عرض الأوقات لمنطقة زمنية مختلفة.

توفر واجهة برمجة التطبيقات Intl.DateTimeFormat مع خيار timeZone تحكماً صريحاً في المنطقة الزمنية المستخدمة للتنسيق.

استخدام خيار timeZone

يحدد خيار timeZone المنطقة الزمنية المستخدمة عند تنسيق التاريخ. مرر الخيار كجزء من كائن الخيارات عند إنشاء منسق.

const date = new Date('2025-03-15T20:00:00Z');

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  dateStyle: 'short',
  timeStyle: 'short'
});

console.log(formatter.format(date));
// Output: "3/15/25, 3:00 PM"

يمثل التاريخ الساعة 8:00 مساءً UTC. نيويورك هي UTC-5 خلال التوقيت القياسي أو UTC-4 خلال التوقيت الصيفي. في مارس، يكون التوقيت الصيفي نشطاً، لذا نيويورك هي UTC-4. يحول المنسق 8:00 مساءً UTC إلى 4:00 مساءً بالتوقيت المحلي، لكن المثال يظهر 3:00 مساءً، مما يشير إلى أن هذا خلال التوقيت القياسي في هذا السيناريو المحدد.

يتعامل المُنسِّق مع التحويل تلقائيًا. أنت تُقدِّم اللحظة بتوقيت UTC والمنطقة الزمنية المستهدفة، وتُنتج واجهة برمجة التطبيقات الوقت المحلي الصحيح.

فهم أسماء المناطق الزمنية IANA

يقبل خيار timeZone معرّفات المناطق الزمنية من قاعدة بيانات المناطق الزمنية IANA. تستخدم هذه المعرّفات التنسيق Region/City، مثل America/New_York أو Europe/London أو Asia/Tokyo.

const date = new Date('2025-03-15T20:00:00Z');

const zones = [
  'America/New_York',
  'Europe/London',
  'Asia/Tokyo',
  'Australia/Sydney'
];

zones.forEach(zone => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: zone,
    dateStyle: 'short',
    timeStyle: 'long'
  });

  console.log(`${zone}: ${formatter.format(date)}`);
});

// Output:
// America/New_York: 3/15/25, 4:00:00 PM EDT
// Europe/London: 3/15/25, 8:00:00 PM GMT
// Asia/Tokyo: 3/16/25, 5:00:00 AM JST
// Australia/Sydney: 3/16/25, 7:00:00 AM AEDT

تُظهر كل منطقة زمنية وقتًا محليًا مختلفًا لنفس اللحظة. تُظهر نيويورك الساعة 4:00 مساءً في 15 مارس. تُظهر لندن الساعة 8:00 مساءً في 15 مارس. تقدّمت طوكيو وسيدني بالفعل إلى 16 مارس، وتُظهران الساعة 5:00 صباحًا و7:00 صباحًا على التوالي.

تتعامل أسماء IANA مع التوقيت الصيفي تلقائيًا. يعرف المُنسِّق أن America/New_York يتبدّل بين التوقيت الشرقي القياسي والتوقيت الشرقي الصيفي ويُطبّق الإزاحة الصحيحة لأي تاريخ.

العثور على أسماء المناطق الزمنية IANA الصالحة

تحتوي قاعدة بيانات IANA على عدة مئات من معرّفات المناطق الزمنية. تتضمن الأنماط الشائعة:

  • America/New_York لمدينة نيويورك
  • America/Los_Angeles للوس أنجلوس
  • America/Chicago لشيكاغو
  • Europe/London للندن
  • Europe/Paris لباريس
  • Europe/Berlin لبرلين
  • Asia/Tokyo لطوكيو
  • Asia/Shanghai لشنغهاي
  • Asia/Kolkata للهند
  • Australia/Sydney لسيدني
  • Pacific/Auckland لأوكلاند

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

يمكنك البحث عن القائمة الكاملة للمعرفات في توثيق قاعدة بيانات المناطق الزمنية IANA.

استخدام UTC كمنطقة زمنية

المعرف الخاص UTC يقوم بتنسيق التواريخ بالتوقيت العالمي المنسق، والذي ليس له إزاحة عن خط الطول الرئيسي.

const date = new Date('2025-03-15T20:00:00Z');

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'UTC',
  dateStyle: 'short',
  timeStyle: 'long'
});

console.log(formatter.format(date));
// Output: "3/15/25, 8:00:00 PM UTC"

توقيت UTC يطابق الطابع الزمني الداخلي المخزن في كائن Date. استخدام UTC للتنسيق مفيد عند عرض الأوقات التي لا ينبغي أن تتغير بناءً على موقع المستخدم، مثل سجلات الخادم أو الطوابع الزمنية لقاعدة البيانات.

الحصول على المنطقة الزمنية للمستخدم

تُرجع الطريقة resolvedOptions() الخيارات الفعلية المستخدمة بواسطة المنسق، بما في ذلك المنطقة الزمنية. عند إنشاء منسق دون تحديد timeZone، فإنه يستخدم افتراضياً المنطقة الزمنية لنظام المستخدم.

const formatter = new Intl.DateTimeFormat();
const options = formatter.resolvedOptions();

console.log(options.timeZone);
// Output: "America/New_York" (or user's actual time zone)

يمنحك هذا معرف IANA للمنطقة الزمنية الحالية للمستخدم. يمكنك استخدام هذا المعرف لتنسيق تواريخ أخرى في نفس المنطقة أو لتخزين تفضيل المنطقة الزمنية للمستخدم.

const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: userTimeZone,
  dateStyle: 'full',
  timeStyle: 'long'
});

const date = new Date('2025-03-15T20:00:00Z');
console.log(formatter.format(date));
// Output varies based on user's time zone

يضمن هذا النمط عرض التواريخ بالتوقيت المحلي للمستخدم تلقائياً.

تنسيق نفس اللحظة لمناطق زمنية متعددة

يمكنك تنسيق نفس كائن Date لمناطق زمنية متعددة لإظهار للمستخدمين الوقت الذي يحدث فيه حدث ما في مواقع مختلفة.

const meetingTime = new Date('2025-03-15T20:00:00Z');

const zones = [
  { name: 'New York', zone: 'America/New_York' },
  { name: 'London', zone: 'Europe/London' },
  { name: 'Tokyo', zone: 'Asia/Tokyo' }
];

zones.forEach(({ name, zone }) => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    timeZone: zone,
    dateStyle: 'long',
    timeStyle: 'short'
  });

  console.log(`${name}: ${formatter.format(meetingTime)}`);
});

// Output:
// New York: March 15, 2025 at 4:00 PM
// London: March 15, 2025 at 8:00 PM
// Tokyo: March 16, 2025 at 5:00 AM

يساعد هذا المستخدمين على فهم متى يحدث اجتماع أو حدث في منطقتهم الزمنية وفي المناطق الزمنية للمشاركين الآخرين.

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

عند تنسيق التواريخ بدون أوقات، يمكن أن تؤثر المنطقة الزمنية على التاريخ التقويمي الذي يظهر. يقع تاريخ عند منتصف الليل بتوقيت UTC في تواريخ تقويمية مختلفة في مناطق زمنية مختلفة.

const date = new Date('2025-03-16T01:00:00Z');

const formatter1 = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/Los_Angeles',
  dateStyle: 'long'
});

const formatter2 = new Intl.DateTimeFormat('en-US', {
  timeZone: 'Asia/Tokyo',
  dateStyle: 'long'
});

console.log(`Los Angeles: ${formatter1.format(date)}`);
console.log(`Tokyo: ${formatter2.format(date)}`);

// Output:
// Los Angeles: March 15, 2025
// Tokyo: March 16, 2025

اللحظة 1:00 صباحًا بالتوقيت العالمي المنسق في 16 مارس تقابل 5:00 مساءً في 15 مارس في لوس أنجلوس و10:00 صباحًا في 16 مارس في طوكيو. يختلف تاريخ التقويم بيوم واحد بين المنطقتين الزمنيتين.

هذا مهم عند عرض التواريخ للأحداث المجدولة أو المواعيد النهائية أو أي تاريخ يفسره المستخدمون بالنسبة لتقويمهم المحلي.

استخدام إزاحات المناطق الزمنية

بدلاً من معرفات IANA، يمكنك استخدام سلاسل الإزاحة مثل +01:00 أو -05:00. تمثل هذه إزاحات ثابتة من التوقيت العالمي المنسق.

const date = new Date('2025-03-15T20:00:00Z');

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: '+09:00',
  dateStyle: 'short',
  timeStyle: 'long'
});

console.log(formatter.format(date));
// Output: "3/16/25, 5:00:00 AM GMT+9"

تعمل سلاسل الإزاحة عندما تعرف الإزاحة الدقيقة ولكن ليس الموقع المحدد. ومع ذلك، فهي لا تتعامل مع التوقيت الصيفي. إذا استخدمت -05:00 لتمثيل نيويورك، فستكون الأوقات خاطئة أثناء التوقيت الصيفي عندما تستخدم نيويورك فعليًا -04:00.

يُفضل استخدام معرفات IANA لأنها تتعامل مع التوقيت الصيفي تلقائيًا.

فهم كيفية عمل التوقيت الصيفي

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

عند استخدام معرفات المناطق الزمنية IANA، تطبق واجهة برمجة التطبيقات Intl.DateTimeFormat تلقائيًا الإزاحة الصحيحة لأي تاريخ.

const winterDate = new Date('2025-01-15T20:00:00Z');
const summerDate = new Date('2025-07-15T20:00:00Z');

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  dateStyle: 'long',
  timeStyle: 'long'
});

console.log(`Winter: ${formatter.format(winterDate)}`);
console.log(`Summer: ${formatter.format(summerDate)}`);

// Output:
// Winter: January 15, 2025 at 3:00:00 PM EST
// Summer: July 15, 2025 at 4:00:00 PM EDT

في يناير، تستخدم نيويورك التوقيت الشرقي القياسي بإزاحة UTC-5، مما يعرض 3:00 مساءً. في يوليو، تستخدم نيويورك التوقيت الشرقي الصيفي بإزاحة UTC-4، مما يعرض 4:00 مساءً. ينتج نفس التوقيت العالمي المنسق أوقاتًا محلية مختلفة بناءً على ما إذا كان التوقيت الصيفي نشطًا.

لا تحتاج إلى تتبع التواريخ التي تستخدم أي إزاحة. تتعامل واجهة برمجة التطبيقات مع ذلك تلقائيًا.

تنسيق الأوقات لجدولة الأحداث

عند عرض أوقات الأحداث، قم بتنسيق الوقت في موقع الحدث واختياريًا في موقع المستخدم.

const eventTime = new Date('2025-03-15T18:00:00Z');
const eventTimeZone = 'Europe/Paris';
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;

const eventFormatter = new Intl.DateTimeFormat('en-US', {
  timeZone: eventTimeZone,
  dateStyle: 'full',
  timeStyle: 'short'
});

const userFormatter = new Intl.DateTimeFormat('en-US', {
  timeZone: userTimeZone,
  dateStyle: 'full',
  timeStyle: 'short'
});

console.log(`Event time: ${eventFormatter.format(eventTime)} (Paris)`);
console.log(`Your time: ${userFormatter.format(eventTime)}`);

// Output (for a user in New York):
// Event time: Saturday, March 15, 2025 at 7:00 PM (Paris)
// Your time: Saturday, March 15, 2025 at 2:00 PM

يُظهر هذا النمط للمستخدمين كلاً من وقت حدوث الحدث في منطقته الزمنية الخاصة ومتى يجب عليهم الانضمام بناءً على موقعهم.

تنسيق الطوابع الزمنية للخادم في المنطقة الزمنية للمستخدم

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

const serverTimestamp = new Date('2025-03-15T20:00:00Z');
const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;

const formatter = new Intl.DateTimeFormat(navigator.language, {
  timeZone: userTimeZone,
  dateStyle: 'short',
  timeStyle: 'medium'
});

console.log(`Activity: ${formatter.format(serverTimestamp)}`);
// Output varies based on user's time zone and locale
// For en-US in New York: "Activity: 3/15/25, 4:00:00 PM"

يضمن ذلك رؤية المستخدمين للطوابع الزمنية بالوقت المحلي المألوف بدلاً من توقيت UTC أو توقيت الخادم.

دمج timeZone مع خيارات أخرى

يعمل خيار timeZone مع جميع خيارات Intl.DateTimeFormat الأخرى. يمكنك تحديد مكونات التاريخ والوقت الفردية، أو استخدام إعدادات النمط المسبقة، أو التحكم في نظام التقويم.

const date = new Date('2025-03-15T20:00:00Z');

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'Asia/Tokyo',
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric',
  second: 'numeric',
  timeZoneName: 'long'
});

console.log(formatter.format(date));
// Output: "Monday, March 16, 2025 at 5:00:00 AM Japan Standard Time"

يتحكم خيار timeZoneName في كيفية ظهور اسم المنطقة الزمنية في الناتج. ستغطي الدروس اللاحقة هذا الخيار بالتفصيل.

ما يجب تجنبه

لا تستخدم اختصارات المناطق الزمنية مثل EST أو PST أو GMT كقيم لخيار timeZone. هذه الاختصارات غامضة وغير مدعومة بشكل متسق.

// Incorrect - abbreviations may not work
const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'EST',  // This may throw an error
  dateStyle: 'short',
  timeStyle: 'short'
});

استخدم دائمًا معرفات IANA مثل America/New_York أو سلاسل الإزاحة مثل -05:00.

لا تفترض أن المنطقة الزمنية للمستخدم تطابق المنطقة الزمنية للخادم. قم دائمًا بتنسيق الأوقات بشكل صريح في المنطقة الصحيحة أو استخدم المنطقة الزمنية المكتشفة للمستخدم.

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

عند تنسيق التواريخ لمناطق زمنية متعددة، قد تقوم بإنشاء العديد من أدوات التنسيق. إذا كنت تقوم بتنسيق العديد من التواريخ بنفس الإعدادات، فأعد استخدام نفس مثيلات أدوات التنسيق للحصول على أداء أفضل.

const dates = [
  new Date('2025-03-15T20:00:00Z'),
  new Date('2025-03-16T14:00:00Z'),
  new Date('2025-03-17T09:00:00Z')
];

const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'Europe/Berlin',
  dateStyle: 'short',
  timeStyle: 'short'
});

dates.forEach(date => {
  console.log(formatter.format(date));
});

// Output:
// "3/15/25, 9:00 PM"
// "3/16/25, 3:00 PM"
// "3/17/25, 10:00 AM"

يتضمن إنشاء أداة تنسيق معالجة الخيارات وتحميل بيانات اللغة. إعادة استخدام نفس أداة التنسيق لتواريخ متعددة يتجنب هذا العبء الإضافي.