-
Notifications
You must be signed in to change notification settings - Fork 164
Polyfill: fix a few non-ISO calendar issues #1977
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
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1977 +/- ##
=======================================
Coverage 94.96% 94.96%
=======================================
Files 19 19
Lines 10962 10967 +5
Branches 1742 1743 +1
=======================================
+ Hits 10410 10415 +5
Misses 535 535
Partials 17 17
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to land the cached built-in objects, let → const and unused parameter changes. I need to think more carefully or at least hear some more justification for the rest.
@@ -633,18 +633,20 @@ const nonIsoHelperBase = { | |||
adjustCalendarDate(calendarDate, cache, overflow /*, fromLegacyDate = false */) { | |||
if (this.calendarType === 'lunisolar') throw new RangeError('Override required for lunisolar calendars'); | |||
this.validateCalendarDate(calendarDate); | |||
const largestMonth = this.monthsInYear(calendarDate, cache); | |||
let { month, year, eraYear, monthCode } = calendarDate; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this isn't intentionally controlling the order of calling any getters on calendarDate
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. calendarDate
here is a record, not a Temporal object. The only access to Temporal instances is via slots. For the non-ISO calendar implementation, the only slot access happens in temporalToCalendarDate()
. So there shouldn't be any observable property access at all. The naming convention in calendar.mjs is this:
- "temporalDate" - temporal instance
- "isoDate" -
{ year: number; month: number; day: number }
record representing a date in the ISO calendar - "calendarDate" - record with properties representing the values of built-in calendar fields, with a shape that varies based on the method. For this method, the shape of the
calendarDate
param has this type over in temporal-polyfill:
{
era?: string | undefined;
eraYear?: number | undefined;
year?: number | undefined;;
month?: number | undefined;;
monthCode?: string | undefined;;
day?: number | undefined;;
monthExtra?: string | undefined; // used internally; never from external data
}
Does that address your concerns?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with a shape that varies based on the method
More specifically, all "calendarDate" objects have the shape above, but depending on the method and where the input comes from, some properties may be missing vs. present. For example, if the input to the method commented above is coming from user calls, then it will match the input of Calendar.dateFromFields
. If the input is internally generated, it might only be YMD fields. Other methods may only care about some subset of the fields. For example, the inLeapYear
methods of these *Helper
objects only care about the year
property and ignores the rest.
The TS polyfill (especially in js-temporal/temporal-polyfill#109 makes this all a lot clearer because everything is now typed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to land the cached built-in objects, let → const and unused parameter changes. I need to think more carefully or at least hear some more justification for the rest.
Sounds good. I replied to your comments above and added comments to explain the purpose of anything other than "cached built-in objects, let → const and unused parameter changes".
@@ -1146,10 +1148,10 @@ const helperHebrew = ObjectAssign({}, nonIsoHelperBase, { | |||
} else { | |||
if (overflow === 'reject') { | |||
ES.RejectToRange(month, 1, this.monthsInYear({ year })); | |||
ES.RejectToRange(day, 1, this.maximumMonthLength(calendarDate)); | |||
ES.RejectToRange(day, 1, this.maximumMonthLength({ year, month })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a legit bug fix. { year, month }
might have been mutated since they were extracted from calendarDate
. I wasn't able to figure out an externally-visible repro case that this bug broke, but it's definitely fragile to changes elsewhere and seemed worth fixing here too.
This PR focuses on unblocking `strictNullChecks` and `strictPropertyInitialization` in ecmascript.ts and calendar.ts. It's intended as a complement to @jens-ox's work to remove `GetIntrinsic`. This PR: * Enables strictNullChecks: true and strictPropertyInitialization: true in tsconfig.json. * Improves calendar.ts TS types (this was most of the work in this PR) * Removes all unnecessary use of `any` throughout the polyfill * Updates types of ecmascript.ts and fixes a number of type bugs * Ports several proposal-temporal PRs: * tc39/proposal-temporal#1975 - Refactors to align ecmascript.mjs with spec text * tc39/proposal-temporal#1974 - Ensure that Temporal prototypes aren't writable * tc39/proposal-temporal#1976 - Throw RangeError if there's an invalid offset string in ZonedDateTime-representing property bags * tc39/proposal-temporal#1977 - Refactor and fix non-ISO calendars in polyfill * Fixes a few index.d.ts types * Refactors a few types in intl.ts * A handful of trivial type changes in other files (ecmascript.ts and calendar-ts were 95%+ of the work). I'd like to split out the runtime behavior changes (mostly the ported PRs above) into a smaller PR which should make it easier to review changes that can actually break things at runtime. I also need to port tests over from tc39/proposal-temporal#1976. Once those are split out, this PR should be ready to review.
@ptomato @Ms2ger Sorry to bug you guys but would love a review on this so I can unblock the next stage of getting js-temporal/temporal-polyfill#109 landed. (That PR depends on porting a bunch of proposal-temporal PRs to temporal-polyfill, and this PR here is the last one needed to be approved from the big batch this week.) |
@justingrant Unfortunately it'll be fairly difficult for me to get thoroughly into a pull request this large before leaving on end-of-year vacation. What I maybe can do, is try to split it up into different commits, and merge the things that Ms2ger already OK'd. Maybe that would unblock some of the changes that are waiting on this? It should also make the remaining code easier to review, as well. |
OK sounds good, I'll split it. |
I'm happy to do the splitting this afternoon, or tomorrow if I don't get to it today. |
Nah, I can do the splitting to save scarce @ptomato time for reviewing PRs like js-temporal/temporal-polyfill#111. 😄 |
Splitting commits and rubber-stamping backports is something that doesn't require me to concentrate, so it's all good 😄 — I opened #1983 but I'll leave the rest of the splitting to you in that case. |
`year` might have been mutated in the first line of this method. So it's not OK to pass `calendarDate` to `maximumMonthLength`. Need to pass current year/month values instead.
This adds an additional check to make sure that the (hard-coded) era definitions for each calendar aren't missing required `isoEpoch` properties. Note that this code is only run once at load time, not each time calendars are accessed, so there's no runtime impact.
Re-order code to guarantee that `calendarDate.year` is set before calculating the number of months in that year. The old code would break for any calendar that a) used a constant `era` like `islamic` and b) had a variable number of months in each year like `hebrew`. Today no ICU calendars fit in that Venn diagram, but if any are added in the future this code will break when initializing any calendar using an eraYear/era pair in a property bag. Better to fix it now!
dd05325
to
ba96615
Compare
@ptomato I split out the remaining edits into 4 separate commits with comments explaining why they're there. Thanks! |
This was originally part of tc39/proposal-temporal#1977 but I removed it because it wasn't high-priority for JS. But it's still needed in this repo so I'm adding it back. Why? TS complains when you assign a maybe-undefined value to a must-not-be-undefined variable. This small runtime change is cleaner than adding a lotta type casts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, much easier to digest now.
This was originally part of tc39/proposal-temporal#1977 but I removed it because it wasn't high-priority for JS. But it's still needed in this repo so I'm adding it back. Why? TS complains when you assign a maybe-undefined value to a must-not-be-undefined variable. This small runtime change is cleaner than adding a lotta type casts.
Fix a few non-ISO calendar issues:
This PR used to have a larger scope but @ptomato helpfully split some of the easier ones into #1983.