תמיכה במערכות לוח שנה גלובליות באמצעות Temporal

תכונות באינטרנט: Temporal

האובייקט Date המסורתי ב-JavaScript מבוסס על לוח שנה גרגוריאני פרופלקטי, ולכן קשה ליצור אפליקציות למשתמשים שמסתמכים על מערכות אחרות של לוחות שנה, כמו לוח השנה המוסלמי (ירחי), העברי (ירחי-שמשי) או הסיני (ירחי-שמשי). בעבר, המפתחים היו צריכים להסתמך על ספריות מורכבות של צד שלישי או על חישובים ידניים כדי לתמוך במערכות האלה.

ה-API‏ Temporal מספק תמיכה מעולה בכמה מערכות של לוחות שנה. כשמשייכים מזהה לוח שנה לאובייקטים של תאריכים, Temporal מטפל באופן מובנה בפעולות החשבון המורכבות ובפורמטים שנדרשים בהקשרים תרבותיים שונים.

איך להטמיע

כדי לתמוך במערכות יומנים גלובליות באמצעות Temporal:

  1. שיוך לוח שנה (חובה): כשיוצרים או ממירים אובייקט Temporal (כמו Temporal.PlainDate), צריך לציין את מערכת לוח השנה הרצויה באמצעות withCalendar() כדי לבצע פעולות שקשורות ללוח השנה.
  2. שימוש במזהים קבועים (חובה בלוחות שנה ירחיים-שמשיים): צריך להשתמש ב-monthCode במקום באינדקס המספרי month כדי לזהות חודשים ספציפיים לאורך השנים בלוחות שנה ירחיים-שמשיים. האפשרות הזו מתאימה רק ליומנים שכוללים חודשים מעוברים, כמו לוח השנה העברי והסיני.
  3. הקפדה על קבועים ביומן (חובה): כשעוברים על חודשים או ימים, אסור להניח ערכים קבועים (כמו 12 חודשים בשנה או 31 ימים בחודש). כדי לוודא שהקוד פועל בכל היומנים, צריך להשתמש במאפיינים כמו monthsInYear ו-daysInMonth.
  4. שימוש בהשוואות שמותאמות ליומן (חובה): כשמשווים בין תאריכים במערכת יומן ספציפית, צריך להשתמש ב-Temporal.PlainDate.compare() במקום להשוות באופן ידני בין מאפייני השנה, החודש והיום. כך הסדר הכרונולוגי מטופל בצורה נכונה במסגרת כללי היומן.

קוד לדוגמה: המרה ואיטרציה של יומנים

// 1. Helper to check calendar support
function isCalendarSupported(calendarId) {
  try {
    return Intl.supportedValuesOf('calendar').includes(calendarId);
  } catch {
    // Fallback for environments where supportedValuesOf is not available
    return false;
  }
}

// 2. Get current date in default ISO 8601 calendar
const isoDate = Temporal.Now.plainDateISO();

// 3. Convert to Hebrew calendar if supported
const calendarId = 'hebrew';
const targetDate = isCalendarSupported(calendarId) 
  ? isoDate.withCalendar(calendarId)
  : isoDate; // Fallback to ISO if not supported

if (targetDate.calendar.id !== calendarId) {
  console.warn(`Calendar ${calendarId} not supported; falling back to ISO 8601`);
}

// 4. Log properties specific to the calendar
console.log(`Calendar: ${targetDate.calendar.id}`);
console.log(`Year: ${targetDate.year}`);
console.log(`Month Code: ${targetDate.monthCode}`); // Stable across leap years

// 5. Safely iterate through months in the current year
for (let m = 1; m <= targetDate.monthsInYear; m++) {
  console.log(`Month ${m} has ${targetDate.with({ month: m }).daysInMonth} days.`);
}

// 6. Compare dates within the same calendar
const today = Temporal.Now.plainDateISO().withCalendar(calendarId);
const comparison = Temporal.PlainDate.compare(targetDate, today);
const relative = comparison < 0 ? 'Past' : comparison > 0 ? 'Future' : 'Today';
console.log(`Timeline: ${relative}`);

// 7. Format for display using toLocaleString (including era for era-based calendars like 'japanese')
const localizedDisplay = targetDate.toLocaleString('en-u-ca-hebrew', {
  day: 'numeric',
  month: 'long',
  year: 'numeric',
  era: 'long'
});

הטמעה אסטרטגית ושיטות מומלצות

  • מומלץ להשתמש ב-monthsInYear כגבול העליון כשעוברים בלולאה על החודשים, במקום להניח שיש 12 חודשים.
  • מומלץ להשתמש ב-monthCode כדי לזהות חודשים ספציפיים ביומנים שכוללים חודשים מעוברים (למשל, עברי או סיני), בלי קשר לשנה.
  • אל תניחו ש-date.month === 12 הוא החודש האחרון בשנה. שימוש ב-date.month === date.monthsInYear.
  • אל תניחו ש-inLeapYear === true אומר שהשנה ארוכה יותר ביום אחד בלבד. בלוחות שנה שמבוססים על שנת הירח ושנת השמש, יכול להיות שיתווסף חודש מעובר שלם.
  • מומלץ להשתמש ב-Temporal.PlainDate.compare() כשמשווים בין שני תאריכים במערכת לוח שנה ספציפית, במקום להשוות בין מאפייני שנה/חודש/יום באופן ידני.
  • מומלץ להשתמש ב-toLocaleString() כדי לעצב תאריכים למשתמשים במקום שרשור מחרוזות ידני.
  • חשוב לוודא שמערכת היומן הרצויה נתמכת בסביבה באמצעות Intl.supportedValuesOf('calendar') לפני שיוצרים אובייקטים זמניים ספציפיים ליומן.
  • חשוב לזכור שחלק מהלוחות (כמו גרסאות שונות של לוח השנה המוסלמי) עשויים להתבסס על תצפיות ויזואליות ולא על חישובים קבועים. ‫Temporal API פועל לפי ההטמעה של Intl בסביבה, שבדרך כלל משתמשת בהערכות מחושבות. לחישובים קריטיים של תאריכים תרבותיים או דתיים, כדאי לאמת את התוצאות עם מומחים בתחום או להשתמש בספריות ייעודיות.
  • חשוב להביא בחשבון שמות של תקופות כשמשתמשים בלוחות שנה שכוללים תקופות (למשל, לוח השנה היפני או הבודהיסטי), באמצעות toLocaleString().

אסטרטגיית גיבוי

תמיכה ב-Temporal בדפדפנים ופתרונות חלופיים

בדפדפנים שעדיין לא תומכים ב-API המקורי של Temporal, צריך להשתמש בזיהוי תכונות ולטעון באופן דינמי את temporal-polyfill (‎~15 KB min+gz).

חשוב לזכור שייבוא של temporal-polyfill לא מקצה באופן אוטומטי את האובייקט Temporal להיקף הגלובלי. אם הקוד שלכם מסתמך על globalThis.Temporal, צריך להקצות אותו באופן מפורש.

// Check if Temporal is supported natively
(async () => {
  if (typeof Temporal === 'undefined') {
    // Load the polyfill conditionally
    const module = await import('https://esm.sh/temporal-polyfill');
    globalThis.Temporal = module.Temporal;
    // Extend Date.prototype if needed
    Date.prototype.toTemporalInstant = module.toTemporalInstant;
  }
  initializeApp();
})();