واجهة برمجة التطبيقات Intl.DateTimeFormat

تنسيق التواريخ والأوقات لأي لغة محلية بدون مكتبات خارجية

مقدمة

تظهر التواريخ بشكل مختلف حول العالم. 15 يناير 2024 يظهر كـ 1/15/2024 في الولايات المتحدة، و15/1/2024 في المملكة المتحدة، و2024/1/15 في اليابان. تختلف تنسيقات الوقت أيضًا. يستخدم الأمريكيون ساعات 12 ساعة مع صباحًا ومساءً، بينما يستخدم معظم العالم نظام 24 ساعة.

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

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

الاستخدام الأساسي

أبسط طريقة لتنسيق تاريخ هي إنشاء نسخة من Intl.DateTimeFormat واستدعاء طريقة format() الخاصة بها.

const date = new Date(2024, 0, 15, 14, 30);

const formatter = new Intl.DateTimeFormat("en-US");
formatter.format(date);
// "1/15/2024"

غيّر اللغة المحلية لرؤية اتفاقيات التنسيق المختلفة.

const ukFormatter = new Intl.DateTimeFormat("en-GB");
ukFormatter.format(date);
// "15/01/2024"

const japanFormatter = new Intl.DateTimeFormat("ja-JP");
japanFormatter.format(date);
// "2024/1/15"

نفس التاريخ، منسق بثلاث طرق مختلفة بناءً على الاتفاقيات المحلية.

فهم اللغات المحلية

اللغة المحلية هي سلسلة تحدد اللغة والتفضيلات الإقليمية. يتبع التنسيق معيار BCP 47: رمز اللغة، متبوعًا اختياريًا برمز المنطقة.

أنماط اللغات المحلية الشائعة:

"en"      // الإنجليزية (عامة)
"en-US"   // الإنجليزية (الولايات المتحدة)
"en-GB"   // الإنجليزية (المملكة المتحدة)
"es"      // الإسبانية (عامة)
"es-MX"   // الإسبانية (المكسيك)
"es-ES"   // الإسبانية (إسبانيا)
"zh-CN"   // الصينية (الصين، المبسطة)
"zh-TW"   // الصينية (تايوان، التقليدية)

إذا أغفلت معلمة اللغة المحلية، يستخدم المتصفح اللغة المحلية الافتراضية للمستخدم.

const formatter = new Intl.DateTimeFormat();
formatter.format(date);
// يختلف الإخراج بناءً على اللغة المحلية للمتصفح الخاص بالمستخدم

يمكنك أيضًا توفير مصفوفة من اللغات المحلية. يستخدم المتصفح أول لغة محلية مدعومة.

const formatter = new Intl.DateTimeFormat(["es-MX", "es", "en"]);
// يستخدم الإسبانية (المكسيك) إذا كانت متاحة، ويعود إلى الإسبانية العامة، ثم الإنجليزية

نظرة عامة على خيارات التنسيق

يقبل مُنشئ Intl.DateTimeFormat كائن خيارات يتحكم فيما يظهر في المخرجات المنسقة. هناك نهجان للتنسيق.

النهج الأول يستخدم اختصارات الأنماط. توفر هذه تنسيقًا سريعًا وتقليديًا.

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "full",
  timeStyle: "short"
});

formatter.format(date);
// "Monday, January 15, 2024 at 2:30 PM"

النهج الثاني يستخدم خيارات المكونات. هذه تعطي تحكمًا دقيقًا في كل جزء من التاريخ.

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

formatter.format(date);
// "January 15, 2024 at 2:30 PM"

لا يمكنك مزج اختصارات الأنماط مع خيارات المكونات. اختر نهجًا واحدًا لكل منسق.

اختصارات الأنماط

توفر خيارات dateStyle وtimeStyle أربعة مستويات تنسيق مسبقة الإعداد.

خيار dateStyle ينسق جزء التاريخ.

const date = new Date(2024, 0, 15);

