-
-
Notifications
You must be signed in to change notification settings - Fork 405
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
First portion of the Temporal implementation #3277
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #3277 +/- ##
==========================================
- Coverage 49.56% 47.09% -2.47%
==========================================
Files 446 474 +28
Lines 43716 48737 +5021
==========================================
+ Hits 21666 22955 +1289
- Misses 22050 25782 +3732
☔ View full report in Codecov by Sentry. |
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.
Just a preliminary review before checking the rest of the changes, but really nice work implementing all of this!
Congratulations! Temporal has a huge surface area, and seeing so much completed already in this PR is really nice to see. I'm one of the Temporal proposal's champions. We're eager to help implementers be successful. If you have any questions about the spec, or if there's anything we can help with, then feel free to drop an issue in the @tc39/proposal-temporal repo and we'll respond quickly. Thanks again! Are you collaborating with the folks building ICU4X? (the Rust port of ICU4C the library that powers the calendar and time zone features of V8, JSC, and SpiderMonkey) |
Thanks! Although, I think there is still more work to be done.
Not currently, and that's actually probably one of the more debatable aspects/areas to maybe improve on the current progress in this PR. Initially, my intended approach with the |
I think this is okay. ICU4X will provide a parser in the future, and if they keep doing Temporal compatibility work, we'll eventually be able to replace all types with ICU4X's. So it is good to separate that to avoid integrating it too tightly to the JS parser. |
It is very much in the design of ICU4X to see the calendar-specific algorithms, including the ones for the ISO calendar, and flags like "overflow" and "constrain", land in ICU4X has support for all CLDR calendars, with a focus on conversion to/from ISO-8601. There is a partial implementation of date arithmetic (add/subtract/since/until) but it needs some polishing from someone who understands the Temporal spec. I'd be very happy to mentor in this area, so that Boa can just pull in the ICU4X code for this. |
That's great to hear! I actually thought of something that might work earlier when I was writing my reply to switch the ISO calendar implementation to |
The ICU4X issue for providing public methods for these operations |
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.
Just a batch of reviews that I wanted to put out to be able to merge them. Mostly doc fixes.
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.
Just wanted to note some things I've noticed while going through this myself and plan to address 😄.
99aafaa
to
88f6cee
Compare
Test262 conformance changes
|
3f0b666
to
225eb4a
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.
Did a review pass on the parser code, and I have some suggestions.
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 looks really, good, still a fair few bits to iron out but its a great starting point.
I'm happy for this to go in (bar any comments made)
boa_ast/src/temporal/mod.rs
Outdated
/// A full precision `UtcOffset` | ||
#[derive(Debug, Clone, Copy)] | ||
#[allow(dead_code)] | ||
pub struct UtcOffset { |
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 think you can capitalize UTC to match up with the specification: https://tc39.es/proposal-temporal/#prod-UTCOffsetMinutePrecision
@@ -7,7 +7,7 @@ | |||
//! [spec]: https://tc39.es/ecma262/#sec-date-objects | |||
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date | |||
|
|||
mod utils; | |||
pub(crate) mod utils; |
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 was the visibility of utils changed? Whats now using it?
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.
14.8.1 GetUTCEpochNanoseconds
uses the preexisting MakeDay
and MakeTime
.
} | ||
} | ||
|
||
fn month_code_to_integer(mc: &JsString) -> JsResult<i32> { |
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 i32
? wouldn't a u8
fit this? I don't think theres any negative months and shouldn't be any higher than 13
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.
Ah I guess this is returning a JS integer https://tc39.es/ecma262/#sec-tointegerorinfinity, I wonder if this is ever observable?
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.
In the long run, month-day fields should probably be eventually optimized down to a u8
according to any overflow option, but I think that's more so on IsoDateRecord
. TemporalFields
is meant to be more of a Rust native representation of the fields
object, so the fields mostly align with the conversion value.
} | ||
|
||
/// Resolve the month and monthCode on this `TemporalFields`. | ||
pub(crate) fn resolve_month(&mut self) -> JsResult<()> { |
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 ISO_resolve_month? https://tc39.es/proposal-temporal/#sec-temporal-isoresolvemonth
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.
Another batch of reviews. I finished reviewing the parser changes and everything looks great!
Millisecond, | ||
Microsecond, | ||
Nanosecond, | ||
Auto, |
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 think it would be preferable to have Auto
be a variant of a new generic enum instead of part of the temporal unit. It's just a transitional variant anyways.
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'll take a look at it again. From what I remember, I wasn't initially wanting to add Auto
either, but had ran into something that felt like it needed it. But it could probably also be refactored out.
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 was looking at the spec again, and Auto
is a valid option value that can be provided for Duration
s round method options, so I'm not entirely sure that it can be totally separated from the enum.
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 think it can, with something like:
enum OptionOrAuto<T> {
Some(T),
Auto
}
impl<T: FromStr> FromStr for OptionOrAuto<T> {
type Err = Error;
fn parse(value: &str) -> Result<Self, Self::Err> {
if value == "auto" {
return Ok(Self::Auto);
}
Ok(Self::Some(<T>::parse(value)?))
}
}
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 that definition, we could add something like OptionOrAuto::resolve(t:T)
to resolve Auto
to a specific value of T
if needed.
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.
Auto
is technically considered a valid extra value in 3 circumstances in the proposal, Duration.prototype.round
, Calendar.prototype.dateUntil
, and GetDifferenceSettings
. dateUntil
does provide a T (Day). But the method that throws a wrench is round
where Auto
is used to designate the use of the computed defaultLargestUnit
value where largestUnitPresent
remains true
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.
We would potentially be returning
Option<OptionOrAuto<T>>
whereNone
,Auto
, orT
may all be a valid value.
Maybe it looks that way thanks to the repetition of Option
. Something like Option<AutoValue<T>>
?
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.
We faced similar challenges in defining the TypeScript types for the Temporal API. In case it's helpful, here's the TS types we ended up with. Each of these types defines valid inputs for units of various Temporal methods.
export type LargestUnit<T extends DateTimeUnit> = 'auto' | T | PluralUnit<T>;
export type SmallestUnit<T extends DateTimeUnit> = T | PluralUnit<T>;
export type TotalUnit<T extends DateTimeUnit> = T | PluralUnit<T>;
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.
BTW, full set of TS types are here: https://github.com/tc39/proposal-temporal/blob/main/polyfill/index.d.ts
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.
@justingrant I've been using that polyfill as a reference! It was super useful for setting up the options initially / finally visualizing all the options 😄 (it was also linked above too). The current options were more or less pulled from that, although I tried to simplify AssignmentOptions
and ArithmeticOptions
to ArithmeticOverflow
(might not be the best name).
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.
Impressive work! I just had the chance to review part of it and add some comments.
@@ -13,6 +13,7 @@ rust-version.workspace = true | |||
[features] | |||
serde = ["dep:serde", "boa_interner/serde", "bitflags/serde"] | |||
arbitrary = ["dep:arbitrary", "boa_interner/arbitrary", "num-bigint/arbitrary"] | |||
experimental = [] |
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.
In other cases, we have used more specific feature names (such as intl
). Should we just name the feature temporal
?
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.
We opted for this approach because temporal
would be removed anyways when it reaches stage 4. Do other engines use a broad experimental
flag for Temporal, or a specific temporal
flag?
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.
Makes sense... but going forward, we might want to have features specific to some JavaScript characteristics (maybe a feature per built-in, things like that), so it might not hurt to have a temporal
feature, and experimental
would just depend on temporal
. Could make it easier to support other experimental features in the future.
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.
That makes sense, since it allows users to reduce binary size if they need to.
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 the feature flag update be included in this PR or a different PR?
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.
Can be done separately :)
Calendar, Date, | ||
}; | ||
|
||
pub(crate) struct IsoCalendar; |
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.
In some cases we use the ISOWhatever
naming convention, and in other cases we use IsoWhatever
. We should probably decide on one of them.
I think that Rust favors IsoWhatever
.
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 think the ISOWhatever
naming convention is relatively newer and potentially isolated just to the AST nodes. The rust notation (IsoWhatever
) is probably better, so I'll switch them.
format!("{n:0min_length$}") | ||
} | ||
|
||
// TODO: 13.1 `IteratorToListOfType` |
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.
Is this still to-do? Seems pretty good :)
Might need some docs, though
Same for others in this file
1d6bf18
to
ddc20e0
Compare
mod iso; | ||
pub(crate) mod utils; | ||
|
||
#[cfg(feature = "experimental")] |
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.
Does this need an experimental
gate? The module is already gated behind the feature.
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.
Probably not.
The editor I use is vscode. And if I use the builtin run test functionality, it won't run the tests without the configuration flag on the test module itself.
match month { | ||
0 => day_in_year + 1, | ||
1 => day_in_year - 30, | ||
2 => day_in_year - 59 - in_leap_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.
2 => day_in_year - 59 - in_leap_year, | |
2 => day_in_year - 58 - in_leap_year, |
Also this could also be an array of offsets, then we subtract in_leap_year
if month >=2
I think this is my last batch of reviews. There may be some patterns that can be improved, but I think we can iterate instead of trying to perfect this PR. Just resolve my previous comments and we can merge this :) |
Linking #1804. Would it be worth while editing the original issue to make it more of a tracking issue in order to keep track of who's working on what. I plan to continue working on this, but just in case anyone else was interested in working on a part of the API, it could be useful. Or would it be better to start a new issue altogether for tracking. |
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 cannot imagine the massive work that was put onto understanding, designing, iterating and developing 13k lines of code for such an important feature. Really impressive work!
We can rename that issue and do a checklist with the missing APIs. You're probably the best person to list what's still missing, so I'll leave that task to you. |
* Started with the Temporal implementation * Implemented some useful functions * Updaating some spec references * Initial work on TimeZone and Instant * More work completed on Temporal.Duration and Temporal.Instant * General scaffolding and heavy work on Instant and Duration complete * ZonedDateTime and Calendar started with further work on duration abstract ops * Further work on temporal work and clippy fixes * Post rebase fixes/reverts * Add BuiltinCalendar and begin IsoCalendar impl * More work completed on calendar/date/yearmonth/monthday * Calendar and iso impl close to completion - no datelike parsing * Initial work on temporal ISO8601 parsing and grammar * Post rebase fixes and updates * More on parser/Duration and work through clippy lints * Fix bug on peek_n and add temporal cfg * Fix clippy lints on parser tests * Build out calendar with icu_calendar, add some tests, and misc. * Fix spec hyperlinks * Parser clean up and invalid annotations * Add Duration and Temporal Parsing * Remove IsoYearMonthRecord * Post rebase update * Fix and add to ISO Parser docs * Parser/ast cleanup and duration refactor/additions * Review feedback, options update, and duration changes * Review changes, general cleanup, and post rebase fixes * Fix time zone parsing issue/test logic * Clean up parse output nodes * Apply review feedback and various fixes * Review feedback and get_option changes * Review feedback --------- Co-authored-by: Iban Eguia Moraza <[email protected]> Co-authored-by: José Julián Espina <[email protected]>
This PR is to propose the work completed so far on the
Temporal
branch for initial review and merging. The changes have become quite large (as visible via the diff 😅), but I think just about most of the building blocks are just about finished and/or close to being finished.It changes the following:
Temporal
built-ins to varying levelsCalendar
,Duration
,Instant
,PlainDate
,PlainDateTime
,PlainYearMonth
, andPlainMonthDay
saw the most attention.PlainTime
,TimeZone
, andZonedDateTime
still need a decent amount of work.There's still a decent amount more work to be done, but I think this is close to being a good starting point.
General design decisions that were made while working on this: the parser for iso8601 is currently separate from the
Lexer
andParser
for JavaScript, due to some grammar incompatibility that came up with the lexer. There is also aBuiltinCalendar
trait that's meant to provide anyone who wants to implement any custom calendar. There are a few internal structs that are implemented with the idea of trying to prevent passing around entireJsObjects
in favor of the native structs wherever possible. It also turned out that these structs were typically the target of the abstract operations too, so some abstract operations are implemented on the structs (I tried to leave a note in the abstract operations section when possible pointing to where the abstract operation is implemented).There's probably a lot more I could say, but I think I'm going to leave it at this. 😆 Let me know what you think!
There are a few more things that I'd like to have completed on this before merging (although it's more of a wish list rather than necessary):
Footnote: I didn't remove
Temporal
from thetest_ignore
due to how fragmented the current progress is and the potential for false positives at this stage, but if anyone thinks it should be removed I definitely can.Edit: Marking calendar complete but it's more of a "complete as can currently be for now".