-
Notifications
You must be signed in to change notification settings - Fork 156
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
Editorial: Use ISO Date, ISO Date-Time, and Time Records in more places #3003
Conversation
We test for the Time Record being ~start-of-day~ here, but that cannot happen; InterpretTemporalDateTimeFields cannot return that value. Closes: #3000
ISOMonthCode was only used in CalendarISOToDate and in an assertion which didn't really need to be there. See: #2948
Instead of passing each component individually. See: #2949
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3003 +/- ##
==========================================
- Coverage 96.36% 95.88% -0.49%
==========================================
Files 21 21
Lines 10491 9605 -886
Branches 1821 1793 -28
==========================================
- Hits 10110 9210 -900
- Misses 323 342 +19
+ Partials 58 53 -5 ☔ View full report in Codecov by Sentry. |
In CompareISODate, use two ISO Date Records instead of passing each component individually. In ISODateSurpasses, the first date does not have to exist, so rewrite the operation without using CompareISODate and only take an ISO Date Record for the second parameter. See: #2949
Instead of passing each component individually. See: #2949
20e0b3d
to
af41492
Compare
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 love this PR! Thanks so much for building this. Makes things much easier to read. I was able to get through only the polyfill changes before I ran out of time, so I'll leave the spec review parts for others.
My comments were mainly suggesting other AOs that might want to take records.
@@ -1545,9 +1413,9 @@ export function InterpretISODateTimeOffset( | |||
if (offsetBehaviour === 'exact' || offsetOpt === 'use') { | |||
// Calculate the instant for the input's date/time and offset | |||
const balanced = BalanceISODateTime( |
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.
Why does BalanceISODateTime
take individual units instead of a date and time record?
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.
There are a number of operations such as BalanceISODateTime, IsValidISODate, RejectTime, etc. that I left alone, that still take individual components. The reason is that these operations can accept unconstrained values. (For example, BalanceISODateTime(1970, 1, 1, 0, -60, 0, 0, 0, 0) when we are subtracting the offsetMinutes from the +01:00
time zone.) Currently, it's an invariant that the ISO Date/Date-Time/Time Records can only contain valid constrained values.
An alternative would be to allow the records to contain unconstrained values, then these AOs could also take records. But on the downside, you then have to have checks or assertions everywhere you use them. On balance I found it better to have the invariant, and write out the components when the values may be unconstrained.
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.
Ahh, makes sense. I should have known there was a good reason!
ValidateEpochNanoseconds(epochNs); | ||
return epochNs; | ||
} | ||
|
||
if (MathAbs(ISODateToEpochDays(year, month - 1, day)) > 1e8) { | ||
if (MathAbs(ISODateToEpochDays(isoDate.year, isoDate.month - 1, isoDate.day)) > 1e8) { |
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.
ISODateToEpochDays
is referenced 11 times, and 9 of those are deconstructing an existing ISO date object into three arguments. And in all 11 cases, the month is decremented by 1 before calling the function.
Maybe refactor this AO to accept a single date
argument, and that handles the decrement internally?
Also, out of curiosity, why is the decrement needed?
Also, the > 1e8
validation is repeated 4 times. Should it be abstracted to a new AO, e.g. ValidateISODateRange(date)
?
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.
ISODateToEpochDays is supposed to be a 1-to-1 replacement for ECMA-262 MakeDay but using mathematical values, because some of the ECMA-262 operations related to MakeDay don't correctly handle the edges of the time value range. There is an issue open for ECMA-262 to fix this, and when it's fixed the intention is to go back to using MakeDay here.
MakeDay and the other existing ECMA-262 ops use 0-based month numbers, so ISODateToEpochDays does as well. That's why we have the decrement. We can maybe improve this down the line when Temporal reaches stage 4.
About the "abs(ISODateToEpochDays(year, month - 1, day)) > 1e8" pattern, it's there because of #2729 and #2985. I like the idea of abstracting it, especially because it can disappear when after that ECMA-262 issue is fixed.
millisecond, | ||
microsecond, | ||
nanosecond | ||
isoDateTime.isoDate.year, |
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.
Out of curiosity why wasn't this refactored to something like this?
BalanceISODateTime({
...isoDateTime,
minute: isoDateTime.time.minute - offsetMinutes
})
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 is a great suggestion. I just didn't think of it. Will do as part of #3005.
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.
Never mind, running on little sleep and didn't see the curly brackets 😄 In that case, same as my earlier comment, I think BalanceISODateTime should continue to take separate components.
fields.nanosecond = GetSlot(this, ISO_NANOSECOND); | ||
let fields = ES.ISODateToFields(calendar, GetSlot(this, ISO_DATE_TIME).isoDate); | ||
const isoDateTime = GetSlot(this, ISO_DATE_TIME); | ||
fields.hour = isoDateTime.time.hour; |
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.
Should this befields = { ...fields, ...isoDateTime.time }
?
fields.microsecond = isoDateTime.microsecond; | ||
fields.nanosecond = isoDateTime.nanosecond; | ||
let fields = ES.ISODateToFields(calendar, isoDateTime.isoDate); | ||
fields.hour = isoDateTime.time.hour; |
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.
Should this be
fields = { ...fields, ...isoDateTime.time }
?
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! This PR is already large enough that I'm going to defer @justingrant's suggestions to another one and only fix the typos here.
return ToTemporalTime(item); | ||
export function ToTimeRecordOrMidnight(item) { | ||
if (item === undefined) return MidnightTimeRecord(); | ||
return GetSlot(ToTemporalTime(item), TIME); |
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.
Future work: it seems like ToTemporalTime
could also return a record
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 did look at doing that, but I think I still prefer what it currently does; it's analogous with ToTemporalDate, ToTemporalZonedDateTime, etc. In this one case it'd be better to return a record, but I'm not sure if it'd be better elsewhere.
Instead of passing each component individually, pass a Time Record. The name is no longer really appropriate, since we are not passing a Temporal object, so rename it to TimeRecordToString. See: #2949 Co-authored-by: Ms2ger <[email protected]>
Instead of passing each component individually, pass Time Records. The name is no longer really appropriate, since we are not passing a Temporal object, so rename it to CompareTimeRecord. See: #2949
Instead of passing each component individually. See: #2949
Instead of passing each component individually. See: #2949
Instead of passing each component individually. See: #2949
…rename Instead of passing each component individually, pass an ISO Date-Time Record. The name is no longer really appropriate, since we are not passing a Temporal object, so rename it to ISODateTimeToString. See: #2949
…ecord Instead of individual fields, give ISO Date-Time Record just two fields, [[ISODate]] and [[Time]]. Since we are passing ISO Date and Time Records in more places, this is better for readability. See: #2949
Instead of passing each component individually. See: #2949
Instead of passing each component individually. See: #2949
…nding Instead of passing each component individually. See: #2949
Instead of passing each component individually. See: #2949
Instead of passing each component individually. See: #2949
Instead of passing each component individually. The [[Day]] field is ignored. See: #2949
Instead of passing each component individually. GetUTCEpochNanoseconds is part of ECMA-262 already, so this requires some <ins> and <del> tags. See: #2949
Instead of passing each component individually. GetNamedTimeZoneEpochNanoseconds is part of ECMA-262 already, so this requires some <ins> and <del> tags. See: #2949
These are repeated several times now throughout the spec, so abstract them out for readability. See: #2949
For PlainDate, PlainYearMonth, and PlainMonthDay, use an ISO Date Record in the [[ISODate]] internal slot. For PlainDateTime, use an ISO Date-Time Record in the [[ISODateTime]] internal slot. For PlainTime, use a Time Record in the [[Time]] internal slot. This makes a lot more concise spec text, and probably corresponds better with how it will be implemented. See: #2949 Co-authored-by: Justin Grant <[email protected]> Co-authored-by: Ms2ger <[email protected]>
Instead of writing out all the field names, add an abstract operation that also encapsulates the IsValidTime assertion. See: #2949 Co-authored-by: Ms2ger <[email protected]>
This is the only place it's called from, and it consolidates operations that take individual time components. See: #2949
This avoids creation of an unobservable Temporal.PlainDateTime object, and is also simpler to read. See: #2949
This fits with the general theme of returning Records instead of Objects, but also these operations were each only called from one place. So inline them. See: #2949
Now that it's trivial to get an ISO Date Record for a Temporal object, we don't really need this operation. All the switching on Temporal object type that it does, is already known at each call site.
This avoids creation of an unobservable Temporal.PlainTime object, and is also simpler to read. Since it no longer returns a Temporal object, give it a more appropriate name.
Instead of passing each component individually. Also, remove the "To" from the name, as it's not a conversion operation. See: #2949
e5ab7a2
to
3490b8f
Compare
This has been on the to-do list since removing user-visible calls from calendars and time zones. It's a huge improvement in conciseness and therefore readability and comprehensibility; and none of it is observable.
Highlights:
Could be reviewed commit by commit, but it's probably just as easy to review in one pass.
Closes: #2949, #3000