Đối tượng Date JavaScript truyền thống dựa trên lịch Gregory, khiến việc xây dựng ứng dụng cho người dùng dựa vào các hệ thống lịch khác (chẳng hạn như lịch Hồi giáo (âm lịch), lịch Do Thái (âm dương lịch) hoặc lịch Trung Quốc (âm dương lịch)) trở nên khó khăn. Trước đây, nhà phát triển phải dựa vào các thư viện bên thứ ba phức tạp hoặc tính toán thủ công để hỗ trợ các hệ thống này.
API Temporal cung cấp dịch vụ hỗ trợ hàng đầu cho nhiều hệ thống lịch. Bằng cách liên kết giá trị nhận dạng lịch với các đối tượng ngày, Temporal xử lý các phép tính và định dạng phức tạp cần thiết cho nhiều bối cảnh văn hoá khác nhau theo cách tự nhiên.
Cách triển khai
Cách hỗ trợ các hệ thống lịch toàn cầu bằng Temporal:
- Liên kết lịch (Bắt buộc): Khi tạo hoặc chuyển đổi một đối tượng Temporal (như
Temporal.PlainDate), bạn phải chỉ định hệ thống lịch mong muốn bằngwithCalendar()để thực hiện các thao tác liên quan đến lịch. - Sử dụng giá trị nhận dạng ổn định (Bắt buộc đối với âm dương lịch): Bạn phải sử dụng
monthCodethay vì chỉ mục sốmonthđể xác định các tháng cụ thể trong nhiều năm theo âm dương lịch. Chỉ sử dụng thuộc tính này cho những lịch có tháng nhuận, chẳng hạn như lịch Do Thái và lịch Trung Quốc. - Tôn trọng các bất biến của lịch (Bắt buộc): Khi lặp lại qua các tháng hoặc ngày, bạn không được giả định các giá trị cố định (như 12 tháng trong một năm hoặc 31 ngày trong một tháng). Sử dụng các thuộc tính như
monthsInYearvàdaysInMonthđể đảm bảo mã của bạn hoạt động trên tất cả các lịch. - Sử dụng phép so sánh nhận biết lịch (Bắt buộc): Khi so sánh ngày trong một hệ thống lịch cụ thể, bạn phải sử dụng
Temporal.PlainDate.compare()thay vì so sánh các thuộc tính năm/tháng/ngày theo cách thủ công. Thao tác này sẽ xử lý chính xác thứ tự thời gian trong các quy tắc của lịch.
Mã ví dụ: Chuyển đổi và lặp lại lịch
// 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'
});
Triển khai chiến lược và các phương pháp hay nhất
- NÊN sử dụng
monthsInYearlàm giới hạn trên khi lặp qua các tháng, thay vì giả định là 12. - NÊN sử dụng
monthCodeđể xác định các tháng cụ thể trong lịch sử dụng tháng nhuận (ví dụ: lịch Do Thái hoặc lịch Trung Quốc), bất kể năm nào. - KHÔNG giả định rằng
date.month === 12là tháng cuối cùng của năm. Sử dụngdate.month === date.monthsInYear. - KHÔNG giả định rằng
inLeapYear === truecó nghĩa là năm đó chỉ dài hơn một ngày. Trong âm dương lịch, năm nhuận có thể có thêm một tháng nhuận. - NÊN sử dụng
Temporal.PlainDate.compare()khi so sánh hai ngày trong một hệ thống lịch cụ thể thay vì so sánh các thuộc tính Năm/Tháng/Ngày theo cách thủ công. - NÊN sử dụng
toLocaleString()để định dạng ngày cho người dùng thay vì nối chuỗi theo cách thủ công. - NÊN xác minh rằng hệ thống lịch mục tiêu được môi trường hỗ trợ bằng cách sử dụng
Intl.supportedValuesOf('calendar')trước khi tạo các đối tượng Temporal theo lịch cụ thể. - NÊN lưu ý rằng một số lịch (như các biến thể của lịch Hồi giáo) có thể dựa vào quan sát trực quan thay vì các phép tính cố định. API
Temporaltuân theo cách triển khaiIntlcủa môi trường, thường sử dụng các giá trị gần đúng được tính toán. Đối với các phép tính ngày quan trọng về văn hoá hoặc tôn giáo, hãy xác minh với các chuyên gia về miền hoặc sử dụng các thư viện chuyên biệt. - NÊN tính đến tên niên hiệu khi sử dụng lịch có niên hiệu (ví dụ: lịch Nhật Bản, Phật lịch), bằng cách sử dụng
toLocaleString().
Chiến lược dự phòng
Phương án dự phòng và hỗ trợ trình duyệt cho Temporal
Đối với các trình duyệt chưa hỗ trợ API Temporal gốc, hãy sử dụng tính năng phát hiện tính năng và tải động temporal-polyfill (~15 KB min+gz).
Xin lưu ý rằng việc nhập temporal-polyfill không tự động chỉ định đối tượng Temporal cho phạm vi toàn cục. Hãy chỉ định rõ ràng nếu mã của bạn dựa vào 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();
})();