Range Error when trying to formate dates using calendar other then `gregory` or `iso8601`
khawarizmus opened this issue · 3 comments
Given this example test:
const dateTimeUTCString = "20201001T090000Z"; // UTC date-time
const dateTimeUTC = Temporal.Instant.from(
dateTimeUTCString
).toZonedDateTime({
timeZone: "UTC",
calendar: "gregory", // <-- test success depends on this
});
expect(iCalendarTimeFormat(dateTimeUTC)).toBe("090000"); // <-- fails if calendar is other then gregory or iso8601
where iCalendarTimeFormat
is as follows:
export function iCalendarTimeFormat(date: Date | DateTime): string {
return date
.toLocaleString("us-EN", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
})
.split(":")
.join("");
}
the test fails is we change the calendar
property of the ZonedDate
to anything other then gregory
or iso8601
throwing the following error:
RangeError: cannot format ZonedDateTime with calendar [inserted calendar] in locale with calendar gregory
Tested with values like: islamic-umalqura
, japanes
, chinese
, coptic
etc..
If it helps, here is the part of the code that is responsible for throwing the error
My question is why this is failing and what does the error actually mean?
Fixed the tests by changing iCalendarTimeFormat
implementation to this:
export function iCalendarTimeFormat(date: Date | DateTime): string {
return date
.toLocaleString("us-EN", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
...(date instanceof Temporal.ZonedDateTime ||
date instanceof Temporal.PlainDateTime
? { calendar: date.calendarId }
: {}),
})
.split(":")
.join("");
}
But still want to understand the error meaning and why it's hapenning
The error is because the date's calendar doesn't match the locale's calendar. The thinking goes, if you have a non-ISO8601 calendar (a 'human' calendar) then that's probably intentional and you want it formatted in that calendar. At the same time, locales have a human calendar associated with them as well, either explicitly or as a default, and it's surprising if you are formatting dates in that locale and they end up in a completely different calendar.
So since it's not clear which calendar should 'win' in that case, we throw, so that the programmer knows they have to make an explicit choice.
More precisely:
- The date's calendar must match the locale's calendar; or
- The date must be in the ISO 8601 calendar, in which case the locale's calendar wins.
So if you want the locale's calendar to always win, do this:
date.withCalendar("iso8601").toLocaleString(...);
If you want the date's calendar to always win, then the workaround you posted above is the correct one.
More info: tc39/proposal-temporal#262 (comment)
Thank you for the explanation. it's clear to me.