commit eb638c7567b660380c749854a1ced136bdb31de8
parent c28ce896b1e416ee498ff64a8e2ff061e3dd56d6
Author: André Bargull <andre.bargull@gmail.com>
Date: Thu, 27 Nov 2025 10:03:34 +0000
Bug 1999315 - Part 2: Split ISO and non-ISO calendar operations. r=spidermonkey-reviewers,dminor
Implements the editorial changes from <https://github.com/tc39/proposal-temporal/pull/3140>.
Differential Revision: https://phabricator.services.mozilla.com/D272022
Diffstat:
2 files changed, 165 insertions(+), 113 deletions(-)
diff --git a/js/src/builtin/temporal/Calendar.cpp b/js/src/builtin/temporal/Calendar.cpp
@@ -2140,38 +2140,12 @@ static bool RegulateISODate(JSContext* cx, int32_t year, double month,
}
/**
- * CalendarDateToISO ( calendar, fields, overflow )
+ * NonISOCalendarDateToISO ( calendar, fields, overflow )
*/
-static bool CalendarDateToISO(JSContext* cx, CalendarId calendar,
- Handle<CalendarFields> fields,
- TemporalOverflow overflow, ISODate* result) {
- // Step 1.
- if (calendar == CalendarId::ISO8601) {
- // Step 1.a.
- MOZ_ASSERT(fields.has(CalendarField::Year));
- MOZ_ASSERT(fields.has(CalendarField::Month) ||
- fields.has(CalendarField::MonthCode));
- MOZ_ASSERT(fields.has(CalendarField::Day));
-
- // Remaining steps from CalendarResolveFields to resolve the month.
- double month;
- if (!ISOCalendarResolveMonth(cx, fields, &month)) {
- return false;
- }
-
- int32_t intYear;
- if (!mozilla::NumberEqualsInt32(fields.year(), &intYear)) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_TEMPORAL_PLAIN_DATE_INVALID);
- return false;
- }
-
- // Step 1.b.
- return RegulateISODate(cx, intYear, month, fields.day(), overflow, result);
- }
-
- // Step 2.
-
+static bool NonISOCalendarDateToISO(JSContext* cx, CalendarId calendar,
+ Handle<CalendarFields> fields,
+ TemporalOverflow overflow,
+ ISODate* result) {
EraYears eraYears;
if (!CalendarFieldYear(cx, calendar, fields, &eraYears)) {
return false;
@@ -2199,16 +2173,15 @@ static bool CalendarDateToISO(JSContext* cx, CalendarId calendar,
}
/**
- * CalendarMonthDayToISOReferenceDate ( calendar, fields, overflow )
+ * CalendarDateToISO ( calendar, fields, overflow )
*/
-static bool CalendarMonthDayToISOReferenceDate(JSContext* cx,
- CalendarId calendar,
- Handle<CalendarFields> fields,
- TemporalOverflow overflow,
- ISODate* result) {
+static bool CalendarDateToISO(JSContext* cx, CalendarId calendar,
+ Handle<CalendarFields> fields,
+ TemporalOverflow overflow, ISODate* result) {
// Step 1.
if (calendar == CalendarId::ISO8601) {
// Step 1.a.
+ MOZ_ASSERT(fields.has(CalendarField::Year));
MOZ_ASSERT(fields.has(CalendarField::Month) ||
fields.has(CalendarField::MonthCode));
MOZ_ASSERT(fields.has(CalendarField::Day));
@@ -2219,33 +2192,28 @@ static bool CalendarMonthDayToISOReferenceDate(JSContext* cx,
return false;
}
- // Step 1.b.
- int32_t referenceISOYear = 1972;
-
- // Step 1.c.
- double year =
- !fields.has(CalendarField::Year) ? referenceISOYear : fields.year();
-
int32_t intYear;
- if (!mozilla::NumberEqualsInt32(year, &intYear)) {
- // Calendar cycles repeat every 400 years in the Gregorian calendar.
- intYear = int32_t(std::fmod(year, 400));
- }
-
- // Step 1.d.
- ISODate regulated;
- if (!RegulateISODate(cx, intYear, month, fields.day(), overflow,
- ®ulated)) {
+ if (!mozilla::NumberEqualsInt32(fields.year(), &intYear)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_PLAIN_DATE_INVALID);
return false;
}
- // Step 1.e.
- *result = {referenceISOYear, regulated.month, regulated.day};
- return true;
+ // Step 1.b.
+ return RegulateISODate(cx, intYear, month, fields.day(), overflow, result);
}
// Step 2.
+ return NonISOCalendarDateToISO(cx, calendar, fields, overflow, result);
+}
+/**
+ * NonISOMonthDayToISOReferenceDate ( calendar, fields, overflow )
+ */
+static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar,
+ Handle<CalendarFields> fields,
+ TemporalOverflow overflow,
+ ISODate* result) {
EraYears eraYears;
if (fields.has(CalendarField::Year) || fields.has(CalendarField::EraYear)) {
if (!CalendarFieldYear(cx, calendar, fields, &eraYears)) {
@@ -2419,43 +2387,64 @@ static bool CalendarMonthDayToISOReferenceDate(JSContext* cx,
return true;
}
-enum class FieldType { Date, YearMonth, MonthDay };
-
/**
- * CalendarResolveFields ( calendar, fields, type )
+ * CalendarMonthDayToISOReferenceDate ( calendar, fields, overflow )
*/
-static bool CalendarResolveFields(JSContext* cx, CalendarId calendar,
- Handle<CalendarFields> fields,
- FieldType type) {
+static bool CalendarMonthDayToISOReferenceDate(JSContext* cx,
+ CalendarId calendar,
+ Handle<CalendarFields> fields,
+ TemporalOverflow overflow,
+ ISODate* result) {
// Step 1.
if (calendar == CalendarId::ISO8601) {
- // Steps 1.a-e.
- const char* missingField = nullptr;
- if ((type == FieldType::Date || type == FieldType::YearMonth) &&
- !fields.has(CalendarField::Year)) {
- missingField = "year";
- } else if ((type == FieldType::Date || type == FieldType::MonthDay) &&
- !fields.has(CalendarField::Day)) {
- missingField = "day";
- } else if (!fields.has(CalendarField::MonthCode) &&
- !fields.has(CalendarField::Month)) {
- missingField = "month";
- }
+ // Step 1.a.
+ MOZ_ASSERT(fields.has(CalendarField::Month) ||
+ fields.has(CalendarField::MonthCode));
+ MOZ_ASSERT(fields.has(CalendarField::Day));
- if (missingField) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_TEMPORAL_CALENDAR_MISSING_FIELD,
- missingField);
+ // Remaining steps from CalendarResolveFields to resolve the month.
+ double month;
+ if (!ISOCalendarResolveMonth(cx, fields, &month)) {
return false;
}
- // Steps 1.f-n. (Handled in ISOCalendarResolveMonth.)
+ // Step 1.b.
+ int32_t referenceISOYear = 1972;
+
+ // Step 1.c.
+ double year =
+ !fields.has(CalendarField::Year) ? referenceISOYear : fields.year();
+
+ int32_t intYear;
+ if (!mozilla::NumberEqualsInt32(year, &intYear)) {
+ // Calendar cycles repeat every 400 years in the Gregorian calendar.
+ intYear = int32_t(std::fmod(year, 400));
+ }
+
+ // Step 1.d.
+ ISODate regulated;
+ if (!RegulateISODate(cx, intYear, month, fields.day(), overflow,
+ ®ulated)) {
+ return false;
+ }
+ // Step 1.e.
+ *result = {referenceISOYear, regulated.month, regulated.day};
return true;
}
// Step 2.
+ return NonISOMonthDayToISOReferenceDate(cx, calendar, fields, overflow,
+ result);
+}
+
+enum class FieldType { Date, YearMonth, MonthDay };
+/**
+ * NonISOResolveFields ( calendar, fields, type )
+ */
+static bool NonISOResolveFields(JSContext* cx, CalendarId calendar,
+ Handle<CalendarFields> fields, FieldType type) {
// Date and Month-Day require |day| to be present.
bool requireDay = type == FieldType::Date || type == FieldType::MonthDay;
@@ -2497,7 +2486,45 @@ static bool CalendarResolveFields(JSContext* cx, CalendarId calendar,
}
/**
+ * CalendarResolveFields ( calendar, fields, type )
+ */
+static bool CalendarResolveFields(JSContext* cx, CalendarId calendar,
+ Handle<CalendarFields> fields,
+ FieldType type) {
+ // Step 1.
+ if (calendar == CalendarId::ISO8601) {
+ // Steps 1.a-e.
+ const char* missingField = nullptr;
+ if ((type == FieldType::Date || type == FieldType::YearMonth) &&
+ !fields.has(CalendarField::Year)) {
+ missingField = "year";
+ } else if ((type == FieldType::Date || type == FieldType::MonthDay) &&
+ !fields.has(CalendarField::Day)) {
+ missingField = "day";
+ } else if (!fields.has(CalendarField::MonthCode) &&
+ !fields.has(CalendarField::Month)) {
+ missingField = "month";
+ }
+
+ if (missingField) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_CALENDAR_MISSING_FIELD,
+ missingField);
+ return false;
+ }
+
+ // Steps 1.f-n. (Handled in ISOCalendarResolveMonth.)
+
+ return true;
+ }
+
+ // Step 2.
+ return NonISOResolveFields(cx, calendar, fields, type);
+}
+
+/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[Era]] field.
*/
@@ -2540,6 +2567,7 @@ bool js::temporal::CalendarEra(JSContext* cx, Handle<CalendarValue> calendar,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[EraYear]] field.
*/
@@ -2574,6 +2602,7 @@ bool js::temporal::CalendarEraYear(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[Year]] field.
*/
@@ -2606,6 +2635,7 @@ bool js::temporal::CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[Month]] field.
*/
@@ -2634,6 +2664,7 @@ bool js::temporal::CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[MonthCode]] field.
*/
@@ -2679,6 +2710,7 @@ bool js::temporal::CalendarMonthCode(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[Day]] field.
*/
@@ -2707,6 +2739,7 @@ bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarValue> calendar,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[DayOfWeek]] field.
*/
@@ -2745,6 +2778,7 @@ bool js::temporal::CalendarDayOfWeek(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[DayOfYear]] field.
*/
@@ -2806,6 +2840,7 @@ bool js::temporal::CalendarDayOfYear(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[WeekOfYear]].[[Week]] field.
*/
@@ -2833,6 +2868,7 @@ bool js::temporal::CalendarWeekOfYear(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[WeekOfYear]].[[Year]] field.
*/
@@ -2860,6 +2896,7 @@ bool js::temporal::CalendarYearOfWeek(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[DaysInWeek]] field.
*/
@@ -2880,6 +2917,7 @@ bool js::temporal::CalendarDaysInWeek(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[DaysInMonth]] field.
*/
@@ -2909,6 +2947,7 @@ bool js::temporal::CalendarDaysInMonth(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[DaysInYear]] field.
*/
@@ -2938,6 +2977,7 @@ bool js::temporal::CalendarDaysInYear(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[MonthsInYear]] field.
*/
@@ -2967,6 +3007,7 @@ bool js::temporal::CalendarMonthsInYear(JSContext* cx,
/**
* CalendarISOToDate ( calendar, isoDate )
+ * NonISOCalendarISOToDate ( calendar, isoDate )
*
* Return the Calendar Date Record's [[InLeapYear]] field.
*/
@@ -3633,10 +3674,12 @@ static bool AddNonISODate(JSContext* cx, CalendarId calendarId,
return true;
}
-static bool AddCalendarDate(JSContext* cx, CalendarId calendarId,
- const ISODate& isoDate,
- const DateDuration& duration,
- TemporalOverflow overflow, ISODate* result) {
+/**
+ * NonISODateAdd ( calendar, isoDate, duration, overflow )
+ */
+static bool NonISODateAdd(JSContext* cx, CalendarId calendarId,
+ const ISODate& isoDate, const DateDuration& duration,
+ TemporalOverflow overflow, ISODate* result) {
// ICU4X doesn't yet provide a public API for CalendarDateAdd.
//
// https://github.com/unicode-org/icu4x/issues/3964
@@ -3696,7 +3739,7 @@ bool js::temporal::CalendarDateAdd(JSContext* cx,
return false;
}
} else {
- if (!AddCalendarDate(cx, calendarId, isoDate, duration, overflow, result)) {
+ if (!NonISODateAdd(cx, calendarId, isoDate, duration, overflow, result)) {
return false;
}
}
@@ -4028,10 +4071,12 @@ static bool DifferenceNonISODate(JSContext* cx, CalendarId calendarId,
return true;
}
-static bool DifferenceCalendarDate(JSContext* cx, CalendarId calendarId,
- const ISODate& one, const ISODate& two,
- TemporalUnit largestUnit,
- DateDuration* result) {
+/**
+ * NonISODateUntil ( calendar, one, two, largestUnit )
+ */
+static bool NonISODateUntil(JSContext* cx, CalendarId calendarId,
+ const ISODate& one, const ISODate& two,
+ TemporalUnit largestUnit, DateDuration* result) {
// ICU4X doesn't yet provide a public API for CalendarDateUntil.
//
// https://github.com/unicode-org/icu4x/issues/3964
@@ -4093,5 +4138,5 @@ bool js::temporal::CalendarDateUntil(JSContext* cx,
}
// Step 2.
- return DifferenceCalendarDate(cx, calendarId, one, two, largestUnit, result);
+ return NonISODateUntil(cx, calendarId, one, two, largestUnit, result);
}
diff --git a/js/src/builtin/temporal/CalendarFields.cpp b/js/src/builtin/temporal/CalendarFields.cpp
@@ -545,31 +545,10 @@ bool js::temporal::PreparePartialCalendarFields(
}
/**
- * CalendarFieldKeysToIgnore ( calendar, keys )
+ * NonISOFieldKeysToIgnore ( calendar, keys )
*/
-static auto CalendarFieldKeysToIgnore(CalendarId calendar,
- mozilla::EnumSet<CalendarField> keys) {
- // Step 1.
- if (calendar == CalendarId::ISO8601) {
- // Steps 1.a and 1.b.i.
- auto ignoredKeys = keys;
-
- // Step 1.b.ii.
- if (keys.contains(CalendarField::Month)) {
- ignoredKeys += CalendarField::MonthCode;
- }
-
- // Step 1.b.iii.
- else if (keys.contains(CalendarField::MonthCode)) {
- ignoredKeys += CalendarField::Month;
- }
-
- // Steps 1.c-d.
- return ignoredKeys;
- }
-
- // Step 2.
-
+static auto NonISOFieldKeysToIgnore(CalendarId calendar,
+ mozilla::EnumSet<CalendarField> keys) {
static constexpr auto eraOrEraYear = mozilla::EnumSet{
CalendarField::Era,
CalendarField::EraYear,
@@ -618,6 +597,34 @@ static auto CalendarFieldKeysToIgnore(CalendarId calendar,
}
/**
+ * CalendarFieldKeysToIgnore ( calendar, keys )
+ */
+static auto CalendarFieldKeysToIgnore(CalendarId calendar,
+ mozilla::EnumSet<CalendarField> keys) {
+ // Step 1.
+ if (calendar == CalendarId::ISO8601) {
+ // Steps 1.a and 1.b.i.
+ auto ignoredKeys = keys;
+
+ // Step 1.b.ii.
+ if (keys.contains(CalendarField::Month)) {
+ ignoredKeys += CalendarField::MonthCode;
+ }
+
+ // Step 1.b.iii.
+ else if (keys.contains(CalendarField::MonthCode)) {
+ ignoredKeys += CalendarField::Month;
+ }
+
+ // Steps 1.c-d.
+ return ignoredKeys;
+ }
+
+ // Step 2.
+ return NonISOFieldKeysToIgnore(calendar, keys);
+}
+
+/**
* CalendarMergeFields ( calendar, fields, additionalFields )
*/
CalendarFields js::temporal::CalendarMergeFields(