Skip to content

Commit

Permalink
Editorial: Split BalanceTimeDurationRelative out of BalanceTimeDuration
Browse files Browse the repository at this point in the history
BalanceTimeDuration (formerly BalanceDuration) had two code paths tangled
up inside of it: one where days always equal 24 hours (duration is not
relative to a ZonedDateTime) and one where days may be different from 24
hours (duration is relative to a ZonedDateTime). Splitting these two code
paths out into two different AOs makes them both easier to understand and
reason about.

It also removes the need to manually indicate that BalanceTimeDuration
doesn't call into user code, because only BalanceTimeDurationRelative can
call into user code.
  • Loading branch information
ptomato committed Jun 12, 2023
1 parent cbec236 commit 0547d29
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 100 deletions.
88 changes: 56 additions & 32 deletions polyfill/lib/duration.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -304,34 +304,47 @@ export class Duration {
roundingMode,
relativeTo
));
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.AdjustRoundedDurationDays(
years,
months,
weeks,
if (ES.IsTemporalZonedDateTime(relativeTo)) {
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.AdjustRoundedDurationDays(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
roundingIncrement,
smallestUnit,
roundingMode,
relativeTo
));
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDurationRelative(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
roundingIncrement,
smallestUnit,
roundingMode,
largestUnit,
relativeTo
));
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit,
relativeTo
));
} else {
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit
));
}
({ years, months, weeks, days } = ES.BalanceDateDurationRelative(
years,
months,
Expand Down Expand Up @@ -370,21 +383,32 @@ export class Duration {
// Convert larger units down to days
({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(years, months, weeks, days, unit, relativeTo));
// If the unit we're totalling is smaller than `days`, convert days down to that unit.
let intermediate;
let balanceResult;
if (ES.IsTemporalZonedDateTime(relativeTo)) {
intermediate = ES.MoveRelativeZonedDateTime(relativeTo, years, months, weeks, 0);
const intermediate = ES.MoveRelativeZonedDateTime(relativeTo, years, months, weeks, 0);
balanceResult = ES.BalancePossiblyInfiniteTimeDurationRelative(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
unit,
intermediate
);
} else {
balanceResult = ES.BalancePossiblyInfiniteTimeDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
unit
);
}
let balanceResult = ES.BalancePossiblyInfiniteTimeDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
unit,
intermediate
);
if (balanceResult === 'positive overflow') {
return Infinity;
} else if (balanceResult === 'negative overflow') {
Expand Down
124 changes: 91 additions & 33 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3149,8 +3149,7 @@ export function BalanceTimeDuration(
milliseconds,
microseconds,
nanoseconds,
largestUnit,
relativeTo = undefined
largestUnit
) {
let result = BalancePossiblyInfiniteTimeDuration(
days,
Expand All @@ -3160,8 +3159,7 @@ export function BalanceTimeDuration(
milliseconds,
microseconds,
nanoseconds,
largestUnit,
relativeTo
largestUnit
);
if (result === 'positive overflow' || result === 'negative overflow') {
throw new RangeError('Duration out of range');
Expand All @@ -3178,45 +3176,26 @@ export function BalancePossiblyInfiniteTimeDuration(
milliseconds,
microseconds,
nanoseconds,
largestUnit,
relativeTo = undefined
largestUnit
) {
if (IsTemporalZonedDateTime(relativeTo)) {
const endNs = AddZonedDateTime(
GetSlot(relativeTo, INSTANT),
GetSlot(relativeTo, TIME_ZONE),
GetSlot(relativeTo, CALENDAR),
0,
0,
0,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds
);
const startNs = GetSlot(relativeTo, EPOCHNANOSECONDS);
nanoseconds = endNs.subtract(startNs);
} else {
nanoseconds = TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0);
}
if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || largestUnit === 'day') {
({ days, nanoseconds } = NanosecondsToDays(nanoseconds, relativeTo));
} else {
days = 0;
}
nanoseconds = TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0);

const sign = nanoseconds.lesser(0) ? -1 : 1;
nanoseconds = nanoseconds.abs();
microseconds = milliseconds = seconds = minutes = hours = bigInt.zero;
microseconds = milliseconds = seconds = minutes = hours = days = bigInt.zero;

switch (largestUnit) {
case 'year':
case 'month':
case 'week':
case 'day':
({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000));
({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000));
({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000));
({ quotient: minutes, remainder: seconds } = seconds.divmod(60));
({ quotient: hours, remainder: minutes } = minutes.divmod(60));
({ quotient: days, remainder: hours } = hours.divmod(24));
break;
case 'hour':
({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000));
({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000));
Expand Down Expand Up @@ -3248,6 +3227,7 @@ export function BalancePossiblyInfiniteTimeDuration(
throw new Error('assert not reached');
}

days = days.toJSNumber() * sign;
hours = hours.toJSNumber() * sign;
minutes = minutes.toJSNumber() * sign;
seconds = seconds.toJSNumber() * sign;
Expand All @@ -3273,6 +3253,84 @@ export function BalancePossiblyInfiniteTimeDuration(
return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
}

export function BalanceTimeDurationRelative(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit,
zonedRelativeTo
) {
let result = BalancePossiblyInfiniteTimeDurationRelative(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit,
zonedRelativeTo
);
if (result === 'positive overflow' || result === 'negative overflow') {
throw new RangeError('Duration out of range');
}
return result;
}

export function BalancePossiblyInfiniteTimeDurationRelative(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit,
zonedRelativeTo
) {
const endNs = AddZonedDateTime(
GetSlot(zonedRelativeTo, INSTANT),
GetSlot(zonedRelativeTo, TIME_ZONE),
GetSlot(zonedRelativeTo, CALENDAR),
0,
0,
0,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds
);
const startNs = GetSlot(zonedRelativeTo, EPOCHNANOSECONDS);
nanoseconds = endNs.subtract(startNs);

if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || largestUnit === 'day') {
({ days, nanoseconds } = NanosecondsToDays(nanoseconds, zonedRelativeTo));
largestUnit = 'hour';
} else {
days = 0;
}

({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalancePossiblyInfiniteTimeDuration(
0,
0,
0,
0,
0,
0,
nanoseconds,
largestUnit
));

return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
}

export function UnbalanceDateDurationRelative(years, months, weeks, days, largestUnit, relativeTo) {
const TemporalDuration = GetIntrinsic('%Temporal.Duration%');
const sign = DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0);
Expand Down
2 changes: 1 addition & 1 deletion spec/calendar.html
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@ <h1>Temporal.Calendar.prototype.dateAdd ( _date_, _duration_ [ , _options_ ] )</
1. Set _duration_ to ? ToTemporalDuration(_duration_).
1. Set _options_ to ? GetOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
1. Let _balanceResult_ be ? <emu-meta suppress-effects="user-code">BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*)</emu-meta>.
1. Let _balanceResult_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*).
1. Let _result_ be ? AddISODate(_date_.[[ISOYear]], _date_.[[ISOMonth]], _date_.[[ISODay]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], _overflow_).
1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], *"iso8601"*).
</emu-alg>
Expand Down
Loading

0 comments on commit 0547d29

Please sign in to comment.