Skip to content

Commit

Permalink
Separate out NanosecondsToDays from DifferenceZonedDateTime
Browse files Browse the repository at this point in the history
This new abstract operation will be used in more places when converting
nanoseconds to time-zone-aware days.

See: #1171
  • Loading branch information
ptomato committed Nov 13, 2020
1 parent a744300 commit 97d30d3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 61 deletions.
125 changes: 74 additions & 51 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,52 @@ export const ES = ObjectAssign({}, ES2020, {
microseconds = bigInt(microseconds).add(milliseconds.multiply(1000));
return bigInt(nanoseconds).add(microseconds.multiply(1000));
},
NanosecondsToDays: (nanoseconds, relativeTo) => {
const TemporalInstant = GetIntrinsic('%Temporal.Instant%');
const sign = MathSign(nanoseconds);
nanoseconds = bigInt(nanoseconds);
let dayLengthNs = 86400e9;
if (sign === 0) return { days: 0, nanoseconds: bigInt.zero };
if (!ES.IsTemporalZonedDateTime(relativeTo)) {
let days;
({ quotient: days, remainder: nanoseconds } = nanoseconds.divmod(dayLengthNs));
days = days.toJSNumber();
return { days, nanoseconds };
}
let isOverflow = false;
let days = 0;
let relativeInstant = GetSlot(relativeTo, INSTANT);
const timeZone = GetSlot(relativeTo, TIME_ZONE);
const calendar = GetSlot(relativeTo, CALENDAR);
do {
// calculate length of the next day (day that contains the time remainder)
const oneDayFartherNs = ES.AddZonedDateTime(
relativeInstant,
timeZone,
calendar,
0,
0,
0,
sign,
0,
0,
0,
0,
0,
0,
'constrain'
);
const relativeNs = GetSlot(relativeInstant, EPOCHNANOSECONDS);
dayLengthNs = oneDayFartherNs.subtract(relativeNs).toJSNumber();
isOverflow = nanoseconds.subtract(dayLengthNs).multiply(sign).geq(0);
if (isOverflow) {
nanoseconds = nanoseconds.subtract(dayLengthNs);
relativeInstant = new TemporalInstant(oneDayFartherNs);
days += sign;
}
} while (isOverflow);
return { days, nanoseconds };
},
BalanceDuration: (days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit) => {
nanoseconds = ES.TotalDurationNanoseconds(
days,
Expand Down Expand Up @@ -2626,57 +2672,34 @@ export const ES = ObjectAssign({}, ES2020, {
); // may do disambiguation
}

let isOverflow = false;
let timeRemainderNs = 0;
do {
// calculate length of the next day (day that contains the time remainder)
const oneDayFartherDuration = ES.AddDuration(
years,
months,
weeks,
days,
0,
0,
0,
0,
0,
0,
0,
0,
0,
direction,
0,
0,
0,
0,
0,
0,
dtStart
);
const oneDayFartherNs = ES.AddZonedDateTime(
GetSlot(start, INSTANT),
timeZone,
calendar,
oneDayFartherDuration.years,
oneDayFartherDuration.months,
oneDayFartherDuration.weeks,
oneDayFartherDuration.days,
0,
0,
0,
0,
0,
0,
'constrain'
);
const dayLengthNs = oneDayFartherNs.subtract(intermediateNs).toJSNumber();
timeRemainderNs = ns2.subtract(intermediateNs).toJSNumber();
isOverflow = (timeRemainderNs - dayLengthNs) * direction >= 0;
if (isOverflow) {
({ years, months, weeks, days } = oneDayFartherDuration);
intermediateNs = oneDayFartherNs;
}
} while (isOverflow);
let timeRemainderNs = ns2.subtract(intermediateNs).toJSNumber();
let deltaDays;
const TemporalZonedDateTime = GetIntrinsic('%Temporal.ZonedDateTime%');
const intermediate = new TemporalZonedDateTime(intermediateNs, timeZone, calendar);
({ nanoseconds: timeRemainderNs, days: deltaDays } = ES.NanosecondsToDays(timeRemainderNs, intermediate));
({ years, months, weeks, days } = ES.AddDuration(
years,
months,
weeks,
days,
0,
0,
0,
0,
0,
0,
0,
0,
0,
deltaDays,
0,
0,
0,
0,
0,
0,
dtStart
));

// Finally, merge the date and time durations and return the merged result.
let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceDuration(
Expand Down
46 changes: 36 additions & 10 deletions spec/zoneddatetime.html
Original file line number Diff line number Diff line change
Expand Up @@ -1350,20 +1350,46 @@ <h1>DifferenceZonedDateTime ( _ns1_, _ns2_, _timeZone_, _calendar_, _largestUnit
1. Repeat, while ! DurationSign(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], 0, 0, 0, 0, 0, 0) = 1 and _intermediateNs_ &gt; _ns2_,
1. Set _dateDifference_ to ? AddDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −1, 0, 0, 0, 0, 0, 0, _startDateTime_).
1. Set _intermediateNs_ to ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], 0, 0, 0, 0, 0, 0, *"constrain"*).
1. Let _timeRemainderNs_ be 0.
1. Let _timeRemainderNs_ be _ns2__intermediateNs_.
1. Let _intermediate_ be ? CreateTemporalZonedDateTime(_intermediateNs_, _timeZone_, _calendar_).
1. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
1. Let _timeDifference_ be ! BalanceDuration(0, 0, 0, 0, 0, 0, _result_.[[Nanoseconds]], *"hours"*).
1. Let _dateDifference_ be ? AddDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], 0, 0, 0, 0, 0, 0, 0, 0, 0, _result_.[[Days]], 0, 0, 0, 0, 0, 0, _startDateTime_).
1. Return the new Record { [[Years]]: _dateDifference_.[[Years]], [[Months]]: _dateDifference_.[[Months]], [[Weeks]]: _dateDifference_.[[Weeks]], [[Days]]: _dateDifference_.[[Days]], [[Hours]]: _timeDifference_.[[Hours]], [[Minutes]]: _timeDifference_.[[Minutes]], [[Seconds]]: _timeDifference_.[[Seconds]], [[Milliseconds]]: _timeDifference_.[[Milliseconds]], [[Microseconds]]: _timeDifference_.[[Microseconds]], [[Nanoseconds]]: _timeDifference_.[[Nanoseconds]] }.
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-nanosecondstodays" aoid="NanosecondsToDays">
<h1>NanosecondsToDays ( _nanoseconds_, _relativeTo_ )</h1>
<p>
The abstract operation NanosecondsToDays takes a number of nanoseconds relative to a Temporal.ZonedDateTime _relativeTo_, and converts it into a number of days and remainder of nanoseconds, taking into account any offset changes in the time zone of _relativeTo_.
</p>
<emu-alg>
1. Let _sign_ be ! Sign(_nanoseconds_).
1. Let _dayLengthNs_ be 8.64 × 10<sup>13</sup><sub>ℝ</sub>.
1. If _sign_ = 0, then
1. Return the new Record { [[Days]]: 0, [[Nanoseconds]]: 0 }.
1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
1. Return the new Record {
[[Days]]: the integral part of _nanoseconds_ ÷ _dayLengthNs_,
[[Nanoseconds]]: _nanoseconds_ modulo _dayLengthNs_
}.
1. Let _days_ be 0.
1. Let _done_ be *false*.
1. Let _relativeNs_ be _relativeTo_.[[Nanoseconds]].
1. Repeat, while _done_ is *false*,
1. Let _oneDayFartherDuration_ be ? AddDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _dateDifference_.[[Days]], 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _startDateTime_).
1. Let _oneDayFartherNs_ be ? AddZonedDateTime(_ns1_, _timeZone, _calendar_, _oneDayFartherDuration_.[[Years]], _oneDayFartherDuration_.[[Months]], _oneDayFartherDuration_.[[Weeks]], _oneDayFartherDuration_.[[Days]], 0, 0, 0, 0, 0, 0, *"constrain"*).
1. Let _dayLengthNs_ be _oneDayFartherNs__intermediateNs_.
1. Set _timeRemainderNs_ to _ns2__intermediateNs_.
1. If (_timeRemainderNs__dayLengthNs_) × _direction_ ≥ 0, then
1. Set _dateDifference_ to _oneDayFartherDuration_.
1. Set _intermediateNs_ to _oneDayFartherNs_.
1. Let _oneDayFartherNs_ be ? AddZonedDateTime(_relativeNs_, _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], 0, 0, 0, _sign_, 0, 0, 0, 0, 0, 0, *"constrain"*).
1. Let _dayLengthNs_ be _oneDayFartherNs__relativeNs_.
1. If (_nanoseconds__dayLengthNs_) × _sign_ ≥ 0, then
1. Set _nanoseconds_ to _nanoseconds__dayLengthNs_.
1. Set _relativeNs_ to _oneDayFartherNs_.
1. Set _days_ to _days_ + _sign_.
1. Else,
1. Set _done_ to *true*.
1. Let _timeDifference_ be ! BalanceDuration(0, 0, 0, 0, 0, 0, _roundedTimeRemainderNs_, *"hours"*).
1. Return the new Record { [[Years]]: _dateDifference_.[[Years]], [[Months]]: _dateDifference_.[[Months]], [[Weeks]]: _dateDifference_.[[Weeks]], [[Days]]: _dateDifference_.[[Days]], [[Hours]]: _timeDifference_.[[Hours]], [[Minutes]]: _timeDifference_.[[Minutes]], [[Seconds]]: _timeDifference_.[[Seconds]], [[Milliseconds]]: _timeDifference_.[[Milliseconds]], [[Microseconds]]: _timeDifference_.[[Microseconds]], [[Nanoseconds]]: _timeDifference_.[[Nanoseconds]] }.
1. Return the new Record {
[[Days]]: _days_,
[[Nanoseconds]]: _nanoseconds_
}.
</emu-alg>
</emu-clause>
</emu-clause>
Expand Down

0 comments on commit 97d30d3

Please sign in to comment.