const full = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });
full.format(date);
// "Monday, January 15, 2024"

const long = new Intl.DateTimeFormat("en-US", { dateStyle: "long" });
long.format(date);
// "January 15, 2024"

const medium = new Intl.DateTimeFormat("en-US", { dateStyle: "medium" });
medium.format(date);
// "Jan 15, 2024"

const short = new Intl.DateTimeFormat("en-US", { dateStyle: "short" });
short.format(date);
// "1/15/24"

خيار timeStyle ينسق جزء الوقت.

const date = new Date(2024, 0, 15, 14, 30, 45);

const full = new Intl.DateTimeFormat("en-US", { timeStyle: "full" });
full.format(date);
// "2:30:45 PM Eastern Standard Time"

const long = new Intl.DateTimeFormat("en-US", { timeStyle: "long" });
long.format(date);
// "2:30:45 PM EST"

const medium = new Intl.DateTimeFormat("en-US", { timeStyle: "medium" });
medium.format(date);
// "2:30:45 PM"

const short = new Intl.DateTimeFormat("en-US", { timeStyle: "short" });
short.format(date);
// "2:30 PM"

يمكنك دمج كلا الخيارين لتنسيق التاريخ والوقت معًا.

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "medium",
  timeStyle: "short"
});

formatter.format(date);
// "Jan 15, 2024, 2:30 PM"

تتكيف اختصارات الأنماط مع اتفاقيات اللغة المحلية تلقائيًا.

const usFormatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "short",
  timeStyle: "short"
});
usFormatter.format(date);
// "1/15/24, 2:30 PM"

const deFormatter = new Intl.DateTimeFormat("de-DE", {
  dateStyle: "short",
  timeStyle: "short"
});
deFormatter.format(date);
// "15.1.24, 14:30"

التنسيق الألماني يستخدم النقاط كفواصل، ويعكس ترتيب اليوم والشهر، ويعرض الوقت بنظام 24 ساعة. التنسيق الأمريكي يستخدم الشرطات المائلة، وترتيب الشهر أولاً، ونظام 12 ساعة مع AM/PM. نفس الخيارات، مخرجات مختلفة بناءً على اللغة المحلية.

خيارات المكون

توفر خيارات المكون تحكمًا دقيقًا فيما يتم عرضه وكيفية عرضه. يحدد كل خيار جزءًا من التاريخ أو الوقت.

يعرض خيار year السنة.

const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric" });
formatter.format(date);
// "2024"

const twoDigit = new Intl.DateTimeFormat("en-US", { year: "2-digit" });
twoDigit.format(date);
// "24"

يعرض خيار month الشهر بتنسيقات مختلفة.

const numeric = new Intl.DateTimeFormat("en-US", { month: "numeric" });
numeric.format(date);
// "1"

const twoDigit = new Intl.DateTimeFormat("en-US", { month: "2-digit" });
twoDigit.format(date);
// "01"

const long = new Intl.DateTimeFormat("en-US", { month: "long" });
long.format(date);
// "January"

const short = new Intl.DateTimeFormat("en-US", { month: "short" });
short.format(date);
// "Jan"

const narrow = new Intl.DateTimeFormat("en-US", { month: "narrow" });
narrow.format(date);
// "J"

يعرض خيار day يوم الشهر.

const formatter = new Intl.DateTimeFormat("en-US", { day: "numeric" });
formatter.format(date);
// "15"

const twoDigit = new Intl.DateTimeFormat("en-US", { day: "2-digit" });
twoDigit.format(date);
// "15"

يعرض خيار weekday يوم الأسبوع.

const long = new Intl.DateTimeFormat("en-US", { weekday: "long" });
long.format(date);
// "Monday"

const short = new Intl.DateTimeFormat("en-US", { weekday: "short" });
short.format(date);
// "Mon"

const narrow = new Intl.DateTimeFormat("en-US", { weekday: "narrow" });
narrow.format(date);
// "M"

