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

Editorial: Use ISO Date, ISO Date-Time, and Time Records in more places #3003

Merged
merged 30 commits into from
Oct 8, 2024

Conversation

ptomato
Copy link
Collaborator

@ptomato ptomato commented Oct 8, 2024

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:

  • ISO Date-Time Record now composed of [[ISODate]] (ISO Date Record) and [[Time]] (Time Record) fields
  • Abstract operations that previously took a series of date/time components (year, month, day, hour, etc.) now take one of the three Records
  • Temporal.PlainDate, PlainDateTime, PlainYearMonth, PlainMonthDay, and PlainTime's internal slots that each stored a date or time component ([[Year]], [[Month]], etc.) are now collapsed into one; they store one of the three Records

Could be reviewed commit by commit, but it's probably just as easy to review in one pass.

Closes: #2949, #3000

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
Copy link

codecov bot commented Oct 8, 2024

Codecov Report

Attention: Patch coverage is 97.23577% with 17 lines in your changes missing coverage. Please review.

Project coverage is 95.88%. Comparing base (b0e9b3a) to head (3490b8f).
Report is 30 commits behind head on main.

Files with missing lines Patch % Lines
polyfill/lib/ecmascript.mjs 98.05% 6 Missing and 1 partial ⚠️
polyfill/lib/plaintime.mjs 75.00% 1 Missing and 5 partials ⚠️
polyfill/lib/calendar.mjs 75.00% 4 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

@ptomato ptomato mentioned this pull request Oct 8, 2024
27 tasks
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
@ptomato ptomato force-pushed the 2949-iso-date-time-records branch from 20e0b3d to af41492 Compare October 8, 2024 01:39
Copy link
Collaborator

@justingrant justingrant left a 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(
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator

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) {
Copy link
Collaborator

@justingrant justingrant Oct 8, 2024

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) ?

Copy link
Collaborator Author

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,
Copy link
Collaborator

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
})

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

polyfill/lib/ecmascript.mjs Outdated Show resolved Hide resolved
polyfill/lib/ecmascript.mjs Outdated Show resolved Hide resolved
polyfill/lib/ecmascript.mjs Show resolved Hide resolved
polyfill/lib/plaindatetime.mjs Show resolved Hide resolved
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;
Copy link
Collaborator

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 } ?

polyfill/lib/plaintime.mjs Show resolved Hide resolved
fields.microsecond = isoDateTime.microsecond;
fields.nanosecond = isoDateTime.nanosecond;
let fields = ES.ISODateToFields(calendar, isoDateTime.isoDate);
fields.hour = isoDateTime.time.hour;
Copy link
Collaborator

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 } ?

Copy link
Collaborator

@Ms2ger Ms2ger left a 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.

polyfill/lib/plaintime.mjs Outdated Show resolved Hide resolved
spec/plaintime.html Outdated Show resolved Hide resolved
return ToTemporalTime(item);
export function ToTimeRecordOrMidnight(item) {
if (item === undefined) return MidnightTimeRecord();
return GetSlot(ToTemporalTime(item), TIME);
Copy link
Collaborator

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

Copy link
Collaborator Author

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.

ptomato and others added 17 commits October 8, 2024 10:47
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]>
ptomato and others added 8 commits October 8, 2024 10:49
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
@Ms2ger Ms2ger force-pushed the 2949-iso-date-time-records branch from e5ab7a2 to 3490b8f Compare October 8, 2024 08:50
@Ms2ger Ms2ger merged commit cb2e0ff into main Oct 8, 2024
6 checks passed
@Ms2ger Ms2ger deleted the 2949-iso-date-time-records branch October 8, 2024 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Editorial: Use ISO Date and Date-Time Records in more places
3 participants