Das herkömmliche JavaScript-Objekt Date basiert auf einem proleptischen gregorianischen Kalender. Das macht es schwierig, Anwendungen für Nutzer zu entwickeln, die andere Kalendersysteme verwenden, z. B. den islamischen (Mondkalender), den hebräischen (lunisolar) oder den chinesischen (lunisolar) Kalender. Bisher mussten Entwickler auf komplexe Bibliotheken von Drittanbietern oder manuelle Berechnungen zurückgreifen, um diese Systeme zu unterstützen.
Die Temporal API bietet erstklassige Unterstützung für mehrere Kalendersysteme. Durch die Verknüpfung einer Kalenderkennung mit Datumsobjekten übernimmt Temporal die komplexe Arithmetik und Formatierung, die für verschiedene kulturelle Kontexte erforderlich ist, nativ.
Implementierung
So unterstützen Sie globale Kalendersysteme mit Temporal:
- Kalender verknüpfen (erforderlich): Wenn Sie ein Temporal-Objekt (z. B.
Temporal.PlainDate) erstellen oder konvertieren, müssen Sie das gewünschte Kalendersystem mitwithCalendar()angeben, um kalenderspezifische Vorgänge auszuführen. - Stabile Kennungen verwenden (für lunisolare Kalender obligatorisch): Du musst
monthCodeanstelle des numerischenmonth-Index verwenden, um bestimmte Monate in lunisolaren Kalendern über mehrere Jahre hinweg zu identifizieren. Verwenden Sie diese Option nur für Kalender, die Schaltmonate verwenden, z. B. den hebräischen und den chinesischen Kalender. - Kalenderinvarianten beachten (obligatorisch): Wenn Sie Monate oder Tage durchlaufen, dürfen Sie nicht von festen Werten ausgehen (z. B. 12 Monate in einem Jahr oder 31 Tage in einem Monat). Verwenden Sie Properties wie
monthsInYearunddaysInMonth, damit Ihr Code in allen Kalendern funktioniert. - Kalenderbezogene Vergleiche verwenden (obligatorisch): Wenn Sie Datumsangaben innerhalb eines bestimmten Kalendersystems vergleichen, müssen Sie
Temporal.PlainDate.compare()verwenden, anstatt die Eigenschaften für Jahr/Monat/Tag manuell zu vergleichen. So wird die chronologische Reihenfolge innerhalb der Kalenderregeln korrekt berücksichtigt.
Beispielcode: Kalender konvertieren und iterieren
// 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'
});
Strategische Implementierung und Best Practices
- DO: Verwenden Sie
monthsInYearals Obergrenze, wenn Sie Monate durchlaufen, anstatt von 12 auszugehen. - DO: Verwenden Sie
monthCode, um bestimmte Monate in Kalendern mit Schaltmonaten (z.B. hebräischer oder chinesischer Kalender) unabhängig vom Jahr zu identifizieren. - Gehen Sie NICHT davon aus, dass
date.month === 12der letzte Monat des Jahres ist. Verwenden Siedate.month === date.monthsInYear. - Gehen Sie NICHT davon aus, dass
inLeapYear === truebedeutet, dass das Jahr nur einen Tag länger ist. In Lunisolarkalendern kann ein ganzer Schaltmonat hinzugefügt werden. - DO use
Temporal.PlainDate.compare()when comparing two dates in a specific calendar system instead of comparing Year/Month/Day properties manually. - DO: Verwenden Sie
toLocaleString(), um Datumsangaben für Nutzer zu formatieren, anstatt Strings manuell zu verketten. - PRÜFEN Sie, ob das Zielkalendersystem von der Umgebung unterstützt wird, indem Sie
Intl.supportedValuesOf('calendar')verwenden, bevor Sie kalenderspezifische Temporal-Objekte erstellen. - BEACHTEN SIE, dass einige Kalender (z. B. Varianten des islamischen Kalenders) möglicherweise auf visueller Beobachtung und nicht auf festen Berechnungen basieren. Die
TemporalAPI folgt derIntl-Implementierung der Umgebung, die normalerweise berechnete Näherungswerte verwendet. Für wichtige kulturelle oder religiöse Datumsberechnungen sollten Sie sich an Experten wenden oder spezialisierte Bibliotheken verwenden. - DO account for era names when using calendars that use eras (e.g., Japanese, Buddhist), using
toLocaleString().
Fallback-Strategie
Fallbacks und Browserunterstützung für Temporal
Für Browser, die die native Temporal API noch nicht unterstützen, können Sie die Funktionserkennung verwenden und temporal-polyfill dynamisch laden (~15 KB min+gz).
Beachten Sie, dass das Importieren von temporal-polyfill das Temporal-Objekt nicht automatisch dem globalen Bereich zuweist. Weisen Sie ihn explizit zu, wenn Ihr Code auf globalThis.Temporal basiert.
// 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();
})();