يمكنك دمج خيارات مكونات متعددة لإنشاء تنسيقات تاريخ مخصصة.

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

formatter.format(date);
// "Monday, January 15, 2024"

يتعامل المتصفح مع المسافات وعلامات الترقيم بناءً على اتفاقيات اللغة المحلية.

تنسيق الوقت

تتحكم خيارات مكون الوقت في عرض الساعة والدقيقة والثانية.

const date = new Date(2024, 0, 15, 14, 30, 45);

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric"
});

formatter.format(date);
// "2:30:45 PM"

يتحكم خيار hour12 في عرض الوقت بنظام 12 ساعة مقابل نظام 24 ساعة.

const hour12 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  hour12: true
});
hour12.format(date);
// "2:30 PM"

const hour24 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  hour12: false
});
hour24.format(date);
// "14:30"

يوفر خيار hourCycle تحكمًا أدق في عرض الساعة. توجد أربعة خيارات.

// h11: 0-11
const h11 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h11"
});
h11.format(new Date(2024, 0, 15, 0, 30));
// "0:30 AM"

// h12: 1-12
const h12 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h12"
});
h12.format(new Date(2024, 0, 15, 0, 30));
// "12:30 AM"

// h23: 0-23
const h23 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h23"
});
h23.format(new Date(2024, 0, 15, 0, 30));
// "0:30"

// h24: 1-24
const h24 = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  hourCycle: "h24"
});
h24.format(new Date(2024, 0, 15, 0, 30));
// "24:30"

الفرق مهم عند منتصف الليل. تستخدم دورة h11 الرقم 0، بينما تستخدم h12 الرقم 12. وبالمثل، تستخدم h23 الرقم 0، بينما تستخدم h24 الرقم 24.

يضيف خيار dayPeriod علامات وصفية للفترات الزمنية في نظام 12 ساعة.

const morning = new Date(2024, 0, 15, 10, 30);
const afternoon = new Date(2024, 0, 15, 14, 30);
const night = new Date(2024, 0, 15, 22, 30);

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  dayPeriod: "long"
});

formatter.format(morning);
// "10:30 in the morning"

formatter.format(afternoon);
// "2:30 in the afternoon"

formatter.format(night);
// "10:30 at night"

يعمل خيار dayPeriod فقط مع تنسيقات الوقت بنظام 12 ساعة.

يعرض خيار fractionalSecondDigits دقة أجزاء الثانية.

const date = new Date(2024, 0, 15, 14, 30, 45, 123);

const oneDigit = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  fractionalSecondDigits: 1
});
oneDigit.format(date);
// "2:30:45.1 PM"

const threeDigits = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  second: "numeric",
  fractionalSecondDigits: 3
});
threeDigits.format(date);
// "2:30:45.123 PM"

يمكنك تحديد رقم واحد أو رقمين أو ثلاثة أرقام عشرية.

معالجة المناطق الزمنية

يقوم خيار timeZone بتحويل التواريخ إلى مناطق زمنية محددة.

const date = new Date("2024-01-15T14:30:00Z"); // توقيت UTC

const newYork = new Intl.DateTimeFormat("en-US", {
  timeZone: "America/New_York",
  dateStyle: "full",
  timeStyle: "long"
});
newYork.format(date);
// "Monday, January 15, 2024 at 9:30:00 AM EST"

const tokyo = new Intl.DateTimeFormat("ja-JP", {
  timeZone: "Asia/Tokyo",
  dateStyle: "full",
  timeStyle: "long"
});
tokyo.format(date);
// "2024年1月15日月曜日 23:30:00 日本標準時"

const london = new Intl.DateTimeFormat("en-GB", {
  timeZone: "Europe/London",
  dateStyle: "full",
  timeStyle: "long"
});
london.format(date);
// "Monday, 15 January 2024 at 14:30:00 GMT"

