diff --git a/Jint.Tests.Test262/Test262Harness.settings.json b/Jint.Tests.Test262/Test262Harness.settings.json index 6a334e9a8f..82d81b92da 100644 --- a/Jint.Tests.Test262/Test262Harness.settings.json +++ b/Jint.Tests.Test262/Test262Harness.settings.json @@ -144,62 +144,8 @@ // .NET ar-EG locale defaults to latn numbering system instead of arab (Arabic-Indic digits) "intl402/DateTimeFormat/prototype/format/temporal-objects-no-time-clip-non-latin-numerals.js", - // Japanese era boundary resolution (Heisei/Showa month-day for PlainYearMonth reference day) - "intl402/Temporal/PlainYearMonth/from/reference-day-japanese.js", - // Leap year until - Hebrew calendar year boundary alignment - "intl402/Temporal/PlainDate/prototype/until/leap-year-until.js", - "intl402/Temporal/PlainDateTime/prototype/until/leap-year-until.js", - "intl402/Temporal/ZonedDateTime/prototype/until/leap-year-until.js", - - // PlainMonthDay non-ISO calendar support needs more work - // PlainMonthDay from - reference year selection, constrain-to-leap-day, and lunisolar dates - "intl402/Temporal/PlainMonthDay/from/chinese-calendar-dates.js", - "intl402/Temporal/PlainMonthDay/from/constrain-to-leap-day.js", - "intl402/Temporal/PlainMonthDay/from/dangi-30-day-leap-months.js", - "intl402/Temporal/PlainMonthDay/from/dangi-calendar-dates.js", - "intl402/Temporal/PlainMonthDay/from/reference-date-noniso-calendar.js", - "intl402/Temporal/PlainMonthDay/from/reference-year-1972.js", - - // PlainMonthDay monthCode - reference year selection affects month code for Chinese/Dangi - "intl402/Temporal/PlainMonthDay/prototype/monthCode/chinese-calendar-dates.js", - "intl402/Temporal/PlainMonthDay/prototype/monthCode/dangi-calendar-dates.js", - - // PlainYearMonth non-ISO calendar reference day and since - "intl402/Temporal/PlainYearMonth/from/reference-day-chinese.js", - "intl402/Temporal/PlainYearMonth/from/reference-day-hebrew.js", - "intl402/Temporal/PlainYearMonth/prototype/since/chinese-calendar-dates.js", - "intl402/Temporal/PlainYearMonth/prototype/since/dangi-calendar-dates.js", - - // === ANNEX B EXCLUSIONS === - - // Acornima parser limitation: HTML-like comments () in dynamic Function() bodies/params - // Requires parser option for HTML comments which Acornima doesn't support in function constructor context - "annexB/built-ins/Function/createdynfn-html-close-comment-body.js", - "annexB/built-ins/Function/createdynfn-html-close-comment-params.js", - "annexB/built-ins/Function/createdynfn-html-open-comment-body.js", - "annexB/built-ins/Function/createdynfn-html-open-comment-params.js", - "annexB/built-ins/Function/createdynfn-no-line-terminator-html-close-comment-body.js", - - // Acornima parser limitation: assignment target type validation for call expressions - // Per B.3.7, non-strict mode should allow CallExpression as assignment target in certain cases - "annexB/language/expressions/assignmenttargettype/callexpression-as-for-in-lhs.js", - "annexB/language/expressions/assignmenttargettype/callexpression-as-for-of-lhs.js", - "annexB/language/expressions/assignmenttargettype/callexpression-in-compound-assignment.js", - "annexB/language/expressions/assignmenttargettype/callexpression-in-postfix-update.js", - "annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js", - "annexB/language/expressions/assignmenttargettype/callexpression.js", - "annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js", - - // Acornima parser limitation: for-in initializer in non-strict mode - // Per B.3.6, `for (var x = expr in obj)` should be allowed in sloppy mode - "annexB/language/statements/for-in/nonstrict-initializer.js", - - // Acornima parser/RegExp limitation: malformed named groups in non-unicode mode - // Per B.1.2, non-unicode RegExp should accept some malformed named group syntax - "annexB/built-ins/RegExp/named-groups/non-unicode-malformed.js", - - // Intl.Era-monthcode: remaining failures in era/calendar support + // Remaining intl402/Temporal calendar failures "intl402/DateTimeFormat/prototype/formatToParts/compare-to-temporal-lunisolar.js", "intl402/DateTimeFormat/prototype/formatToParts/compare-to-temporal.js", "intl402/DateTimeFormat/prototype/formatToParts/era.js", @@ -207,25 +153,17 @@ "intl402/Temporal/PlainDate/from/calendarresolvefields-error-ordering-chinese.js", "intl402/Temporal/PlainDate/from/calendarresolvefields-error-ordering-gregory.js", "intl402/Temporal/PlainDate/from/calendarresolvefields-error-ordering-japanese.js", - "intl402/Temporal/PlainDate/from/canonicalize-era-codes-non-gregorian.js", - "intl402/Temporal/PlainDate/from/era-boundary-japanese.js", - "intl402/Temporal/PlainDate/from/era-japanese.js", "intl402/Temporal/PlainDate/from/extreme-dates.js", "intl402/Temporal/PlainDate/from/hebrew-keviah.js", "intl402/Temporal/PlainDate/from/invalid-month-codes-hebrew.js", - "intl402/Temporal/PlainDate/from/islamic-rgsa.js", - "intl402/Temporal/PlainDate/from/islamic.js", "intl402/Temporal/PlainDate/from/non-positive-single-era-year.js", - "intl402/Temporal/PlainDate/from/roundtrip-from-iso.js", "intl402/Temporal/PlainDate/from/roundtrip-from-property-bag.js", "intl402/Temporal/PlainDate/from/roundtrip-from-string.js", "intl402/Temporal/PlainDate/prototype/add/leap-months-hebrew.js", "intl402/Temporal/PlainDate/prototype/add/leap-year-hebrew.js", - "intl402/Temporal/PlainDate/prototype/dayOfYear/non-iso-calendar-basic.js", "intl402/Temporal/PlainDate/prototype/daysInMonth/basic-islamic-umalqura.js", "intl402/Temporal/PlainDate/prototype/daysInYear/basic-islamic-umalqura.js", "intl402/Temporal/PlainDate/prototype/inLeapYear/basic-islamic-umalqura.js", - "intl402/Temporal/PlainDate/prototype/since/era-boundary-japanese.js", "intl402/Temporal/PlainDate/prototype/since/intercalary-month-coptic.js", "intl402/Temporal/PlainDate/prototype/since/intercalary-month-ethioaa.js", "intl402/Temporal/PlainDate/prototype/since/intercalary-month-ethiopic.js", @@ -235,15 +173,14 @@ "intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js", "intl402/Temporal/PlainDate/prototype/subtract/leap-months-hebrew.js", "intl402/Temporal/PlainDate/prototype/subtract/leap-year-hebrew.js", - "intl402/Temporal/PlainDate/prototype/until/era-boundary-japanese.js", "intl402/Temporal/PlainDate/prototype/until/intercalary-month-coptic.js", "intl402/Temporal/PlainDate/prototype/until/intercalary-month-ethioaa.js", "intl402/Temporal/PlainDate/prototype/until/intercalary-month-ethiopic.js", "intl402/Temporal/PlainDate/prototype/until/leap-months-chinese.js", "intl402/Temporal/PlainDate/prototype/until/leap-months-dangi.js", "intl402/Temporal/PlainDate/prototype/until/leap-months-hebrew.js", + "intl402/Temporal/PlainDate/prototype/until/leap-year-until.js", "intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js", - "intl402/Temporal/PlainDate/prototype/with/basic-coptic.js", "intl402/Temporal/PlainDate/prototype/with/basic-islamic-umalqura.js", "intl402/Temporal/PlainDate/prototype/with/leap-months-hebrew.js", "intl402/Temporal/PlainDate/prototype/with/leap-year-hebrew.js", @@ -251,23 +188,16 @@ "intl402/Temporal/PlainDate/prototype/year/arithmetic-year.js", "intl402/Temporal/PlainDate/prototype/year/epoch-year.js", "intl402/Temporal/PlainDateTime/from/calendarresolvefields-error-ordering-chinese.js", - "intl402/Temporal/PlainDateTime/from/canonicalize-era-codes-non-gregorian.js", - "intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js", - "intl402/Temporal/PlainDateTime/from/era-japanese.js", "intl402/Temporal/PlainDateTime/from/extreme-dates.js", "intl402/Temporal/PlainDateTime/from/invalid-month-codes-hebrew.js", - "intl402/Temporal/PlainDateTime/from/islamic-rgsa.js", - "intl402/Temporal/PlainDateTime/from/islamic.js", "intl402/Temporal/PlainDateTime/from/non-positive-single-era-year.js", "intl402/Temporal/PlainDateTime/from/roundtrip-from-property-bag.js", "intl402/Temporal/PlainDateTime/from/roundtrip-from-string.js", "intl402/Temporal/PlainDateTime/prototype/add/leap-months-hebrew.js", "intl402/Temporal/PlainDateTime/prototype/add/leap-year-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/dayOfYear/non-iso-calendar-basic.js", "intl402/Temporal/PlainDateTime/prototype/daysInMonth/basic-islamic-umalqura.js", "intl402/Temporal/PlainDateTime/prototype/daysInYear/basic-islamic-umalqura.js", "intl402/Temporal/PlainDateTime/prototype/inLeapYear/basic-islamic-umalqura.js", - "intl402/Temporal/PlainDateTime/prototype/since/era-boundary-japanese.js", "intl402/Temporal/PlainDateTime/prototype/since/intercalary-month-coptic.js", "intl402/Temporal/PlainDateTime/prototype/since/intercalary-month-ethioaa.js", "intl402/Temporal/PlainDateTime/prototype/since/intercalary-month-ethiopic.js", @@ -277,42 +207,17 @@ "intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js", "intl402/Temporal/PlainDateTime/prototype/subtract/leap-months-hebrew.js", "intl402/Temporal/PlainDateTime/prototype/subtract/leap-year-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/until/era-boundary-japanese.js", "intl402/Temporal/PlainDateTime/prototype/until/intercalary-month-coptic.js", "intl402/Temporal/PlainDateTime/prototype/until/intercalary-month-ethioaa.js", "intl402/Temporal/PlainDateTime/prototype/until/intercalary-month-ethiopic.js", "intl402/Temporal/PlainDateTime/prototype/until/leap-months-chinese.js", "intl402/Temporal/PlainDateTime/prototype/until/leap-months-dangi.js", "intl402/Temporal/PlainDateTime/prototype/until/leap-months-hebrew.js", + "intl402/Temporal/PlainDateTime/prototype/until/leap-year-until.js", "intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/with/basic-buddhist.js", - "intl402/Temporal/PlainDateTime/prototype/with/basic-coptic.js", "intl402/Temporal/PlainDateTime/prototype/with/basic-islamic-umalqura.js", - "intl402/Temporal/PlainDateTime/prototype/with/basic-roc.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-chinese.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-dangi.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-indian.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-islamic-civil.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-islamic-tbla.js", - "intl402/Temporal/PlainDateTime/prototype/with/constrain-day-persian.js", "intl402/Temporal/PlainDateTime/prototype/with/leap-months-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/with/leap-year-buddhist.js", "intl402/Temporal/PlainDateTime/prototype/with/leap-year-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/with/leap-year-roc.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-buddhist.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-chinese.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-coptic.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-dangi.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-ethioaa.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-ethiopic.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-hebrew.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-indian.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-islamic-civil.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-islamic-tbla.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-islamic-umalqura.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-persian.js", - "intl402/Temporal/PlainDateTime/prototype/with/mutually-exclusive-fields-roc.js", "intl402/Temporal/PlainDateTime/prototype/withCalendar/extreme-dates.js", "intl402/Temporal/PlainDateTime/prototype/year/arithmetic-year.js", "intl402/Temporal/PlainDateTime/prototype/year/epoch-year.js", @@ -320,12 +225,16 @@ "intl402/Temporal/PlainMonthDay/from/calendarresolvefields-error-ordering-hebrew.js", "intl402/Temporal/PlainMonthDay/from/calendarresolvefields-error-ordering-islamic.js", "intl402/Temporal/PlainMonthDay/from/chinese-30-day-leap-months.js", + "intl402/Temporal/PlainMonthDay/from/chinese-calendar-dates.js", "intl402/Temporal/PlainMonthDay/from/chinese-dangi-constrain-rare-leap-months.js", "intl402/Temporal/PlainMonthDay/from/chinese-dangi-leap-month-with-year-from-options-bag-overflow-reject.js", "intl402/Temporal/PlainMonthDay/from/chinese-dangi-leap-month-with-year-from-options-bag.js", "intl402/Temporal/PlainMonthDay/from/chinese-leap-month-codes-common.js", "intl402/Temporal/PlainMonthDay/from/chinese-month-codes.js", + "intl402/Temporal/PlainMonthDay/from/constrain-to-leap-day.js", "intl402/Temporal/PlainMonthDay/from/coptic-invalid-month-code-m14.js", + "intl402/Temporal/PlainMonthDay/from/dangi-30-day-leap-months.js", + "intl402/Temporal/PlainMonthDay/from/dangi-calendar-dates.js", "intl402/Temporal/PlainMonthDay/from/dangi-leap-month-codes-common.js", "intl402/Temporal/PlainMonthDay/from/dangi-month-codes.js", "intl402/Temporal/PlainMonthDay/from/dont-calculate-month-info-for-out-of-range-year.js", @@ -337,25 +246,23 @@ "intl402/Temporal/PlainMonthDay/from/invalid-month-codes-hebrew.js", "intl402/Temporal/PlainMonthDay/from/islamic-civil-invalid-month-code-m13.js", "intl402/Temporal/PlainMonthDay/from/islamic-civil-month-codes.js", - "intl402/Temporal/PlainMonthDay/from/islamic-rgsa.js", "intl402/Temporal/PlainMonthDay/from/islamic-tbla-invalid-month-code-m13.js", "intl402/Temporal/PlainMonthDay/from/islamic-tbla-month-codes.js", "intl402/Temporal/PlainMonthDay/from/islamic-umalqura-invalid-month-code-m13.js", "intl402/Temporal/PlainMonthDay/from/islamic-umalqura-month-codes.js", - "intl402/Temporal/PlainMonthDay/from/islamic.js", "intl402/Temporal/PlainMonthDay/from/persian-invalid-month-code-m13.js", + "intl402/Temporal/PlainMonthDay/from/reference-date-noniso-calendar.js", + "intl402/Temporal/PlainMonthDay/from/reference-year-1972.js", + "intl402/Temporal/PlainMonthDay/prototype/monthCode/chinese-calendar-dates.js", + "intl402/Temporal/PlainMonthDay/prototype/monthCode/dangi-calendar-dates.js", "intl402/Temporal/PlainYearMonth/from/calendarresolvefields-error-ordering-chinese.js", "intl402/Temporal/PlainYearMonth/from/calendarresolvefields-error-ordering-gregory.js", "intl402/Temporal/PlainYearMonth/from/calendarresolvefields-error-ordering-japanese.js", - "intl402/Temporal/PlainYearMonth/from/canonicalize-era-codes-non-gregorian.js", - "intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js", - "intl402/Temporal/PlainYearMonth/from/era-japanese.js", "intl402/Temporal/PlainYearMonth/from/extreme-dates.js", "intl402/Temporal/PlainYearMonth/from/extreme-unsupported-dates.js", "intl402/Temporal/PlainYearMonth/from/invalid-month-codes-hebrew.js", - "intl402/Temporal/PlainYearMonth/from/islamic-rgsa.js", - "intl402/Temporal/PlainYearMonth/from/islamic.js", "intl402/Temporal/PlainYearMonth/from/non-positive-single-era-year.js", + "intl402/Temporal/PlainYearMonth/from/reference-day-hebrew.js", "intl402/Temporal/PlainYearMonth/from/roundtrip-from-property-bag.js", "intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js", "intl402/Temporal/PlainYearMonth/prototype/add/leap-year-hebrew.js", @@ -373,10 +280,11 @@ "intl402/Temporal/PlainYearMonth/prototype/since/basic-islamic-tbla.js", "intl402/Temporal/PlainYearMonth/prototype/since/basic-islamic-umalqura.js", "intl402/Temporal/PlainYearMonth/prototype/since/basic-persian.js", + "intl402/Temporal/PlainYearMonth/prototype/since/chinese-calendar-dates.js", + "intl402/Temporal/PlainYearMonth/prototype/since/dangi-calendar-dates.js", "intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-islamic-civil.js", "intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-islamic-tbla.js", "intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-islamic-umalqura.js", - "intl402/Temporal/PlainYearMonth/prototype/since/era-boundary-japanese.js", "intl402/Temporal/PlainYearMonth/prototype/since/intercalary-month-coptic.js", "intl402/Temporal/PlainYearMonth/prototype/since/intercalary-month-ethioaa.js", "intl402/Temporal/PlainYearMonth/prototype/since/intercalary-month-ethiopic.js", @@ -399,18 +307,13 @@ "intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-islamic-civil.js", "intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-islamic-tbla.js", "intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-islamic-umalqura.js", - "intl402/Temporal/PlainYearMonth/prototype/until/era-boundary-japanese.js", "intl402/Temporal/PlainYearMonth/prototype/until/intercalary-month-coptic.js", "intl402/Temporal/PlainYearMonth/prototype/until/intercalary-month-ethioaa.js", "intl402/Temporal/PlainYearMonth/prototype/until/intercalary-month-ethiopic.js", "intl402/Temporal/PlainYearMonth/prototype/until/leap-months-chinese.js", "intl402/Temporal/PlainYearMonth/prototype/until/leap-months-dangi.js", "intl402/Temporal/PlainYearMonth/prototype/until/leap-months-hebrew.js", - "intl402/Temporal/PlainYearMonth/prototype/with/basic-buddhist.js", - "intl402/Temporal/PlainYearMonth/prototype/with/basic-coptic.js", - "intl402/Temporal/PlainYearMonth/prototype/with/basic-roc.js", "intl402/Temporal/PlainYearMonth/prototype/with/leap-months-hebrew.js", - "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-buddhist.js", "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-chinese.js", "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-coptic.js", "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-dangi.js", @@ -422,27 +325,19 @@ "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-islamic-tbla.js", "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-islamic-umalqura.js", "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-persian.js", - "intl402/Temporal/PlainYearMonth/prototype/with/mutually-exclusive-fields-roc.js", "intl402/Temporal/PlainYearMonth/prototype/year/arithmetic-year.js", "intl402/Temporal/PlainYearMonth/prototype/year/epoch-year.js", "intl402/Temporal/ZonedDateTime/from/calendarresolvefields-error-ordering-chinese.js", - "intl402/Temporal/ZonedDateTime/from/canonicalize-era-codes-non-gregorian.js", - "intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js", - "intl402/Temporal/ZonedDateTime/from/era-japanese.js", "intl402/Temporal/ZonedDateTime/from/extreme-dates.js", "intl402/Temporal/ZonedDateTime/from/invalid-month-codes-hebrew.js", - "intl402/Temporal/ZonedDateTime/from/islamic-rgsa.js", - "intl402/Temporal/ZonedDateTime/from/islamic.js", "intl402/Temporal/ZonedDateTime/from/non-positive-single-era-year.js", "intl402/Temporal/ZonedDateTime/from/roundtrip-from-property-bag.js", "intl402/Temporal/ZonedDateTime/from/roundtrip-from-string.js", "intl402/Temporal/ZonedDateTime/prototype/add/leap-months-hebrew.js", "intl402/Temporal/ZonedDateTime/prototype/add/leap-year-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/dayOfYear/non-iso-calendar-basic.js", "intl402/Temporal/ZonedDateTime/prototype/daysInMonth/basic-islamic-umalqura.js", "intl402/Temporal/ZonedDateTime/prototype/daysInYear/basic-islamic-umalqura.js", "intl402/Temporal/ZonedDateTime/prototype/inLeapYear/basic-islamic-umalqura.js", - "intl402/Temporal/ZonedDateTime/prototype/since/era-boundary-japanese.js", "intl402/Temporal/ZonedDateTime/prototype/since/intercalary-month-coptic.js", "intl402/Temporal/ZonedDateTime/prototype/since/intercalary-month-ethioaa.js", "intl402/Temporal/ZonedDateTime/prototype/since/intercalary-month-ethiopic.js", @@ -452,48 +347,48 @@ "intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js", "intl402/Temporal/ZonedDateTime/prototype/subtract/leap-months-hebrew.js", "intl402/Temporal/ZonedDateTime/prototype/subtract/leap-year-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/until/era-boundary-japanese.js", "intl402/Temporal/ZonedDateTime/prototype/until/intercalary-month-coptic.js", "intl402/Temporal/ZonedDateTime/prototype/until/intercalary-month-ethioaa.js", "intl402/Temporal/ZonedDateTime/prototype/until/intercalary-month-ethiopic.js", "intl402/Temporal/ZonedDateTime/prototype/until/leap-months-chinese.js", "intl402/Temporal/ZonedDateTime/prototype/until/leap-months-dangi.js", "intl402/Temporal/ZonedDateTime/prototype/until/leap-months-hebrew.js", + "intl402/Temporal/ZonedDateTime/prototype/until/leap-year-until.js", "intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/with/basic-buddhist.js", - "intl402/Temporal/ZonedDateTime/prototype/with/basic-coptic.js", "intl402/Temporal/ZonedDateTime/prototype/with/basic-islamic-umalqura.js", - "intl402/Temporal/ZonedDateTime/prototype/with/basic-roc.js", - "intl402/Temporal/ZonedDateTime/prototype/with/calendarresolvefields-error-ordering-gregory.js", - "intl402/Temporal/ZonedDateTime/prototype/with/calendarresolvefields-error-ordering-japanese.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-chinese.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-dangi.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-indian.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-islamic-civil.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-islamic-tbla.js", - "intl402/Temporal/ZonedDateTime/prototype/with/constrain-day-persian.js", "intl402/Temporal/ZonedDateTime/prototype/with/leap-months-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/with/leap-year-buddhist.js", "intl402/Temporal/ZonedDateTime/prototype/with/leap-year-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/with/leap-year-roc.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-buddhist.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-chinese.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-coptic.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-dangi.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-ethioaa.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-ethiopic.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-gregory.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-hebrew.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-indian.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-islamic-civil.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-islamic-tbla.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-islamic-umalqura.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-persian.js", - "intl402/Temporal/ZonedDateTime/prototype/with/mutually-exclusive-fields-roc.js", "intl402/Temporal/ZonedDateTime/prototype/withCalendar/extreme-dates.js", "intl402/Temporal/ZonedDateTime/prototype/year/arithmetic-year.js", - "intl402/Temporal/ZonedDateTime/prototype/year/epoch-year.js" + "intl402/Temporal/ZonedDateTime/prototype/year/epoch-year.js", + + // === ANNEX B EXCLUSIONS === + + // Acornima parser limitation: HTML-like comments () in dynamic Function() bodies/params + // Requires parser option for HTML comments which Acornima doesn't support in function constructor context + "annexB/built-ins/Function/createdynfn-html-close-comment-body.js", + "annexB/built-ins/Function/createdynfn-html-close-comment-params.js", + "annexB/built-ins/Function/createdynfn-html-open-comment-body.js", + "annexB/built-ins/Function/createdynfn-html-open-comment-params.js", + "annexB/built-ins/Function/createdynfn-no-line-terminator-html-close-comment-body.js", + + // Acornima parser limitation: assignment target type validation for call expressions + // Per B.3.7, non-strict mode should allow CallExpression as assignment target in certain cases + "annexB/language/expressions/assignmenttargettype/callexpression-as-for-in-lhs.js", + "annexB/language/expressions/assignmenttargettype/callexpression-as-for-of-lhs.js", + "annexB/language/expressions/assignmenttargettype/callexpression-in-compound-assignment.js", + "annexB/language/expressions/assignmenttargettype/callexpression-in-postfix-update.js", + "annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js", + "annexB/language/expressions/assignmenttargettype/callexpression.js", + "annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js", + + // Acornima parser limitation: for-in initializer in non-strict mode + // Per B.3.6, `for (var x = expr in obj)` should be allowed in sloppy mode + "annexB/language/statements/for-in/nonstrict-initializer.js", + + // Acornima parser/RegExp limitation: malformed named groups in non-unicode mode + // Per B.1.2, non-unicode RegExp should accept some malformed named group syntax + "annexB/built-ins/RegExp/named-groups/non-unicode-malformed.js" ] } diff --git a/Jint/Native/Temporal/NonIsoCalendars.cs b/Jint/Native/Temporal/NonIsoCalendars.cs index d9ab4c838c..4bf668c7c4 100644 --- a/Jint/Native/Temporal/NonIsoCalendars.cs +++ b/Jint/Native/Temporal/NonIsoCalendars.cs @@ -408,13 +408,15 @@ internal static DurationRecord CalendarDateUntil(string calendar, in IsoDate one { return calendar switch { + // Single-era calendars: always use the primary era "hebrew" => "am", "persian" => "ap", - "chinese" or "dangi" => null, // no eras - "coptic" => calDate.Year >= 1 ? "am" : "bd", - "ethiopic" => calDate.Year >= 1 ? "am" : "aa", + "coptic" => "am", "ethioaa" => "aa", "indian" => "shaka", + "chinese" or "dangi" => null, // no eras + // Multi-era calendars: era depends on year + "ethiopic" => calDate.Year >= 1 ? "am" : "aa", "islamic-umalqura" or "islamic-civil" or "islamic-tbla" => calDate.Year >= 1 ? "ah" : "bh", _ => null }; @@ -427,13 +429,15 @@ internal static DurationRecord CalendarDateUntil(string calendar, in IsoDate one { return calendar switch { + // Single-era calendars: eraYear = year directly "hebrew" => calDate.Year, "persian" => calDate.Year, - "chinese" or "dangi" => null, // no eras - "coptic" => calDate.Year >= 1 ? calDate.Year : 1 - calDate.Year, - "ethiopic" => calDate.Year >= 1 ? calDate.Year : calDate.Year + 5500, + "coptic" => calDate.Year, "ethioaa" => calDate.Year, "indian" => calDate.Year, + "chinese" or "dangi" => null, // no eras + // Multi-era calendars: eraYear depends on year + "ethiopic" => calDate.Year >= 1 ? calDate.Year : calDate.Year + 5500, "islamic-umalqura" or "islamic-civil" or "islamic-tbla" => calDate.Year >= 1 ? calDate.Year : 1 - calDate.Year, _ => null }; @@ -450,34 +454,26 @@ internal static int MonthCodeToOrdinal(string calendar, Calendar? cal, int year, if (calendar is "persian" or "indian" or "islamic-umalqura" or "islamic-civil" or "islamic-tbla") { - // These calendars have no leap months - if (isLeap) + // These calendars have 12 months and no leap months + // Invalid monthCode (M13+, M00, or any leap month) is always rejected + if (isLeap || displayMonth < 1 || displayMonth > 12) { - if (string.Equals(overflow, "reject", StringComparison.Ordinal)) - { - throw new InvalidOperationException("reject"); - } - - // Constrain: use the base month + throw new InvalidOperationException("reject"); } - return Clamp(displayMonth, 1, 12); + return displayMonth; } if (calendar is "coptic" or "ethiopic" or "ethioaa") { // 13-month calendars with no leap months - if (isLeap) + // Invalid monthCode (M14+, M00, or any leap month) is always rejected + if (isLeap || displayMonth < 1 || displayMonth > 13) { - if (string.Equals(overflow, "reject", StringComparison.Ordinal)) - { - throw new InvalidOperationException("reject"); - } - - // Constrain: use the base month + throw new InvalidOperationException("reject"); } - return Clamp(displayMonth, 1, 13); + return displayMonth; } var leapOrdinal = GetLeapMonthOrdinal(calendar, cal, year); diff --git a/Jint/Native/Temporal/PlainDate/PlainDateConstructor.cs b/Jint/Native/Temporal/PlainDate/PlainDateConstructor.cs index 62f3672b62..7c97720006 100644 --- a/Jint/Native/Temporal/PlainDate/PlainDateConstructor.cs +++ b/Jint/Native/Temporal/PlainDate/PlainDateConstructor.cs @@ -102,6 +102,8 @@ private JsPlainDate ToTemporalDateFromObjectWithOptions(ObjectInstance obj, JsVa calendar = TemporalHelpers.ToTemporalCalendarIdentifier(_realm, calendarValue); } + TemporalHelpers.RejectTemporalUnsupportedCalendar(_realm, calendar); + // 2. day - read and convert immediately var dayValue = obj.Get("day"); if (dayValue.IsUndefined()) diff --git a/Jint/Native/Temporal/PlainDate/PlainDatePrototype.cs b/Jint/Native/Temporal/PlainDate/PlainDatePrototype.cs index ff6dda6ede..f64e644d8e 100644 --- a/Jint/Native/Temporal/PlainDate/PlainDatePrototype.cs +++ b/Jint/Native/Temporal/PlainDate/PlainDatePrototype.cs @@ -120,7 +120,11 @@ private JsNumber GetDay(JsValue thisObject, JsCallArguments arguments) return JsNumber.Create(TemporalHelpers.CalendarDay(pd.Calendar, pd.IsoDate)); } private JsNumber GetDayOfWeek(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDate(thisObject).IsoDate.DayOfWeek()); - private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDate(thisObject).IsoDate.DayOfYear()); + private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) + { + var pd = ValidatePlainDate(thisObject); + return JsNumber.Create(TemporalHelpers.CalendarDayOfYear(pd.Calendar, pd.IsoDate)); + } private JsValue GetWeekOfYear(JsValue thisObject, JsCallArguments arguments) { var pd = ValidatePlainDate(thisObject); @@ -338,20 +342,10 @@ private JsPlainDate With(JsValue thisObject, JsCallArguments arguments) var calMonth = month ?? plainDate.IsoDate.Month; var calDay = day ?? plainDate.IsoDate.Day; - if (monthCode is not null) + var mc = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar(_realm, monthCode, month); + if (mc.HasValue) { - var mc = TemporalHelpers.ParseMonthCode(_realm, monthCode); - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for calendar: {monthCode}"); - } - - if (month.HasValue && month.Value != mc) - { - Throw.RangeError(_realm, "month and monthCode must match"); - } - - calMonth = mc; + calMonth = mc.Value; } var date2 = TemporalHelpers.CalendarDateToISO(_realm, plainDate.Calendar, calYear, calMonth, calDay, overflow); @@ -368,28 +362,8 @@ private JsPlainDate With(JsValue thisObject, JsCallArguments arguments) var finalMonth = month ?? plainDate.IsoDate.Month; var finalDayVal = day ?? plainDate.IsoDate.Day; - // Handle monthCode - if provided, validate and use it - int? parsedMonthCode = null; - if (monthCode is not null) - { - parsedMonthCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); - - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for ISO 8601 calendar: {monthCode}"); - } - - if (parsedMonthCode.Value < 1 || parsedMonthCode.Value > 12) - { - Throw.RangeError(_realm, $"Month {parsedMonthCode.Value} is not valid for ISO 8601 calendar"); - } - } - - if (month.HasValue && parsedMonthCode.HasValue && month.Value != parsedMonthCode.Value) - { - Throw.RangeError(_realm, "month and monthCode must match"); - } - + // Handle monthCode - validate and use it + var parsedMonthCode = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar(_realm, monthCode, month); if (parsedMonthCode.HasValue) { finalMonth = parsedMonthCode.Value; diff --git a/Jint/Native/Temporal/PlainDateTime/PlainDateTimeConstructor.cs b/Jint/Native/Temporal/PlainDateTime/PlainDateTimeConstructor.cs index 27ac313543..eac7be56f6 100644 --- a/Jint/Native/Temporal/PlainDateTime/PlainDateTimeConstructor.cs +++ b/Jint/Native/Temporal/PlainDateTime/PlainDateTimeConstructor.cs @@ -252,6 +252,8 @@ private JsPlainDateTime ToTemporalDateTimeFromFields(ObjectInstance obj, JsValue calendar = TemporalHelpers.ToTemporalCalendarIdentifier(_realm, calendarProp); } + TemporalHelpers.RejectTemporalUnsupportedCalendar(_realm, calendar); + // 2. day - read and convert immediately var dayValue = obj.Get("day"); if (dayValue.IsUndefined()) diff --git a/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs b/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs index 6f42854344..0db1bba97f 100644 --- a/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs +++ b/Jint/Native/Temporal/PlainDateTime/PlainDateTimePrototype.cs @@ -134,7 +134,11 @@ private JsNumber GetDay(JsValue thisObject, JsCallArguments arguments) private JsNumber GetMicrosecond(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDateTime(thisObject).IsoDateTime.Microsecond); private JsNumber GetNanosecond(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDateTime(thisObject).IsoDateTime.Nanosecond); private JsNumber GetDayOfWeek(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDateTime(thisObject).IsoDateTime.Date.DayOfWeek()); - private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidatePlainDateTime(thisObject).IsoDateTime.Date.DayOfYear()); + private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) + { + var pdt = ValidatePlainDateTime(thisObject); + return JsNumber.Create(TemporalHelpers.CalendarDayOfYear(pdt.Calendar, pdt.IsoDateTime.Date)); + } private JsValue GetWeekOfYear(JsValue thisObject, JsCallArguments arguments) { var pdt = ValidatePlainDateTime(thisObject); @@ -221,6 +225,8 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) var any = false; JsValue v; + var isNonIso8601 = plainDateTime.Calendar is not "iso8601" and not "gregory"; + v = obj.Get("day"); int day; if (!v.IsUndefined()) @@ -230,7 +236,7 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - day = NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar) + day = isNonIso8601 ? TemporalHelpers.CalendarDay(plainDateTime.Calendar, plainDateTime.IsoDateTime.Date) : plainDateTime.IsoDateTime.Day; } @@ -282,7 +288,7 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - month = NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar) + month = isNonIso8601 ? TemporalHelpers.CalendarMonth(plainDateTime.Calendar, plainDateTime.IsoDateTime.Date) : plainDateTime.IsoDateTime.Month; } @@ -294,7 +300,7 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) any = true; monthCode = TypeConverter.ToString(v); } - else if (NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar) && !monthExplicit) + else if (isNonIso8601 && !monthExplicit) { monthCode = TemporalHelpers.CalendarMonthCode(plainDateTime.Calendar, plainDateTime.IsoDateTime.Date); } @@ -326,7 +332,7 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - year = NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar) + year = isNonIso8601 ? TemporalHelpers.CalendarYear(plainDateTime.Calendar, plainDateTime.IsoDateTime.Date) : plainDateTime.IsoDateTime.Year; } @@ -365,41 +371,32 @@ private JsPlainDateTime With(JsValue thisObject, JsCallArguments arguments) // Process monthCode (after reading options, before algorithmic validation) int? monthFromCode = null; - if (monthCode is not null) + if (!isNonIso8601 || TemporalHelpers.IsGregorianBasedCalendar(plainDateTime.Calendar)) { - // Use ParseMonthCode which properly handles "L" suffix for leap months - monthFromCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); + // ISO/Gregorian-based: validate no leap months, month/monthCode consistency + monthFromCode = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar( + _realm, monthCode, monthExplicit ? month : null); - // ISO/Gregorian calendars don't support leap months - if (!NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar)) + if (monthFromCode.HasValue) { - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for calendar: {monthCode}"); - } - - if (monthFromCode.Value < 1 || monthFromCode.Value > 12) - { - Throw.RangeError(_realm, $"Month {monthFromCode.Value} is not valid for calendar"); - } + month = monthFromCode.Value; } } - - // Validate month/monthCode consistency (ISO calendars only - non-ISO ordinal ≠ display) - if (!NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar) && - monthFromCode.HasValue && month != plainDateTime.IsoDateTime.Month && month != monthFromCode.Value) + else if (monthCode is not null) { - Throw.RangeError(_realm, "month and monthCode must match"); - } + // Non-ISO: just parse for well-formedness, let CalendarDateToISO handle validation + monthFromCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); - // Use monthCode if provided (for ISO calendars) - if (monthFromCode.HasValue && !NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar)) - { - month = monthFromCode.Value; + // When monthCode is explicitly provided but month is not, signal CalendarDateToIso + // to use monthCode by passing month=0 (avoids month/monthCode mismatch rejection) + if (!monthExplicit) + { + month = 0; + } } IsoDate? date; - if (NonIsoCalendars.IsNonIsoCalendar(plainDateTime.Calendar)) + if (isNonIso8601) { date = TemporalHelpers.CalendarDateToISO(_realm, plainDateTime.Calendar, year, month, day, overflow, monthCode); } diff --git a/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayConstructor.cs b/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayConstructor.cs index a57cf0afa2..1a0a25929d 100644 --- a/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayConstructor.cs +++ b/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayConstructor.cs @@ -195,6 +195,8 @@ private JsPlainMonthDay ToTemporalMonthDayFromFields(ObjectInstance obj, JsValue calendar = TemporalHelpers.ToTemporalCalendarIdentifier(_realm, calendarValue); } + TemporalHelpers.RejectTemporalUnsupportedCalendar(_realm, calendar); + // 2. day - read and convert immediately var dayValue = obj.Get("day"); if (dayValue.IsUndefined()) diff --git a/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayPrototype.cs b/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayPrototype.cs index 250a703315..c0369ece39 100644 --- a/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayPrototype.cs +++ b/Jint/Native/Temporal/PlainMonthDay/PlainMonthDayPrototype.cs @@ -182,35 +182,20 @@ private JsPlainMonthDay With(JsValue thisObject, JsCallArguments arguments) // NOW validate monthCode (after options are read) int? monthFromCode = null; - if (monthCode is not null) + if (!isNonIso) { - monthFromCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); + // ISO/Gregorian: validate no leap months, month/monthCode consistency + monthFromCode = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar( + _realm, monthCode, !monthProp.IsUndefined() ? month : null); - // For ISO/Gregorian calendars: validate monthCode is valid (01-12, no leap months) - if (!isNonIso) + if (monthFromCode.HasValue) { - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for calendar: {monthCode}"); - } - - if (monthFromCode.Value < 1 || monthFromCode.Value > 12) - { - Throw.RangeError(_realm, $"Month {monthFromCode.Value} is not valid for calendar"); - } + month = monthFromCode.Value; } } - - // Validate month/monthCode consistency (ISO only) - if (!isNonIso && monthFromCode.HasValue && !monthProp.IsUndefined() && month != monthFromCode.Value) + else if (monthCode is not null) { - Throw.RangeError(_realm, "month and monthCode must match"); - } - - // Use monthCode if provided (ISO only - non-ISO handled by CalendarDateToISO) - if (!isNonIso && monthFromCode.HasValue) - { - month = monthFromCode.Value; + monthFromCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); } if (isNonIso) diff --git a/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthConstructor.cs b/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthConstructor.cs index 0d4669b2ce..0665323512 100644 --- a/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthConstructor.cs +++ b/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthConstructor.cs @@ -234,6 +234,8 @@ private JsPlainYearMonth ToTemporalYearMonthFromObjectWithOptions(ObjectInstance calendar = TemporalHelpers.ToTemporalCalendarIdentifier(_realm, calendarValue); } + TemporalHelpers.RejectTemporalUnsupportedCalendar(_realm, calendar); + // 2. era/eraYear - read for era-supporting calendars (alphabetically between calendar and month) var eraYear = TemporalHelpers.ReadEraFields(_realm, obj, calendar); diff --git a/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthPrototype.cs b/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthPrototype.cs index 52f31a684a..b307fed75a 100644 --- a/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthPrototype.cs +++ b/Jint/Native/Temporal/PlainYearMonth/PlainYearMonthPrototype.cs @@ -158,7 +158,7 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) // Read and convert properties in alphabetical order with immediate conversion per spec // Order: month, monthCode, year - var isNonIso = NonIsoCalendars.IsNonIsoCalendar(ym.Calendar); + var isNonIso8601 = ym.Calendar is not "iso8601" and not "gregory"; var monthProp = obj.Get("month"); int month; @@ -168,7 +168,7 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) } else { - month = isNonIso + month = isNonIso8601 ? TemporalHelpers.CalendarMonth(ym.Calendar, ym.IsoDate) : ym.IsoDate.Month; } @@ -202,7 +202,7 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) monthCode = TypeConverter.ToString(monthCodeProp); } } - else if (isNonIso && monthProp.IsUndefined()) + else if (isNonIso8601 && monthProp.IsUndefined()) { // Only default monthCode from the calendar when month was not explicitly provided; // if the user set month explicitly, let month drive the conversion without monthCode @@ -217,7 +217,7 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) } else { - year = isNonIso + year = isNonIso8601 ? TemporalHelpers.CalendarYear(ym.Calendar, ym.IsoDate) : ym.IsoDate.Year; } @@ -254,13 +254,36 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) // Read options BEFORE any validation (per spec) var overflow = TemporalHelpers.GetOverflowOption(_realm, options); - // Handle non-ISO calendars via CalendarDateToISO - if (isNonIso) + // Handle non-ISO8601 calendars via CalendarDateToISO + if (isNonIso8601) { // Validate monthCode well-formedness if explicitly provided if (!monthCodeProp.IsUndefined() && monthCode is not null) { - TemporalHelpers.ParseMonthCode(_realm, monthCode); + var mc = TemporalHelpers.ParseMonthCode(_realm, monthCode); + + // Gregorian-based calendars don't support leap months + if (TemporalHelpers.IsGregorianBasedCalendar(ym.Calendar)) + { + if (monthCode.Length == 4 && monthCode[3] == 'L') + { + Throw.RangeError(_realm, $"Leap months are not valid for calendar: {monthCode}"); + } + + if (mc < 1 || mc > 12) + { + Throw.RangeError(_realm, $"Month {mc} is not valid for calendar"); + } + + // month/monthCode consistency for Gregorian-based + if (!monthProp.IsUndefined() && month != mc) + { + Throw.RangeError(_realm, "month and monthCode must match"); + } + + month = mc; + monthCode = null; // let month drive for Gregorian-based + } } var date = TemporalHelpers.CalendarDateToISO(_realm, ym.Calendar, year, month, 1, overflow, monthCode); @@ -277,31 +300,9 @@ private JsPlainYearMonth With(JsValue thisObject, JsCallArguments arguments) return _constructor.Construct(date.Value, ym.Calendar); } - // ISO calendar path - int? parsedMonthFromCode = null; - if (monthCode is not null) - { - // Use ParseMonthCode for proper validation (well-formedness and range) - parsedMonthFromCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); - - // For ISO 8601 calendar: validate monthCode is valid (01-12, no leap months) - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for ISO 8601 calendar: {monthCode}"); - } - if (parsedMonthFromCode.Value < 1 || parsedMonthFromCode.Value > 12) - { - Throw.RangeError(_realm, $"Month {parsedMonthFromCode.Value} is not valid for ISO 8601 calendar"); - } - } - - // Validate month/monthCode consistency - if (parsedMonthFromCode.HasValue && !monthProp.IsUndefined() && month != parsedMonthFromCode.Value) - { - Throw.RangeError(_realm, "month and monthCode must match"); - } - - // Use monthCode if provided + // ISO calendar path - validate monthCode + var parsedMonthFromCode = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar( + _realm, monthCode, !monthProp.IsUndefined() ? month : null); if (parsedMonthFromCode.HasValue) { month = parsedMonthFromCode.Value; diff --git a/Jint/Native/Temporal/TemporalHelpers.cs b/Jint/Native/Temporal/TemporalHelpers.cs index 455f46533a..17d8dd0836 100644 --- a/Jint/Native/Temporal/TemporalHelpers.cs +++ b/Jint/Native/Temporal/TemporalHelpers.cs @@ -1607,6 +1607,50 @@ public static bool ISOYearMonthWithinLimits(int year, int month) }; } + /// + /// Throws RangeError for observation-based calendars that cannot be used in Temporal. + /// "islamic" and "islamic-rgsa" are only valid for Intl.DateTimeFormat, not Temporal. + /// + internal static void RejectTemporalUnsupportedCalendar(Realm realm, string calendar) + { + if (calendar is "islamic" or "islamic-rgsa") + { + Throw.RangeError(realm, $"Calendar '{calendar}' is not supported for Temporal operations"); + } + } + + /// + /// Validates a monthCode for calendars that don't support leap months (ISO, Gregorian-based). + /// Also validates month/monthCode consistency when both are provided. + /// Returns the parsed month number from the monthCode, or null if no monthCode. + /// + internal static int? ValidateMonthCodeForNonLeapCalendar(Realm realm, string? monthCode, int? explicitMonth) + { + if (monthCode is null) + { + return null; + } + + var parsed = ParseMonthCode(realm, monthCode); + + if (monthCode.Length == 4 && monthCode[3] == 'L') + { + Throw.RangeError(realm, $"Leap months are not valid for calendar: {monthCode}"); + } + + if (parsed < 1 || parsed > 12) + { + Throw.RangeError(realm, $"Month {parsed} is not valid for calendar"); + } + + if (explicitMonth.HasValue && explicitMonth.Value != parsed) + { + Throw.RangeError(realm, "month and monthCode must match"); + } + + return parsed; + } + /// /// Converts calendar-system year/month/day to ISO year/month/day. /// For non-ISO calendars, the input fields are in the calendar's system @@ -1614,6 +1658,13 @@ public static bool ISOYearMonthWithinLimits(int year, int month) /// public static IsoDate? CalendarDateToISO(Realm realm, string calendar, int year, int month, int day, string overflow, string? monthCode = null) { + // Observation-based calendars cannot be used for Temporal arithmetic + if (calendar is "islamic" or "islamic-rgsa") + { + Throw.RangeError(realm, $"Calendar '{calendar}' is not supported for Temporal operations"); + return null; + } + if (calendar is "iso8601" or "gregory") { return RegulateIsoDate(year, month, day, overflow); @@ -1849,7 +1900,6 @@ internal static bool CalendarUsesEras(string calendar) case "buddhist": return "be"; case "japanese": - // Temporal uses 1873 as Meiji start (when Japan adopted Gregorian calendar) if (isoYear >= 2019) return "reiwa"; if (isoYear >= 1989) return "heisei"; if (isoYear >= 1926) return "showa"; @@ -1980,6 +2030,32 @@ internal static int CalendarDay(string calendar, in IsoDate isoDate) return isoDate.Day; } + /// + /// Returns the day of the year in the calendar system for the given ISO date. + /// + internal static int CalendarDayOfYear(string calendar, in IsoDate isoDate) + { + if (IsGregorianBasedCalendar(calendar)) + { + return isoDate.DayOfYear(); + } + + if (NonIsoCalendars.IsNonIsoCalendar(calendar)) + { + var calDate = NonIsoCalendars.IsoToCalendarDate(calendar, isoDate); + // Find the first day of the calendar year + var firstDay = NonIsoCalendars.CalendarDateToIso(calendar, calDate.Year, "M01", 0, 1, "constrain"); + if (firstDay is not null) + { + var epochThis = IsoDateToDays(isoDate.Year, isoDate.Month, isoDate.Day); + var epochFirst = IsoDateToDays(firstDay.Value.Year, firstDay.Value.Month, firstDay.Value.Day); + return (int) (epochThis - epochFirst) + 1; + } + } + + return isoDate.DayOfYear(); + } + /// /// Returns the number of days in the calendar month containing the given ISO date. /// @@ -2117,8 +2193,8 @@ private static string JapaneseEra(in IsoDate d) if (y > 1926 || (y == 1926 && m == 12 && day >= 25)) return "showa"; // Taisho: July 30, 1912 - December 24, 1926 if (y > 1912 || (y == 1912 && (m > 7 || (m == 7 && day >= 30)))) return "taisho"; - // Meiji: January 25, 1873 - July 29, 1912 - if (y > 1873 || (y == 1873 && (m > 1 || (m == 1 && day >= 25)))) return "meiji"; + // Meiji: year 1873+ (Japan adopted Gregorian calendar in Meiji 6) + if (y >= 1873) return "meiji"; // Before Meiji return y >= 1 ? "ce" : "bce"; } @@ -2132,7 +2208,7 @@ private static int JapaneseEraYear(in IsoDate d) "heisei" => d.Year - 1988, "showa" => d.Year - 1925, "taisho" => d.Year - 1911, - "meiji" => d.Year - 1872, + "meiji" => d.Year - 1867, "bce" => 1 - d.Year, _ => d.Year // "ce" }; @@ -2198,9 +2274,9 @@ private static int ComputeYearFromEra(Realm realm, string calendar, string era, if (era is "heisei") return eraYear + 1988; if (era is "showa") return eraYear + 1925; if (era is "taisho") return eraYear + 1911; - if (era is "meiji") return eraYear + 1872; - if (era is "ce" or "gregory") return eraYear; - if (era is "bce" or "gregory-inverse") return 1 - eraYear; + if (era is "meiji") return eraYear + 1867; + if (era is "ce" or "ad" or "gregory") return eraYear; + if (era is "bce" or "bc" or "gregory-inverse") return 1 - eraYear; break; case "roc": if (era is "roc" or "minguo") diff --git a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs index 7c43987962..e2f29c2c0a 100644 --- a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs +++ b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimeConstructor.cs @@ -194,6 +194,8 @@ private JsZonedDateTime ToZonedDateTimeFromFieldsWithOptions(ObjectInstance obj, calendar = TemporalHelpers.ToTemporalCalendarIdentifier(_realm, calendarProp); } + TemporalHelpers.RejectTemporalUnsupportedCalendar(_realm, calendar); + // 2. day - read and convert immediately var dayValue = obj.Get("day"); if (dayValue.IsUndefined()) diff --git a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs index 9ec7725c8e..05930ac01c 100644 --- a/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs +++ b/Jint/Native/Temporal/ZonedDateTime/ZonedDateTimePrototype.cs @@ -194,8 +194,12 @@ private JsBigInt GetEpochNanoseconds(JsValue thisObject, JsCallArguments argumen private JsNumber GetDayOfWeek(JsValue thisObject, JsCallArguments arguments) => JsNumber.Create(ValidateZonedDateTime(thisObject).GetIsoDateTime().Date.DayOfWeek()); - private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) => - JsNumber.Create(ValidateZonedDateTime(thisObject).GetIsoDateTime().Date.DayOfYear()); + private JsNumber GetDayOfYear(JsValue thisObject, JsCallArguments arguments) + { + var zdt = ValidateZonedDateTime(thisObject); + var date = zdt.GetIsoDateTime().Date; + return JsNumber.Create(TemporalHelpers.CalendarDayOfYear(zdt.Calendar, date)); + } private JsValue GetWeekOfYear(JsValue thisObject, JsCallArguments arguments) { @@ -327,7 +331,7 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) // Each field is read and immediately converted, defaulting to current value if undefined // Track if at least one property is present - var isNonIso = NonIsoCalendars.IsNonIsoCalendar(zdt.Calendar); + var isNonIso8601 = zdt.Calendar is not "iso8601" and not "gregory"; var dayValue = obj.Get("day"); int day; @@ -337,7 +341,7 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - day = isNonIso + day = isNonIso8601 ? TemporalHelpers.CalendarDay(zdt.Calendar, current.Date) : current.Day; } @@ -364,7 +368,7 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - month = isNonIso + month = isNonIso8601 ? TemporalHelpers.CalendarMonth(zdt.Calendar, current.Date) : current.Month; } @@ -401,7 +405,7 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) monthCode = TypeConverter.ToString(monthCodeValue); } } - else if (isNonIso && !monthExplicit) + else if (isNonIso8601 && !monthExplicit) { monthCode = TemporalHelpers.CalendarMonthCode(zdt.Calendar, current.Date); } @@ -428,16 +432,40 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) } else { - year = isNonIso + year = isNonIso8601 ? TemporalHelpers.CalendarYear(zdt.Calendar, current.Date) : current.Year; } + // Read era/eraYear only for calendars that support them + var hasEraFields = false; + if (TemporalHelpers.CalendarUsesEras(zdt.Calendar)) + { + var eraValue = obj.Get("era"); + var eraYearValue = obj.Get("eraYear"); + if (!eraValue.IsUndefined() || !eraYearValue.IsUndefined()) + { + hasEraFields = true; + if (!eraValue.IsUndefined() && !eraYearValue.IsUndefined()) + { + var eraYear2 = TemporalHelpers.ReadEraFields(_realm, obj, zdt.Calendar); + if (eraYear2.HasValue) + { + year = eraYear2.Value; + } + } + else + { + Throw.TypeError(_realm, "Both era and eraYear must be provided together"); + } + } + } + // Check that at least one property is present if (dayValue.IsUndefined() && hourValue.IsUndefined() && microsecondValue.IsUndefined() && millisecondValue.IsUndefined() && minuteValue.IsUndefined() && monthValue.IsUndefined() && monthCodeValue.IsUndefined() && nanosecondValue.IsUndefined() && offsetValue.IsUndefined() && - secondValue.IsUndefined() && yearValue.IsUndefined()) + secondValue.IsUndefined() && yearValue.IsUndefined() && !hasEraFields) { Throw.TypeError(_realm, "with() requires at least one temporal property"); } @@ -477,46 +505,33 @@ private JsZonedDateTime With(JsValue thisObject, JsCallArguments arguments) // Parse and validate monthCode int? parsedMonthCode = null; - if (monthCode is not null) + if (!isNonIso8601 || TemporalHelpers.IsGregorianBasedCalendar(zdt.Calendar)) { - // Parse monthCode for well-formedness - parsedMonthCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); - - if (!isNonIso) - { - // For ISO 8601 calendar: validate monthCode is valid (01-12, no leap months) - if (monthCode.Length == 4 && monthCode[3] == 'L') - { - Throw.RangeError(_realm, $"Leap months are not valid for ISO 8601 calendar: {monthCode}"); - } - - if (parsedMonthCode.Value < 1 || parsedMonthCode.Value > 12) - { - Throw.RangeError(_realm, $"Month {parsedMonthCode.Value} is not valid for ISO 8601 calendar"); - } - } + // ISO/Gregorian-based: validate no leap months, month/monthCode consistency + parsedMonthCode = TemporalHelpers.ValidateMonthCodeForNonLeapCalendar( + _realm, monthCode, !monthValue.IsUndefined() ? month : null); - // Check if month property was explicitly provided (not just defaulted) - // Use the monthValue that was already read earlier - if (!monthValue.IsUndefined()) + if (parsedMonthCode.HasValue) { - // Both month and monthCode provided - they must match - if (month != parsedMonthCode.Value) - { - Throw.RangeError(_realm, "month and monthCode must match"); - } + month = parsedMonthCode.Value; } + } + else if (monthCode is not null) + { + // Non-ISO: just parse for well-formedness, let CalendarDateToISO handle validation + parsedMonthCode = TemporalHelpers.ParseMonthCode(_realm, monthCode); - // Use monthCode if provided (for ISO calendars) - if (!isNonIso) + // When monthCode is explicitly provided but month is not, signal CalendarDateToIso + // to use monthCode by passing month=0 (avoids month/monthCode mismatch rejection) + if (monthValue.IsUndefined()) { - month = parsedMonthCode.Value; + month = 0; } } // Regulate date with user's overflow option IsoDate? date; - if (isNonIso) + if (isNonIso8601) { date = TemporalHelpers.CalendarDateToISO(_realm, zdt.Calendar, year, month, day, overflow, monthCode); }