O objeto JavaScript Date tradicional é baseado em um calendário gregoriano proléptico, o que dificulta a criação de aplicativos para usuários que usam outros sistemas de calendário, como o islâmico (lunar), o hebraico (lunissolar) ou o chinês (lunissolar). Antes, os desenvolvedores precisavam usar bibliotecas complexas de terceiros ou fazer cálculos manuais para oferecer suporte a esses sistemas.
A API Temporal oferece suporte de primeira classe para vários sistemas de calendário. Ao associar um identificador de calendário a objetos de data, o Temporal lida com a aritmética e a formatação complexas exigidas para diferentes contextos culturais de forma nativa.
Como implementar
Para oferecer suporte a sistemas de calendário globais usando o Temporal:
- Associe uma agenda (obrigatório): ao criar ou converter um objeto Temporal (como
Temporal.PlainDate), especifique o sistema de agenda desejado usandowithCalendar()para realizar operações que dependem da agenda. - Use identificadores estáveis (obrigatório para calendários lunissolares): use
monthCodeem vez do índice numéricomonthpara identificar meses específicos em anos diferentes em calendários lunissolares. Use apenas para calendários que usam meses bissextos, como o hebraico e o chinês. - Respeite as invariantes do calendário (obrigatório): ao iterar meses ou dias, não presuma valores fixos (como 12 meses em um ano ou 31 dias em um mês). Use propriedades como
monthsInYearedaysInMonthpara garantir que seu código funcione em todos os calendários. - Use comparações que consideram o calendário (obrigatório): ao comparar datas em um sistema de calendário específico, use
Temporal.PlainDate.compare()em vez de comparar as propriedades de ano/mês/dia manualmente. Isso lida corretamente com a ordem cronológica dentro das regras do calendário.
Exemplo de código: como converter e iterar 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'
});
Implementação estratégica e práticas recomendadas
- USE
monthsInYearcomo o limite superior ao fazer um loop pelos meses, em vez de assumir 12. - USE
monthCodepara identificar meses específicos em calendários que usam meses bissextos (por exemplo, hebraico ou chinês), independentemente do ano. - NÃO presuma que
date.month === 12é o último mês do ano. Usedate.month === date.monthsInYear. - NÃO presuma que
inLeapYear === trueimplica que o ano é apenas um dia mais longo. Em calendários lunissolares, pode ser adicionado um mês bissexto completo. - USE
Temporal.PlainDate.compare()ao comparar duas datas em um sistema de calendário específico, em vez de comparar as propriedades de ano/mês/dia manualmente. - USE
toLocaleString()para formatar datas para os usuários em vez de concatenação manual de strings. - VERIFIQUE se o sistema de calendário de destino é compatível com o ambiente usando
Intl.supportedValuesOf('calendar')antes de criar objetos temporais específicos do calendário. - É IMPORTANTE saber que alguns calendários (como variantes do calendário islâmico) podem depender de observação visual em vez de cálculos fixos. A API
Temporalsegue a implementação deIntldo ambiente, que geralmente usa aproximações calculadas. Para cálculos de datas culturais ou religiosas importantes, consulte especialistas no assunto ou use bibliotecas especializadas. - É RECOMENDÁVEL considerar os nomes das eras ao usar calendários que as utilizam (por exemplo, japonês, budista), usando
toLocaleString().
Estratégia de fallback
Substitutos e suporte do navegador para Temporal
Para navegadores que ainda não são compatíveis com a API Temporal nativa, use a detecção de recursos e carregue dinamicamente temporal-polyfill (~15 KB min+gz).
A importação de temporal-polyfill não atribui automaticamente o objeto Temporal ao escopo global. Atribua-o explicitamente se o código depender de 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();
})();