تظهر نفس اللحظة الزمنية بشكل مختلف استنادًا إلى المنطقة الزمنية. استخدم معرفات المناطق الزمنية IANA مثل America/New_York أو Europe/London أو Asia/Tokyo.

يعرض خيار timeZoneName اسم المنطقة الزمنية.

const formatter = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "short"
});
formatter.format(date);
// "9:30 AM EST"

const long = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "long"
});
long.format(date);
// "9:30 AM Eastern Standard Time"

const shortOffset = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "shortOffset"
});
shortOffset.format(date);
// "9:30 AM GMT-5"

const longOffset = new Intl.DateTimeFormat("en-US", {
  hour: "numeric",
  minute: "numeric",
  timeZone: "America/New_York",
  timeZoneName: "longOffset"
});
longOffset.format(date);
// "9:30 AM GMT-05:00"

توفر قيم timeZoneName المختلفة مستويات متفاوتة من التفاصيل.

أنظمة التقويم وأنظمة الترقيم

يدعم خيار calendar التقويمات غير الغريغورية.

const date = new Date(2024, 0, 15);

const gregorian = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});
gregorian.format(date);
// "January 15, 2024"

const japanese = new Intl.DateTimeFormat("ja-JP", {
  calendar: "japanese",
  year: "numeric",
  month: "long",
  day: "numeric"
});
japanese.format(date);
// "令和6年1月15日"

const islamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});
islamic.format(date);
// "٦ رجب ١٤٤٥"

const chinese = new Intl.DateTimeFormat("zh-CN", {
  calendar: "chinese",
  year: "numeric",
  month: "long",
  day: "numeric"
});
chinese.format(date);
// "2023甲辰年腊月初五"

يتم تحويل نفس التاريخ إلى أنظمة تقويم مختلفة. تشمل التقويمات المتاحة gregory وjapanese وislamic وislamic-umalqura وislamic-tbla وislamic-civil وislamic-rgsa وchinese وhebrew وindian وpersian وغيرها.

يعرض خيار numberingSystem الأرقام بنصوص مختلفة.

const date = new Date(2024, 0, 15);

const western = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "latn",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
western.format(date);
// "1/15/2024"

const arabic = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "arab",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
arabic.format(date);
// "١‏/١٥‏/٢٠٢٤"

const devanagari = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "deva",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
devanagari.format(date);
// "१/१५/२०२४"

const bengali = new Intl.DateTimeFormat("en-US", {
  numberingSystem: "beng",
  year: "numeric",
  month: "numeric",
  day: "numeric"
});
bengali.format(date);
// "১/১৫/২০২৪"

تشمل أنظمة الترقيم المتاحة latn (الأرقام الغربية) وarab (الأرقام العربية-الهندية) وarabext (الأرقام العربية-الهندية الموسعة) وbeng (الأرقام البنغالية) وdeva (أرقام ديفاناغاري) والعديد من الأنظمة الأخرى.

تنسيق نطاقات التواريخ

تقوم طريقة formatRange() بتنسيق نطاق تاريخي مع حذف ذكي للمعلومات المكررة.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

formatter.formatRange(start, end);
// "January 15 – 20, 2024"

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

عندما يمتد النطاق عبر شهور مختلفة، يتم عرض كليهما.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 1, 20);

formatter.formatRange(start, end);
// "January 15 – February 20, 2024"

عندما يمتد النطاق عبر سنوات مختلفة، يتم عرض جميع المعلومات.

const start = new Date(2024, 0, 15);
const end = new Date(2025, 1, 20);

formatter.formatRange(start, end);
// "January 15, 2024 – February 20, 2025"

تعمل طريقة formatRange() مع نطاقات الوقت أيضًا.

const start = new Date(2024, 0, 15, 14, 30);
const end = new Date(2024, 0, 15, 16, 45);

