L'objet JavaScript Date traditionnel est basé sur un calendrier grégorien proleptique, ce qui complique la création d'applications pour les utilisateurs qui s'appuient sur d'autres systèmes de calendrier, tels que les calendriers islamique (lunaire), hébreu (lunisolaire) ou chinois (lunisolaire). Auparavant, les développeurs devaient s'appuyer sur des bibliothèques tierces complexes ou des calculs manuels pour prendre en charge ces systèmes.
L'API Temporal est compatible avec plusieurs systèmes de calendrier. En associant un identifiant de calendrier à des objets de date, Temporal gère de manière native les calculs et la mise en forme complexes requis pour différents contextes culturels.
Implémentation
Pour prendre en charge les systèmes de calendrier mondiaux à l'aide de Temporal :
- Associer un calendrier (obligatoire) : lorsque vous créez ou convertissez un objet temporel (comme
Temporal.PlainDate), vous devez spécifier le système de calendrier souhaité à l'aide dewithCalendar()pour effectuer des opérations sensibles au calendrier. - Utilisez des identifiants stables (obligatoire pour les calendriers lunisolaires) : vous devez utiliser
monthCodeplutôt que l'index numériquemonthpour identifier des mois spécifiques sur plusieurs années dans les calendriers lunisolaires. Utilisez cette option uniquement pour les calendriers qui utilisent des mois bissextiles, comme les calendriers hébreu et chinois. - Respecter les invariants du calendrier (obligatoire) : lorsque vous parcourez les mois ou les jours, vous ne devez pas supposer de valeurs fixes (comme 12 mois dans une année ou 31 jours dans un mois). Utilisez des propriétés telles que
monthsInYearetdaysInMonthpour vous assurer que votre code fonctionne sur tous les agendas. - Utilisez des comparaisons tenant compte du calendrier (obligatoire) : lorsque vous comparez des dates dans un système de calendrier spécifique, vous devez utiliser
Temporal.PlainDate.compare()au lieu de comparer manuellement les propriétés d'année/mois/jour. Cela permet de gérer correctement l'ordre chronologique dans les règles d'agenda.
Exemple de code : conversion et itération d'agendas
// 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'
});
Implémentation stratégique et bonnes pratiques
- À FAIRE : Utilisez
monthsInYearcomme limite supérieure lorsque vous parcourez les mois, plutôt que de supposer qu'il y en a 12. - À FAIRE : Utilisez
monthCodepour identifier des mois spécifiques dans les calendriers qui utilisent des mois intercalaires (par exemple, hébreu ou chinois), quelle que soit l'année. - NE SUPPOSEZ PAS que
date.month === 12est le dernier mois de l'année. Utilisezdate.month === date.monthsInYear. - NE PARTEZ PAS DU PRINCIPE que
inLeapYear === trueimplique que l'année ne dure qu'un jour de plus. Dans les calendriers lunisolaires, il peut ajouter un mois bissextile complet. - À FAIRE : Utilisez
Temporal.PlainDate.compare()lorsque vous comparez deux dates dans un système de calendrier spécifique au lieu de comparer manuellement les propriétés Année/Mois/Jour. - À FAIRE : Utilisez
toLocaleString()pour formater les dates pour les utilisateurs au lieu de concaténer manuellement les chaînes. - À FAIRE : Vérifiez que le système de calendrier cible est compatible avec l'environnement à l'aide de
Intl.supportedValuesOf('calendar')avant de créer des objets temporels spécifiques au calendrier. - À FAIRE : Sachez que certains calendriers (comme les variantes du calendrier islamique) peuvent s'appuyer sur une observation visuelle plutôt que sur des calculs fixes. L'API
Temporalsuit l'implémentationIntlde l'environnement, qui utilise généralement des approximations calculées. Pour les calculs de dates culturelles ou religieuses critiques, vérifiez auprès d'experts du domaine ou utilisez des bibliothèques spécialisées. - PRENEZ EN COMPTE les noms d'ères lorsque vous utilisez des calendriers qui utilisent des ères (par exemple, japonais, bouddhiste), en utilisant
toLocaleString().
Stratégie de remplacement
Solutions de remplacement et compatibilité des navigateurs avec Temporal
Pour les navigateurs qui ne sont pas encore compatibles avec l'API Temporal native, utilisez la détection de fonctionnalités et chargez dynamiquement temporal-polyfill (~15 Ko min+gz).
Notez que l'importation de temporal-polyfill n'attribue pas automatiquement l'objet Temporal au champ d'application global. Attribuez-la explicitement si votre code repose sur 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();
})();