كيفية الحصول على أجزاء فردية من تاريخ منسق في JavaScript

استخدم formatToParts()‎ للوصول إلى كل مكون من مكونات التاريخ المنسق حسب اللغة بشكل منفصل

مقدمة

تُرجع الدالة format() سلسلة نصية منسقة كاملة مثل "January 15, 2025" أو "15/01/2025". يعمل هذا بشكل جيد للعرض البسيط، لكن لا يمكنك تنسيق الأجزاء الفردية بشكل مختلف. لا يمكنك جعل اسم الشهر غامقاً، أو تلوين السنة بشكل مختلف، أو تطبيق ترميز مخصص على مكونات محددة.

توفر JavaScript الدالة formatToParts() لحل هذه المشكلة. بدلاً من إرجاع سلسلة نصية واحدة، تُرجع مصفوفة من الكائنات، يمثل كل منها جزءاً واحداً من التاريخ المنسق. كل جزء له نوع مثل month، أو day، أو year، وقيمة مثل January، أو 15، أو 2025. يمكنك بعد ذلك معالجة هذه الأجزاء لتطبيق تنسيق مخصص، أو بناء تخطيطات معقدة، أو دمج التواريخ المنسقة في واجهات مستخدم غنية.

لماذا يصعب تخصيص السلاسل النصية المنسقة

عندما تتلقى سلسلة نصية منسقة مثل "January 15, 2025"، لا يمكنك تحديد مكان انتهاء الشهر وبداية اليوم بسهولة. تضع اللغات المختلفة المكونات بترتيبات مختلفة. تستخدم بعض اللغات فواصل مختلفة. يتطلب تحليل هذه السلاسل النصية بشكل موثوق منطقاً معقداً يكرر قواعد التنسيق المطبقة بالفعل في Intl API.

لنأخذ مثالاً على تطبيق تقويم يعرض التواريخ مع اسم الشهر بخط غامق. مع format()، ستحتاج إلى:

  1. اكتشاف الأحرف التي تمثل اسم الشهر
  2. مراعاة المسافات وعلامات الترقيم بين المكونات
  3. التعامل مع تنسيقات الأشهر المختلفة عبر اللغات
  4. تحليل السلسلة النصية بعناية لتجنب كسر التاريخ

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

تقضي طريقة formatToParts() على هذه المشكلة من خلال توفير المكونات بشكل منفصل. تتلقى بيانات منظمة تخبرك بالضبط أي جزء هو أي، بغض النظر عن اللغة.

استخدام formatToParts للحصول على مكونات التاريخ

تعمل طريقة formatToParts() بشكل مطابق لـ format() باستثناء قيمة الإرجاع الخاصة بها. تقوم بإنشاء منسق بنفس الخيارات، ثم تستدعي formatToParts() بدلاً من format().

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);

يخرج هذا مصفوفة من الكائنات:

[
  { type: "month", value: "January" },
  { type: "literal", value: " " },
  { type: "day", value: "15" },
  { type: "literal", value: ", " },
  { type: "year", value: "2025" }
]

يحتوي كل كائن على خاصية type تحدد ما يمثله الجزء وخاصية value تحتوي على السلسلة النصية الفعلية. تظهر الأجزاء بنفس الترتيب الذي ستظهر به في المخرجات المنسقة.

يمكنك التحقق من ذلك عن طريق ضم جميع القيم معًا:

const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Output: "January 15, 2025"

تنتج الأجزاء المتسلسلة نفس المخرجات تمامًا كما لو استدعيت format().

فهم أنواع الأجزاء

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

لتنسيق التاريخ الأساسي:

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

يمثل نوع month اسم الشهر أو رقمه. يمثل نوع day يوم الشهر. يمثل نوع year السنة. يمثل نوع literal المسافات أو علامات الترقيم أو النص الآخر الذي يدرجه المنسق.

للتواريخ مع أيام الأسبوع:

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "weekday", value: "Wednesday" },
//   { type: "literal", value: ", " },
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

يمثل نوع weekday يوم الأسبوع.

للتواريخ مع الأوقات:

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

const date = new Date(2025, 0, 15, 14, 30, 45);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" },
//   { type: "literal", value: " at " },
//   { type: "hour", value: "2" },
//   { type: "literal", value: ":" },
//   { type: "minute", value: "30" },
//   { type: "literal", value: ":" },
//   { type: "second", value: "45" },
//   { type: "literal", value: " " },
//   { type: "dayPeriod", value: "PM" }
// ]

تمثل أنواع hour وminute وsecond مكونات الوقت. يمثل نوع dayPeriod صباحاً أو مساءً في التنسيق ذي الـ 12 ساعة.

تطبيق تنسيق مخصص على أجزاء التاريخ

