Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CalendarDateToISO: Constrain behaviour for regnal years? #2865

Open
anba opened this issue May 27, 2024 · 8 comments · Fixed by #2986
Open

CalendarDateToISO: Constrain behaviour for regnal years? #2865

anba opened this issue May 27, 2024 · 8 comments · Fixed by #2986
Assignees
Labels
Milestone

Comments

@anba
Copy link
Contributor

anba commented May 27, 2024

It'd be nice to have some more detailed information how constraining should work for regnal years in the Japanese calendar.

  1. Dates before the start of the era (from SpiderMonkey WIP implementation):
js> Temporal.PlainDate.from({calendar: "japanese", era: "reiwa", eraYear: 1, month: 1, day: 1}).toString()
"2019-05-01[u-ca=japanese]"
js> Temporal.PlainDate.from({calendar: "japanese", era: "reiwa", eraYear: 1, month: 1, day: 1}).era
"reiwa"
js> Temporal.PlainDate.from({calendar: "japanese", era: "reiwa", eraYear: 1, month: 1, day: 1}, {overflow: "reject"}).toString()
typein:1:20 RangeError: calendar field "eraYear" is too large: 1

Whereas the spec polyfill changes to the previous era (for both constrain and reject overflow behaviour):

js> Temporal.PlainDate.from({calendar: "japanese", era: "reiwa", eraYear: 1, month: 1, day: 1}).toString()
"2019-01-01[u-ca=japanese]"
js> Temporal.PlainDate.from({calendar: "japanese", era: "reiwa", eraYear: 1, month: 1, day: 1}).era       
"heisei"
  1. Dates after the end of the era (from SpiderMonkey WIP implementation):
js> Temporal.PlainDate.from({calendar: "japanese", era: "heisei", eraYear: 100, month: 1, day: 1}).toString()
"2019-04-30[u-ca=japanese]"
js> Temporal.PlainDate.from({calendar: "japanese", era: "heisei", eraYear: 100, month: 1, day: 1}).era
"heisei"
js> Temporal.PlainDate.from({calendar: "japanese", era: "heisei", eraYear: 100, month: 1, day: 1}, {overflow: "reject"}).toString()
typein:1:20 RangeError: calendar field "eraYear" is too large: 100

Spec polyfill has (for both constrain and reject overflow behaviour):

js> Temporal.PlainDate.from({calendar: "japanese", era: "heisei", eraYear: 100, month: 1, day: 1}).toString()
"2088-01-01[u-ca=japanese]"
js> Temporal.PlainDate.from({calendar: "japanese", era: "heisei", eraYear: 100, month: 1, day: 1}).era       
"reiwa"

Questions I've asked myself:

  • Does overflow behaviour affect era/eraYear?
  • If yes, does constrain overflow constrain to the start/end of the era?
  • If yes, does reject overflow reject dates before/after the era?
anba added a commit to anba/test262 that referenced this issue May 30, 2024
See also <tc39/proposal-temporal#2865>
requesting for more information how this case should actually be
handled.
@justingrant
Copy link
Collaborator

Meeting 2024-05-30:

  • For constrain we'll treat each era "proleptically": dates before the era starts are mapped to the previous era (without changing the ISO date due to constraining), and dates after the era ends are mapped to later era (again without changing ISO date).
  • For reject, throw if it's before or after the era boundaries.

@gibson042 to make a PR. Because current behavior is unspecified, this will not be a normative change.

@anba
Copy link
Contributor Author

anba commented Jun 19, 2024

I've just found tc39/ecma402#540 which actually wants a different behaviour for reject, namely that reject doesn't throw for dates after the end of the era. I don't know if this should imply that reject should also not throw for dates before the start of the era, though.

ptomato pushed a commit to anba/test262 that referenced this issue Jul 25, 2024
See also <tc39/proposal-temporal#2865>
requesting for more information how this case should actually be
handled.
ptomato pushed a commit to anba/test262 that referenced this issue Jul 25, 2024
See also <tc39/proposal-temporal#2865>
requesting for more information how this case should actually be
handled.
ptomato pushed a commit to tc39/test262 that referenced this issue Jul 25, 2024
See also <tc39/proposal-temporal#2865>
requesting for more information how this case should actually be
handled.
@ptomato
Copy link
Collaborator

ptomato commented Sep 9, 2024

@gibson042 Will you have time to work on this in the next while? If not, I can take it over.

@ptomato ptomato added this to the Stage "3.5" milestone Sep 9, 2024
@gibson042
Copy link
Collaborator

I think this should extend #2940, but don't plan to start it until after that lands (or is at least in a final form).

@gibson042
Copy link
Collaborator

As I went to start on this, I realized that I'm not certain where it landed. To clarify, I think there are three dimensions to consider:

  • overflow: Developer choice of "constrain" vs. "reject" might affect behavior (keeping in mind that it applies not just to from and with, but also to add/subtract arithmetic).
  • stability: Many calendars have stable eras that are not expected to change (e.g., Gregorian, Coptic, Ethiopic, Minguo/ROC), while others (particularly regnal calendars such as Japanese) frequently add new eras and occasionally add past eras.
  • directionality: A year later than the end of an era may be differentiated from a year earlier than the beginning. And as an added wrinkle, if they are so differentiated, backwards-counting inverse eras might logically classify "year value less than the chronologically latest year of the era" as either (e.g., the polyfill currently interprets { calendar: "gregory", era: "ce", eraYear: 0, month: 1, day: 1 } as 0000-01-01 [1 BCE] but rejects { calendar: "gregory", era: "bce", eraYear: 0, month: 1, day: 1 } with a "Years in bce era must be positive" RangeError rather than interpreting it as 0001-01-01 [1 CE]).

tc39/ecma402#540 suggests that overflow should be irrelevant for unstable/regnal calendars (specifically, that out-of-bounds era/eraYear combinations are always mapped into the appropriate era), and presumably for stable calendars as well (in which case directionality would also become irrelevant). That makes sense to me, but it does mean that e.g. Temporal.PlainDate.from({ calendar: "gregory", era: "bce", eraYear: 0, month: 2, day: 1 }, { overflow: "reject" }) will return a date with a different era and eraYear rather than interpreting "reject" as justifying a RangeError (as it would with e.g. day: 30) and I'd want to update docs in addition to the spec guidance.

Does anyone want to argue for an approach that would sometimes throw Errors rather than remapping?

@ptomato
Copy link
Collaborator

ptomato commented Sep 28, 2024

I think you are right that remapping is the natural expected behaviour for eras. In other words, overflow: 'balance'. (Unlike for days that exceed the month, where we struggled to find use cases for balance.)

To take an example:

Temporal.PlainDate.from('2019-06-01').withCalendar('japanese').subtract({ years: 1 }, { overflow: 'reject' })

I cannot imagine any possible scenario in which it would be good that this throws. Likewise, for the same example with { overflow: 'constrain' }, I cannot imagine any possible scenario in which it would be good that this returns 2019-05-01 (i.e. constraining to the start date of the era) or 2019-06-01 (i.e., subtracting 1 year and then constraining the year to the first year of the era.)

@gibson042
Copy link
Collaborator

PR: #2986

@ptomato
Copy link
Collaborator

ptomato commented Oct 2, 2024

Reopening for test262 coverage.

@ptomato ptomato reopened this Oct 2, 2024
@ptomato ptomato added test262 and removed editorial labels Oct 2, 2024
@ptomato ptomato assigned ptomato and unassigned gibson042 Oct 2, 2024
@ptomato ptomato modified the milestones: Stage "3.5", Stage 4 Oct 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants