-
Notifications
You must be signed in to change notification settings - Fork 158
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
Initial implementation of ZonedDateTime #1073
Conversation
Time.toZonedDateTime was missing the options argument. Calendar.dateUntil had the wrong variable names.
…tion Since the allowed values for roundingIncrement are identical in DateTime.round() and ZonedDateTime.round(), the tedious list of ifs can go into one operation.
Make the calendar argument in DateTime align with the other types, and use the new conversion symbols for Instant.epochSeconds and friends.
Codecov Report
@@ Coverage Diff @@
## main #1073 +/- ##
==========================================
- Coverage 94.88% 93.59% -1.29%
==========================================
Files 18 19 +1
Lines 7134 7702 +568
Branches 1137 1224 +87
==========================================
+ Hits 6769 7209 +440
- Misses 358 486 +128
Partials 7 7
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
The only discrepancy so far in the implemented parts with the TypeScript implementation that I'm aware of is |
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 amazing! I started reviewing tonight, will finish tomorrow. Looks good so far.
Yep, I put that in for better compatibility with Java's parser which treats Regardless of what we do with ZDT, I don't think we should be accepting Z strings in TimeZone.from. #1075 Here's context around the Java thing:
|
This is so cool. Amazing progress. I started reviewing tonight, will plan to finish tomorrow. |
Thanks for the reviews, both!
OK, I'll address this in the next PR (which will also enable |
01af565
to
bd12c6e
Compare
This contains all the "easy" ZonedDateTime methods, and their tests, as well as all the methods to convert from other types to ZonedDateTime and their tests and specification. See: #569
bd12c6e
to
1cb5160
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.
This looks great. I tried to give this (esp. the tests and the polyfill implementation) a close review. Found a few issues but overall LGTM. Thanks!
@@ -16,6 +16,7 @@ import { | |||
NANOSECOND, | |||
DATE_BRAND, | |||
CALENDAR, | |||
EPOCHNANOSECONDS, |
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.
Minor question: why is it TIME_ZONE but EPOCHNANOSECONDS? Is there some meaning intended by not splitting EPOCH and NANOSECONDS with an underscore?
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's pretty arbitrary. Splitting with an underscore seems proper, but I didn't want to rename all of the existing EPOCHNANOSECONDS names used in Instant.
if (ES.Type(item) === 'Object') { | ||
if (ES.IsTemporalZonedDateTime(item)) return item; | ||
calendar = item.calendar; | ||
if (calendar === undefined) calendar = new (GetIntrinsic('%Temporal.ISO8601Calendar%'))(); |
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 to confirm: Temporal.ISO8601Calendar
is internal-facing only, right? Regular users won't see it when they type Temporal.
in browser dev tools? If so, that's great. I assume that this is currently the plan per the Sept 18 meeting discussion of #292, but then seeing the code above I wasn't 100% sure.
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.
To be decided in #847
offset, | ||
calendar | ||
} = ES.ParseTemporalZonedDateTimeString(ES.ToString(item))); | ||
if (!ianaName) throw new RangeError('named time zone required'); |
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 error message is a little misleading in the case where there's an offset in brackets. How about 'time zone ID required in [brackets]'
or something like that.
ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); | ||
let roundingMode = ES.ToTemporalRoundingMode(options, 'nearest'); | ||
roundingMode = ES.NegateTemporalRoundingMode(roundingMode); | ||
const maximumIncrements = { |
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.
move this to module scope to avoid repeating it 2x?
const tz = GetSlot(this, TIME_ZONE); | ||
return { | ||
calendar: GetSlot(this, CALENDAR), | ||
hour: GetSlot(dt, 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.
will ZDT or time calendars land first? If the latter, then should these be isoHour, isoMinute, etc?
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.
Apparently ZDT :)
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.
Cool, @ptomato wins the game of PR chicken! ;-)
it('casts its argument', () => { | ||
equal(`${zdt.withCalendar('japanese')}`, '2019-11-18T15:23:30.123456789-08:00[America/Los_Angeles][c=japanese]'); | ||
}); | ||
it('keeps instant and calendar the same', () => { |
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.
You mean time zone, not calendar in the title of this test.
}) | ||
); | ||
}); | ||
it('at least the required properties must be present', () => { |
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 there be another test that verifies that the minimal required properties can successfully be parsed? For example, I'd assume that the calendar property is optional in this context. Would be good to have a test to verify that.
Might also make sense to verify that the minimum set really is the minimum set. For example, try deleting each property from the bag one at a time and verify that it throws every time.
@@ -181,8 +181,8 @@ <h1>get Temporal.Instant.prototype.epochSeconds</h1> | |||
1. Let _instant_ be the *this* value. | |||
1. Perform ? RequireInternalSlot(_instant_, [[InitializedTemporalInstant]]). | |||
1. Let _ns_ be _instant_.[[Nanoseconds]]. | |||
1. Let _s_ be RoundTowardsZero(_ns_ / 1,000,000,000<sub>ℝ</sub>). | |||
1. Return the Number value for _s_. | |||
1. Let _s_ be RoundTowardsZero(ℝ(_ns_) / 1,000,000,000<sub>ℝ</sub>). |
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 noticed these symbols "𝔽" and ℝ in the spec when I was working on the PR for lessThan/greaterThan/etc. What do they mean? Also, what does the "?" (or, more rarely, "!") mean before a function call?
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.
readonly daysInYear: number; | ||
readonly monthsInYear: number; | ||
readonly inLeapYear: boolean; | ||
readonly startOfDay: Temporal.ZonedDateTime; |
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, crap. This may run afoul of the guidelines we've been using that object-valued property getters must return the same (===
) object every time. That'd imply a slot to cache this object. Probably not worth the hassle and potential runtime performance impact to keep it a property. Should we change this to a startOfDay()
method?
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.
Good catch, this is better off as a method.
} | ||
toLocaleString(locales = undefined, options = undefined) { | ||
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver'); | ||
return new DateTimeFormat(locales, options).format(this); |
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.
All the toLocaleString logic happens over in Intl? I'm particularly wondering about the case where the time zone of the receiver conflicts with the time zone in the options. #700 forces the instance's time zone into the options bag and throws an exception if the user put a timezone in the options that's different from the instance's time zone. I think (not 100% sure) matches how calendar conflicts are handled which seems like a reasonable behavior to match.
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.
Yeah, the check is in PartitionDateTimePattern; we wouldn't want toLocaleString
to be inconsistent with manually using DateTimeFormat
.
Haha @Ms2ger I keep losing races with you to finish reviewing PRs before they're merged! Next time I'll ping you if I'm in the middle of a long late-night (for me) review. No worries in this PR though, it looks solid and the issues I found can all be addressed in later PRs. |
I will be addressing all these remarks in my next pull request. |
This includes all the "trivial" methods of ZonedDateTime and their tests (spec text already exists), and all the conversion methods from other types to ZonedDateTime, and their tests and specification text.
Unimplemented methods (with, add, subtract, until, since, round, getFields, and anything dealing with DST) throw, for the time being. Some options (offset and overflow in from()) are also not implemented yet.