حالة الاستخدام الأساسية لـ formatToParts() هي تطبيق أنماط مختلفة على مكونات مختلفة. يمكنك معالجة مصفوفة الأجزاء لتغليف أنواع محددة في عناصر HTML.

جعل اسم الشهر بخط عريض:

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
  .map(part => {
    if (part.type === "month") {
      return `<strong>${part.value}</strong>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "<strong>January</strong> 15, 2025"

يعمل هذا النهج مع أي لغة ترميز. يمكنك إنشاء HTML أو JSX أو أي تنسيق آخر من خلال معالجة مصفوفة الأجزاء.

تنسيق السنة بشكل مختلف:

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
  .map(part => {
    if (part.type === "year") {
      return `<span class="text-gray-500">${part.value}</span>`;
    }
    return part.value;
  })
  .join("");

console.log(html);
// Output: "January 15, <span class="text-gray-500">2025</span>"

هذا النمط شائع في عروض التقويم حيث تحتاج المكونات المختلفة إلى تركيز بصري مختلف.

بناء عروض تاريخ مخصصة بأنماط متعددة

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

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);

const html = parts
  .map(part => {
    switch (part.type) {
      case "weekday":
        return `<span class="weekday">${part.value}</span>`;
      case "month":
        return `<span class="month">${part.value}</span>`;
      case "day":
        return `<span class="day">${part.value}</span>`;
      case "year":
        return `<span class="year">${part.value}</span>`;
      case "literal":
        return `<span class="literal">${part.value}</span>`;
      default:
        return part.value;
    }
  })
  .join("");

console.log(html);
// Output: "<span class="weekday">Wednesday</span><span class="literal">, </span><span class="month">January</span><span class="literal"> </span><span class="day">15</span><span class="literal">, </span><span class="year">2025</span>"

يتيح هذا التحكم الدقيق تنسيقاً محدداً لكل مكون. يمكنك بعد ذلك استخدام CSS لتنسيق كل فئة بشكل مختلف.

إنشاء تخطيطات تاريخ مخصصة

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

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

const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);

const day = parts.find(p => p.type === "day").value;
const month = parts.find(p => p.type === "month").value;
const year = parts.find(p => p.type === "year").value;

const customLayout = `
  <div class="date-card">
    <div class="day-large">${day}</div>
    <div class="month-small">${month}</div>
    <div class="year-small">${year}</div>
  </div>
`;

console.log(customLayout);

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

جميع أنواع الأجزاء المتاحة

يمكن أن تحتوي خاصية type على هذه القيم اعتماداً على خيارات التنسيق المستخدمة:

  • weekday: يوم الأسبوع مثل الاثنين أو Mon
  • era: مؤشر الحقبة مثل BC أو AD أو BCE
  • year: السنة مثل 2025
  • month: اسم الشهر أو رقمه مثل يناير أو 01
  • day: يوم الشهر مثل 15
  • dayPeriod: صباحاً أو مساءً أو فترات يوم أخرى خاصة باللغة
  • hour: الساعة مثل 14 أو 2
  • minute: الدقيقة مثل 30
  • second: الثانية مثل 45
  • fractionalSecond: الميلي ثانية أو أجزاء أخرى من الثواني
  • timeZoneName: اسم المنطقة الزمنية مثل PST أو Pacific Standard Time
  • literal: المسافات أو علامات الترقيم أو نص آخر مضاف بواسطة التنسيق
  • relatedYear: السنة الميلادية في أنظمة التقويم البديلة
  • yearName: السنة المسماة في بعض أنظمة التقويم
  • unknown: رموز غير معروفة

لا تنتج كل خيارات التنسيق كل أنواع الأجزاء. تعتمد الأجزاء التي تتلقاها على قيمة التاريخ وإعدادات المنسق.

التواريخ مع مؤشرات الحقبة:

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

const date = new Date(-100, 0, 1);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
//   { type: "year", value: "101" },
//   { type: "literal", value: " " },
//   { type: "era", value: "BC" }
// ]

التواريخ مع المناطق الزمنية:

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

const date = new Date(2025, 0, 15, 14, 30);
const parts = formatter.formatToParts(date);
console.log(parts);
// Parts will include { type: "timeZoneName", value: "PST" } or similar

التواريخ مع الثواني الكسرية:

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

const date = new Date(2025, 0, 15, 14, 30, 45, 123);
const parts = formatter.formatToParts(date);
// Parts will include { type: "fractionalSecond", value: "123" }

تمييز مكونات التاريخ بشكل مشروط

تقوم بعض التطبيقات بتمييز مكونات تاريخ محددة بناءً على منطق الأعمال. باستخدام formatToParts()، يمكنك تطبيق التنسيق بناءً على قيمة التاريخ مع الحفاظ على التنسيق الصحيح.

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

function formatDateWithHighlight(date) {
  const parts = formatter.formatToParts(date);
  const isWeekend = date.getDay() === 0 || date.getDay() === 6;

  const html = parts
    .map(part => {
      if (part.type === "day" && isWeekend) {
        return `<span class="text-blue-600 font-bold">${part.value}</span>`;
      }
      return part.value;
    })
    .join("");

  return html;
}

const saturday = new Date(2025, 0, 18);
console.log(formatDateWithHighlight(saturday));
// Output: "January <span class="text-blue-600 font-bold">18</span>, 2025"

const monday = new Date(2025, 0, 13);
console.log(formatDateWithHighlight(monday));
// Output: "January 13, 2025"

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

إنشاء عروض تاريخ يسهل الوصول إليها

يمكنك استخدام formatToParts() لإضافة سمات إمكانية الوصول إلى التواريخ المنسقة. يساعد هذا قارئات الشاشة على الإعلان عن القيم بشكل صحيح.

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

function formatAccessibleDate(date) {
  const parts = formatter.formatToParts(date);
  const formatted = parts.map(part => part.value).join("");
  const isoDate = date.toISOString().split('T')[0];

  return `<time datetime="${isoDate}">${formatted}</time>`;
}

const date = new Date(2025, 0, 15);
console.log(formatAccessibleDate(date));
// Output: "<time datetime="2025-01-15">January 15, 2025</time>"

يضمن هذا أن قارئات الشاشة يمكنها الإعلان عن التاريخ بشكل صحيح بينما يعرض العرض الإصدار المنسق حسب اللغة المحلية.

كيف تحافظ الأجزاء على التنسيق الخاص باللغة المحلية

يحافظ مصفوفة الأجزاء على قواعد التنسيق الخاصة باللغة المحلية تلقائيًا. تضع اللغات المحلية المختلفة المكونات بترتيبات مختلفة وتستخدم تنسيقات مختلفة، لكن formatToParts() يتعامل مع هذه الاختلافات.

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

const date = new Date(2025, 0, 15);
console.log(usFormatter.formatToParts(date));
// [
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "day", value: "15" },
//   { type: "literal", value: ", " },
//   { type: "year", value: "2025" }
// ]

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

console.log(ukFormatter.formatToParts(date));
// [
//   { type: "day", value: "15" },
//   { type: "literal", value: " " },
//   { type: "month", value: "January" },
//   { type: "literal", value: " " },
//   { type: "year", value: "2025" }
// ]

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

const jpFormatter = new Intl.DateTimeFormat("ja-JP", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(jpFormatter.formatToParts(date));
// [
//   { type: "year", value: "2025" },
//   { type: "literal", value: "年" },
//   { type: "month", value: "1月" },
//   { type: "day", value: "15" },
//   { type: "literal", value: "日" }
// ]

يستخدم التنسيق الياباني ترتيبًا مختلفًا ويتضمن أحرفًا حرفية مثل 年 (سنة) و 日 (يوم). تعكس مصفوفة الأجزاء هذه الاتفاقيات الخاصة باللغة المحلية تلقائيًا.

دمج formatToParts مع مكونات إطار العمل

يمكن لأطر العمل الحديثة مثل React استخدام formatToParts() لبناء المكونات بكفاءة.

function DateDisplay({ date, locale, options }) {
  const formatter = new Intl.DateTimeFormat(locale, options);
  const parts = formatter.formatToParts(date);

  return (
    <span className="date-display">
      {parts.map((part, index) => {
        if (part.type === "month") {
          return <strong key={index}>{part.value}</strong>;
        }
        if (part.type === "year") {
          return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
        }
        return <span key={index}>{part.value}</span>;
      })}
    </span>
  );
}

يطبق هذا المكون تنسيقًا مختلفًا على أجزاء مختلفة مع الحفاظ على التنسيق الصحيح لأي لغة محلية.

متى تستخدم formatToParts مقابل format

استخدم format() عندما تحتاج إلى سلسلة نصية منسقة بسيطة دون أي تخصيص. هذه هي الحالة الشائعة لمعظم عمليات عرض التاريخ.

استخدم formatToParts() عندما تحتاج إلى:

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

تتطلب طريقة formatToParts() موارد أكثر قليلاً من format() لأنها تنشئ مصفوفة من الكائنات بدلاً من سلسلة نصية واحدة. هذا الفرق ضئيل للتطبيقات النموذجية، ولكن إذا كنت تنسق آلاف التواريخ في الثانية، فإن format() يقدم أداءً أفضل.

بالنسبة لمعظم التطبيقات، اختر بناءً على احتياجات التنسيق الخاصة بك بدلاً من مخاوف الأداء. إذا كنت لا تحتاج إلى تخصيص المخرجات، استخدم format(). إذا كنت بحاجة إلى تنسيق أو ترميز مخصص، استخدم formatToParts().