const formatter = new Intl.DateTimeFormat("en-US", {
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

formatter.formatRange(start, end);
// "January 15, 2:30 – 4:45 PM"

يظهر التاريخ مرة واحدة لأن كلا الوقتين يحدثان في نفس اليوم.

الوصول إلى الأجزاء المنسقة

تُرجع طريقة formatToParts() مصفوفة من الكائنات تمثل كل جزء من التاريخ المنسق. هذا يتيح منطق تنسيق مخصص.

const date = new Date(2024, 0, 15, 14, 30);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric",
  hour: "numeric",
  minute: "numeric"
});

const parts = formatter.formatToParts(date);

كل كائن في المصفوفة يحتوي على خاصية type و value.

[
  { type: "month", value: "January" },
  { type: "literal", value: " " },
  { type: "day", value: "15" },
  { type: "literal", value: ", " },
  { type: "year", value: "2024" },
  { type: "literal", value: " at " },
  { type: "hour", value: "2" },
  { type: "literal", value: ":" },
  { type: "minute", value: "30" },
  { type: "literal", value: " " },
  { type: "dayPeriod", value: "PM" }
]

يمكنك تصفية ومعالجة هذه الأجزاء لبناء تنسيقات مخصصة.

const dateParts = parts.filter(part =>
  ["month", "day", "year"].includes(part.type)
);

const dateString = dateParts.map(part => part.value).join("/");
// "January/15/2024"

توفر طريقة formatRangeToParts() نفس الوظائف لنطاقات التاريخ.

const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);

const formatter = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

const parts = formatter.formatRangeToParts(start, end);

يتضمن كل كائن جزء خاصية source تشير إلى ما إذا كان مصدره من تاريخ البداية أو النهاية.

[
  { type: "month", value: "January", source: "startRange" },
  { type: "literal", value: " ", source: "startRange" },
  { type: "day", value: "15", source: "startRange" },
  { type: "literal", value: " – ", source: "shared" },
  { type: "day", value: "20", source: "endRange" },
  { type: "literal", value: ", ", source: "shared" },
  { type: "year", value: "2024", source: "shared" }
]

أفضل الممارسات

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

// أقل كفاءة
dates.forEach(date => {
  const formatted = new Intl.DateTimeFormat("en-US").format(date);
  console.log(formatted);
});

// أكثر كفاءة
const formatter = new Intl.DateTimeFormat("en-US");
dates.forEach(date => {
  const formatted = formatter.format(date);
  console.log(formatted);
});

استخدم لغة متصفح المستخدم عندما يكون ذلك ممكنًا عن طريق حذف معلمة اللغة. هذا يحترم تفضيلات المستخدم.

const formatter = new Intl.DateTimeFormat();
// يستخدم لغة المتصفح تلقائيًا

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

const formatter = new Intl.DateTimeFormat(["fr-CA", "fr", "en"]);
// يفضل الفرنسية (كندا)، ويعود إلى الفرنسية، ثم الإنجليزية

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

const formatter = new Intl.DateTimeFormat("en-US", {
  dateStyle: "medium",
  timeStyle: "short"
});

استخدم خيارات المكونات عندما تحتاج إلى تحكم دقيق في تنسيق الإخراج.

const formatter = new Intl.DateTimeFormat("en-US", {
  weekday: "long",
  year: "numeric",
  month: "long",
  day: "numeric"
});

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

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

استخدم formatRange() لنطاقات التاريخ بدلاً من تنسيق كل تاريخ بشكل منفصل ودمجها. تتعامل الطريقة مع الحذف الذكي للمعلومات المكررة.

// أقل وضوحًا
const startFormatted = formatter.format(start);
const endFormatted = formatter.format(end);
const range = `${startFormatted} to ${endFormatted}`;

// أفضل
const range = formatter.formatRange(start, end);

تحقق من توافق المتصفح للميزات المتقدمة مثل dayPeriod وfractionalSecondDigits وقيم timeZoneName معينة. تدعم جميع المتصفحات الحديثة الوظائف الأساسية، ولكن قد تتطلب الخيارات الأحدث بدائل للمتصفحات القديمة.