Skip to content

Commit

Permalink
Normative: Avoid reconverting Zoned- to PlainDateTime in AddZonedDate…
Browse files Browse the repository at this point in the history
…Time

There are several places where a ZonedDateTime is converted to a
PlainDateTime, which is a user-visible time zone operation, and then the
same operation is performed again right afterwards in AddZonedDateTime.

Avoid this by making AddZonedDateTime take an optional precalculated
PlainDateTime. If we have it, we can pass it in, and avoid the second
conversion.
  • Loading branch information
ptomato committed Sep 13, 2023
1 parent 392d86e commit 853a212
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 25 deletions.
27 changes: 22 additions & 5 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3338,7 +3338,7 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo) {
'day',
ObjectCreate(null)
);
let intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days, 0, 0, 0, 0, 0, 0);
let intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days, 0, 0, 0, 0, 0, 0, dtStart);
// may disambiguate

// If clock time after addition was in the middle of a skipped period, the
Expand All @@ -3353,7 +3353,22 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo) {
if (sign === 1) {
while (days.greater(0) && intermediateNs.greater(endNs)) {
days = days.prev();
intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days.toJSNumber(), 0, 0, 0, 0, 0, 0);
intermediateNs = AddZonedDateTime(
start,
timeZone,
calendar,
0,
0,
0,
days.toJSNumber(),
0,
0,
0,
0,
0,
0,
dtStart
);
// may do disambiguation
}
}
Expand Down Expand Up @@ -4253,7 +4268,7 @@ export function DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, largestUni
largestUnit,
options
);
let intermediateNs = AddZonedDateTime(start, timeZone, calendar, years, months, weeks, 0, 0, 0, 0, 0, 0, 0);
let intermediateNs = AddZonedDateTime(start, timeZone, calendar, years, months, weeks, 0, 0, 0, 0, 0, 0, 0, dtStart);
// may disambiguate
let timeRemainderNs = ns2.subtract(intermediateNs);
const intermediate = CreateTemporalZonedDateTime(intermediateNs, timeZone, calendar);
Expand Down Expand Up @@ -4947,7 +4962,8 @@ export function AddZonedDateTime(
ms,
µs,
ns,
options
precalculatedPlainDateTime = undefined,
options = undefined
) {
// If only time is to be added, then use Instant math. It's not OK to fall
// through to the date/time code below because compatible disambiguation in
Expand All @@ -4964,7 +4980,7 @@ export function AddZonedDateTime(

// RFC 5545 requires the date portion to be added in calendar days and the
// time portion to be added in exact time.
let dt = GetPlainDateTimeFor(timeZone, instant, calendar);
const dt = precalculatedPlainDateTime ?? GetPlainDateTimeFor(timeZone, instant, calendar);
const datePart = CreateTemporalDate(GetSlot(dt, ISO_YEAR), GetSlot(dt, ISO_MONTH), GetSlot(dt, ISO_DAY), calendar);
const dateDuration = new TemporalDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0);
const addedDate = CalendarDateAdd(calendar, datePart, dateDuration, options);
Expand Down Expand Up @@ -5170,6 +5186,7 @@ export function AddDurationToOrSubtractDurationFromZonedDateTime(operation, zone
sign * milliseconds,
sign * microseconds,
sign * nanoseconds,
undefined,
options
);
return CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar);
Expand Down
15 changes: 2 additions & 13 deletions polyfill/lib/zoneddatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -387,20 +387,9 @@ export class ZonedDateTime {
let nanosecond = GetSlot(dt, ISO_NANOSECOND);

const calendar = GetSlot(this, CALENDAR);
const dtStart = ES.CreateTemporalDateTime(
GetSlot(dt, ISO_YEAR),
GetSlot(dt, ISO_MONTH),
GetSlot(dt, ISO_DAY),
0,
0,
0,
0,
0,
0,
'iso8601'
);
const dtStart = ES.CreateTemporalDateTime(year, month, day, 0, 0, 0, 0, 0, 0, 'iso8601');
const instantStart = ES.GetInstantFor(timeZone, dtStart, 'compatible');
const endNs = ES.AddZonedDateTime(instantStart, timeZone, calendar, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
const endNs = ES.AddZonedDateTime(instantStart, timeZone, calendar, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, dtStart);
const dayLengthNs = endNs.subtract(GetSlot(instantStart, EPOCHNANOSECONDS));
if (dayLengthNs.leq(0)) {
throw new RangeError('cannot round a ZonedDateTime in a calendar with zero- or negative-length days');
Expand Down
19 changes: 12 additions & 7 deletions spec/zoneddatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ <h1>Temporal.ZonedDateTime.prototype.round ( _roundTo_ )</h1>
1. Let _dtStart_ be ? CreateTemporalDateTime(_temporalDateTime_.[[ISOYear]], _temporalDateTime_.[[ISOMonth]], _temporalDateTime_.[[ISODay]], 0, 0, 0, 0, 0, 0, *"iso8601"*).
1. Let _instantStart_ be ? GetInstantFor(_timeZone_, _dtStart_, *"compatible"*).
1. Let _startNs_ be _instantStart_.[[Nanoseconds]].
1. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _calendar_, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0).
1. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _calendar_, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, _dtStart_).
1. Let _dayLengthNs_ be ℝ(_endNs_ - _startNs_).
1. If _dayLengthNs_ &le; 0, then
1. Throw a *RangeError* exception.
Expand Down Expand Up @@ -1290,6 +1290,7 @@ <h1>
_milliseconds_: an integer,
_microseconds_: an integer,
_nanoseconds_: an integer,
optional _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*,
optional _options_: an Object,
): either a normal completion containing a BigInt or an abrupt completion
</h1>
Expand All @@ -1300,13 +1301,17 @@ <h1>
As specified in <a href="https://tools.ietf.org/html/rfc5545">RFC 5545</a>, the date portion of the duration is added in calendar days, and the time portion is added in exact time.
</dd>
</dl>
<p>Unless _precalculatedPlainDateTime_ is supplied, the given _timeZone_'s `getOffsetNanosecondsFor` method will be called to convert _epochNanoseconds_ to a wall-clock time.</p>
<emu-alg>
1. If _options_ is not present, set _options_ to *undefined*.
1. Assert: Type(_options_) is Object or Undefined.
1. If _years_ = 0, _months_ = 0, _weeks_ = 0, and _days_ = 0, then
1. Return ? AddInstant(_epochNanoseconds_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
1. Let _instant_ be ! CreateTemporalInstant(_epochNanoseconds_).
1. Let _temporalDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
1. If _precalculatedPlainDateTime_ is not *undefined*, then
1. Let _temporalDateTime_ be _precalculatedPlainDateTime_.
1. Else,
1. Let _instant_ be ! CreateTemporalInstant(_epochNanoseconds_).
1. Let _temporalDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _instant_, _calendar_).
1. Let _datePart_ be ! CreateTemporalDate(_temporalDateTime_.[[ISOYear]], _temporalDateTime_.[[ISOMonth]], _temporalDateTime_.[[ISODay]], _calendar_).
1. Let _dateDuration_ be ! CreateTemporalDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0).
1. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_).
Expand Down Expand Up @@ -1341,7 +1346,7 @@ <h1>
1. Let _endInstant_ be ! CreateTemporalInstant(_ns2_).
1. Let _endDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _endInstant_, _calendar_).
1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0, _startDateTime_).
1. Let _timeRemainderNs_ be _ns2_ - _intermediateNs_.
1. Let _intermediate_ be ! CreateTemporalZonedDateTime(_intermediateNs_, _timeZone_, _calendar_).
1. Let _result_ be ? NanosecondsToDays(ℝ(_timeRemainderNs_), _intermediate_).
Expand Down Expand Up @@ -1377,11 +1382,11 @@ <h1>
1. Let _endDateTime_ be ? GetPlainDateTimeFor(_zonedRelativeTo_.[[TimeZone]], _endInstant_, _zonedRelativeTo_.[[Calendar]]).
1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _zonedRelativeTo_.[[Calendar]], *"day"*, OrdinaryObjectCreate(*null*)).
1. Let _days_ be _dateDifference_.[[Days]].
1. Let _intermediateNs_ be ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0)).
1. Let _intermediateNs_ be ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0, _startDateTime_)).
1. If _sign_ is 1, then
1. Repeat, while _days_ &gt; 0 and _intermediateNs_ &gt; _endNs_,
1. Set _days_ to _days_ - 1.
1. Set _intermediateNs_ to ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0)).
1. Set _intermediateNs_ to ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0, _startDateTime_)).
1. Set _nanoseconds_ to _endNs_ - _intermediateNs_.
1. Let _done_ be *false*.
1. Let _dayLengthNs_ be ~unset~.
Expand Down Expand Up @@ -1465,7 +1470,7 @@ <h1>
1. Set _options_ to ? GetOptionsObject(_options_).
1. Let _timeZone_ be _zonedDateTime_.[[TimeZone]].
1. Let _calendar_ be _zonedDateTime_.[[Calendar]].
1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _sign_ &times; _duration_.[[Years]], _sign_ &times; _duration_.[[Months]], _sign_ &times; _duration_.[[Weeks]], _sign_ &times; _duration_.[[Days]], _sign_ &times; _duration_.[[Hours]], _sign_ &times; _duration_.[[Minutes]], _sign_ &times; _duration_.[[Seconds]], _sign_ &times; _duration_.[[Milliseconds]], _sign_ &times; _duration_.[[Microseconds]], _sign_ &times; _duration_.[[Nanoseconds]], _options_).
1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _sign_ &times; _duration_.[[Years]], _sign_ &times; _duration_.[[Months]], _sign_ &times; _duration_.[[Weeks]], _sign_ &times; _duration_.[[Days]], _sign_ &times; _duration_.[[Hours]], _sign_ &times; _duration_.[[Minutes]], _sign_ &times; _duration_.[[Seconds]], _sign_ &times; _duration_.[[Milliseconds]], _sign_ &times; _duration_.[[Microseconds]], _sign_ &times; _duration_.[[Nanoseconds]], *undefined*, _options_).
1. Return ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZone_, _calendar_).
</emu-alg>
</emu-clause>
Expand Down

0 comments on commit 853a212

Please sign in to comment.