diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 7c1e62271f..42b0f4ce3e 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -339,7 +339,14 @@ export class Duration { ) { calendarRec.lookup('dateAdd'); } - if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || smallestUnit === 'year') { + if ( + largestUnit === 'year' || + largestUnit === 'month' || + largestUnit === 'week' || + smallestUnit === 'year' || + smallestUnit === 'month' || + smallestUnit === 'week' + ) { calendarRec.lookup('dateUntil'); } } @@ -479,7 +486,7 @@ export class Duration { if (years !== 0 || months !== 0 || weeks !== 0 || unit === 'year' || unit === 'month' || unit === 'week') { calendarRec.lookup('dateAdd'); } - if (unit === 'year' || (unit === 'month' && years !== 0)) { + if (unit === 'year' || unit === 'month' || unit === 'week') { calendarRec.lookup('dateUntil'); } } diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 32286c115d..f8d03ef257 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -4143,7 +4143,9 @@ export function DifferenceTemporalPlainDate(operation, plainDate, other, options settings.largestUnit === 'year' || settings.largestUnit === 'month' || settings.largestUnit === 'week' || - settings.smallestUnit === 'year' + settings.smallestUnit === 'year' || + settings.smallestUnit === 'month' || + settings.smallestUnit === 'week' ) { calendarRec.lookup('dateUntil'); } @@ -4211,7 +4213,9 @@ export function DifferenceTemporalPlainDateTime(operation, plainDateTime, other, if ( (!datePartsIdentical && (settings.largestUnit === 'year' || settings.largestUnit === 'month' || settings.largestUnit === 'week')) || - settings.smallestUnit === 'year' + settings.smallestUnit === 'year' || + settings.smallestUnit === 'month' || + settings.smallestUnit === 'week' ) { calendarRec.lookup('dateUntil'); } @@ -5498,8 +5502,11 @@ export function RoundDuration( zonedRelativeTo = undefined, timeZoneRec = undefined ) { - // dateAdd, dateUntil must be looked up if smallestUnit == year - // dateAdd must be looked up if smallestUnit == month or week + // calendarRec must have looked up: + // dateAdd, if + // smallestUnit == year, month, week or + // (smallestUnit == day && any of years, months, weeks days ≠ 0 && zonedRelativeTo defined) + // dateUntil, if smallestUnit == year, month, week && any of years, months, weeks ≠ 0 const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); if ((unit === 'year' || unit === 'month' || unit === 'week') && !plainRelativeTo) { @@ -5598,21 +5605,32 @@ export function RoundDuration( plainRelativeTo = yearsMonthsLater; days += weeksInDays; - // Months may be different lengths of days depending on the calendar, - // convert days to months in a loop as described above under 'years'. - const sign = MathSign(days); + const isoResult = AddISODate( + GetSlot(plainRelativeTo, ISO_YEAR), + GetSlot(plainRelativeTo, ISO_MONTH), + GetSlot(plainRelativeTo, ISO_DAY), + 0, + 0, + 0, + days, + 'constrain' + ); + const wholeDaysLater = CreateTemporalDate(isoResult.year, isoResult.month, isoResult.day, calendarRec.receiver); + const untilOptions = ObjectCreate(null); + untilOptions.largestUnit = 'month'; + const monthsPassed = GetSlot(DifferenceDate(calendarRec, plainRelativeTo, wholeDaysLater, untilOptions), MONTHS); + months += monthsPassed; + const monthsPassedDuration = new TemporalDuration(0, monthsPassed); + let daysPassed; + ({ relativeTo: plainRelativeTo, days: daysPassed } = MoveRelativeDate( + calendarRec, + plainRelativeTo, + monthsPassedDuration + )); + days -= daysPassed; const oneMonth = new TemporalDuration(0, days < 0 ? -1 : 1); - let oneMonthDays; - ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate(calendarRec, plainRelativeTo, oneMonth)); - while (MathAbs(days) >= MathAbs(oneMonthDays)) { - months += sign; - days -= oneMonthDays; - ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate( - calendarRec, - plainRelativeTo, - oneMonth - )); - } + let { days: oneMonthDays } = MoveRelativeDate(calendarRec, plainRelativeTo, oneMonth); + oneMonthDays = MathAbs(oneMonthDays); const divisor = bigInt(oneMonthDays).multiply(dayLengthNs); nanoseconds = divisor.multiply(months).plus(bigInt(days).multiply(dayLengthNs)).plus(nanoseconds); @@ -5624,17 +5642,32 @@ export function RoundDuration( break; } case 'week': { - // Weeks may be different lengths of days depending on the calendar, - // convert days to weeks in a loop as described above under 'years'. - const sign = MathSign(days); + const isoResult = AddISODate( + GetSlot(plainRelativeTo, ISO_YEAR), + GetSlot(plainRelativeTo, ISO_MONTH), + GetSlot(plainRelativeTo, ISO_DAY), + 0, + 0, + 0, + days, + 'constrain' + ); + const wholeDaysLater = CreateTemporalDate(isoResult.year, isoResult.month, isoResult.day, calendarRec.receiver); + const untilOptions = ObjectCreate(null); + untilOptions.largestUnit = 'week'; + const weeksPassed = GetSlot(DifferenceDate(calendarRec, plainRelativeTo, wholeDaysLater, untilOptions), WEEKS); + weeks += weeksPassed; + const weeksPassedDuration = new TemporalDuration(0, 0, weeksPassed); + let daysPassed; + ({ relativeTo: plainRelativeTo, days: daysPassed } = MoveRelativeDate( + calendarRec, + plainRelativeTo, + weeksPassedDuration + )); + days -= daysPassed; const oneWeek = new TemporalDuration(0, 0, days < 0 ? -1 : 1); - let oneWeekDays; - ({ relativeTo: plainRelativeTo, days: oneWeekDays } = MoveRelativeDate(calendarRec, plainRelativeTo, oneWeek)); - while (MathAbs(days) >= MathAbs(oneWeekDays)) { - weeks += sign; - days -= oneWeekDays; - ({ relativeTo: plainRelativeTo, days: oneWeekDays } = MoveRelativeDate(calendarRec, plainRelativeTo, oneWeek)); - } + let { days: oneWeekDays } = MoveRelativeDate(calendarRec, plainRelativeTo, oneWeek); + oneWeekDays = MathAbs(oneWeekDays); const divisor = bigInt(oneWeekDays).multiply(dayLengthNs); nanoseconds = divisor.multiply(weeks).plus(bigInt(days).multiply(dayLengthNs)).plus(nanoseconds); diff --git a/spec/duration.html b/spec/duration.html index 9fdb8763e3..84e9dd464f 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -478,7 +478,7 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. Set _calendarRec_ to ! CreateCalendarRecord(_calendar_, « »). 1. If _duration_.[[Years]] ≠ 0; or _duration_.[[Months]] ≠ 0; or _duration_.[[Weeks]] ≠ 0; or _duration_.[[Days]] ≠ 0 and _largestUnit_ is any of *"year"*, *"month"*, *"week"*; or _smallestUnit_ is any of *"year"*, *"month"*, *"week"*; then 1. Set _calendarRec_.[[DateAdd]] to ? GetMethod(_calendar_, *"dateAdd"*). - 1. If _largestUnit_ is any of *"year"*, *"month"*, *"week"*, or _smallestUnit_ is *"year"*, then + 1. If _largestUnit_ is any of *"year"*, *"month"*, *"week"*, or _smallestUnit_ is any of *"year"*, *"month"*, *"week"*, then 1. Set _calendarRec_.[[DateUntil]] to ? GetMethod(_calendar_, *"dateUntil"*). 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _plainRelativeTo_, _calendarRec_). 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _plainRelativeTo_, _calendarRec_, _zonedRelativeTo_, _timeZoneRec_). @@ -532,7 +532,7 @@

Temporal.Duration.prototype.total ( _totalOf_ )

1. Set _calendarRec_ to ! CreateCalendarRecord(_calendar_, « »). 1. If _duration_.[[Years]] ≠ 0; or _duration_.[[Months]] ≠ 0; or _duration_.[[Weeks]] ≠ 0; or _unit_ is any of *"year"*, *"month"*, *"week"*; then 1. Set _calendarRec_.[[DateAdd]] to ? GetMethod(_calendar_, *"dateAdd"*). - 1. If _unit_ is *"year"*, or _unit_ is *"month"* and _duration_.[[Years]] ≠ 0, then + 1. If _unit_ is any of *"year"*, *"month"*, *"week"*, then 1. Set _calendarRec_.[[DateUntil]] to ? GetMethod(_calendar_, *"dateUntil"*). 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _plainRelativeTo_, _calendarRec_). 1. If _zonedRelativeTo_ is not *undefined*, then @@ -1656,10 +1656,12 @@

1. Assert: If either of _plainRelativeTo_ or _zonedRelativeTo_ are present and not *undefined*, _calendarRec_ is not *undefined*. 1. Assert: If _zonedRelativeTo_ is present and not *undefined*, _timeZoneRec_ is not *undefined*. - 1. Assert: If _unit_ is *"year"*, neither of _calendarRec_.[[DateAdd]] or _calendarRec_.[[DateUntil]] are *undefined*. - 1. Assert: If _unit_ is *"month"* or *"week"*, _calendarRec_.[[DateAdd]] is not *undefined*. + 1. If _unit_ is any of *"year"*, *"month"*, *"week"*, then + 1. Assert: _calendarRec_.[[DateAdd]] is not *undefined*. + 1. Assert: If _years_ ≠ 0, or _months_ ≠ 0, or _weeks_ ≠ 0, _calendarRec_.[[DateUntil]] is not *undefined*. 1. If _plainRelativeTo_ is not present, set _plainRelativeTo_ to *undefined*. 1. If _zonedRelativeTo_ is not present, set _zonedRelativeTo_ to *undefined*. + 1. Assert: If _unit_ is *"day"*; and _zonedRelativeTo_ is not *undefined*; and _years_ ≠ 0, or _months_ ≠ 0, or _weeks_ ≠ 0, or _days_ ≠ 0, _calendarRec_.[[DateAdd]] is not *undefined*. 1. If _unit_ is *"year"*, *"month"*, or *"week"*, and _plainRelativeTo_ is *undefined*, then 1. Throw a *RangeError* exception. 1. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then @@ -1712,33 +1714,43 @@

1. Let _weeksInDays_ be DaysUntil(_yearsMonthsLater_, _yearsMonthsWeeksLater_). 1. Set _plainRelativeTo_ to _yearsMonthsLater_. 1. Set _fractionalDays_ to _fractionalDays_ + _weeksInDays_. + 1. Let _isoResult_ be ! AddISODate(_plainRelativeTo_.[[ISOYear]], _plainRelativeTo_.[[ISOMonth]], _plainRelativeTo_.[[ISODay]], 0, 0, 0, truncate(_fractionalDays_), *"constrain"*). + 1. Let _wholeDaysLater_ be ! CreateTemporalDate(_isoResult_.[[Year]], _isoResult_.[[Month]], _isoResult_.[[Day]], _calendarRec_.[[Receiver]]). + 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + 1. Let _timePassed_ be ? DifferenceDate(_calendarRec_, _plainRelativeTo_, _wholeDaysLater_, _untilOptions_). + 1. Let _monthsPassed_ be _timePassed_.[[Months]]. + 1. Set _months_ to _months_ + _monthsPassed_. + 1. Let _monthsPassedDuration_ be ! CreateTemporalDuration(0, _monthsPassed_, 0, 0, 0, 0, 0, 0, 0, 0). + 1. Let _moveResult_ be ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _monthsPassedDuration_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _daysPassed_ be _moveResult_.[[Days]]. + 1. Set _fractionalDays_ to _fractionalDays_ - _daysPassed_. 1. If _fractionalDays_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Let _oneMonth_ be ! CreateTemporalDuration(0, _sign_, 0, 0, 0, 0, 0, 0, 0, 0). 1. Let _moveResult_ be ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _oneMonth_). - 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _oneMonthDays_ be _moveResult_.[[Days]]. - 1. Repeat, while abs(_fractionalDays_) ≥ abs(_oneMonthDays_), - 1. Set _months_ to _months_ + _sign_. - 1. Set _fractionalDays_ to _fractionalDays_ - _oneMonthDays_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _oneMonth_). - 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. - 1. Set _oneMonthDays_ to _moveResult_.[[Days]]. 1. Let _fractionalMonths_ be _months_ + _fractionalDays_ / abs(_oneMonthDays_). 1. Set _months_ to RoundNumberToIncrement(_fractionalMonths_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalMonths_. 1. Set _weeks_ to 0. 1. Else if _unit_ is *"week"*, then + 1. Let _isoResult_ be ! AddISODate(_plainRelativeTo_.[[ISOYear]], _plainRelativeTo_.[[ISOMonth]], _plainRelativeTo_.[[ISODay]], 0, 0, 0, truncate(_fractionalDays_), *"constrain"*). + 1. Let _wholeDaysLater_ be ! CreateTemporalDate(_isoResult_.[[Year]], _isoResult_.[[Month]], _isoResult_.[[Day]], _calendarRec_.[[Receiver]]). + 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"week"*). + 1. Let _timePassed_ be ? DifferenceDate(_calendarRec_, _plainRelativeTo_, _wholeDaysLater_, _untilOptions_). + 1. Let _weeksPassed_ be _timePassed_.[[Weeks]]. + 1. Set _weeks_ to _weeks_ + _weeksPassed_. + 1. Let _weeksPassedDuration_ be ! CreateTemporalDuration(0, 0, _weeksPassed_, 0, 0, 0, 0, 0, 0, 0). + 1. Let _moveResult_ be ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _weeksPassedDuration_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _daysPassed_ be _moveResult_.[[Days]]. + 1. Set _fractionalDays_ to _fractionalDays_ - _daysPassed_. 1. If _fractionalDays_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Let _oneWeek_ be ! CreateTemporalDuration(0, 0, _sign_, 0, 0, 0, 0, 0, 0, 0). 1. Let _moveResult_ be ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _oneWeek_). - 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _oneWeekDays_ be _moveResult_.[[Days]]. - 1. Repeat, while abs(_fractionalDays_) ≥ abs(_oneWeekDays_), - 1. Set _weeks_ to _weeks_ + _sign_. - 1. Set _fractionalDays_ to _fractionalDays_ - _oneWeekDays_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendarRec_, _plainRelativeTo_, _oneWeek_). - 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. - 1. Set _oneWeekDays_ to _moveResult_.[[Days]]. 1. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_). 1. Set _weeks_ to RoundNumberToIncrement(_fractionalWeeks_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalWeeks_. diff --git a/spec/plaindate.html b/spec/plaindate.html index 025e16122b..6db2020f5b 100644 --- a/spec/plaindate.html +++ b/spec/plaindate.html @@ -1055,7 +1055,7 @@

1. Let _calendarRec_ be ! CreateCalendarRecord(_temporalDate_.[[Calendar]], « »). 1. If _settings_.[[SmallestUnit]] is one of *"year"*, *"month"*, or *"week"*, then 1. Set _calendarRec_.[[DateAdd]] to ? GetMethod(_temporalDate_.[[Calendar]], *"dateAdd"*). - 1. If _settings_.[[LargestUnit]] is one of *"year"*, *"month"*, or *"week"*, or _settings_.[[SmallestUnit]] is *"year"*, then + 1. If _settings_.[[LargestUnit]] is one of *"year"*, *"month"*, or *"week"*, or _settings_.[[SmallestUnit]] is one of *"year"*, *"month"*, *"week"*, then 1. Set _calendarRec_.[[DateUntil]] to ? GetMethod(_temporalDate_.[[Calendar]], *"dateUntil"*). 1. Let _result_ be ? DifferenceDate(_calendarRec_, _temporalDate_, _other_, _resolvedOptions_). 1. If _settings_.[[SmallestUnit]] is not *"day"* or _settings_.[[RoundingIncrement]] ≠ 1, then diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html index b45e0321e3..8b61bcb9ba 100644 --- a/spec/plaindatetime.html +++ b/spec/plaindatetime.html @@ -1194,7 +1194,7 @@

1. Let _largestUnitRequiresDateUntilLookup_ be *false*. 1. If _datePartsIdentical_ is *false* and _settings_.[[LargestUnit]] is one of *"year"*, *"month"*, or *"week"*, then 1. Set _largestUnitRequiresDateUntilLookup_ to *true*. - 1. If _largestUnitRequiresDateUntilLookup_ is *true*, or _settings_.[[SmallestUnit]] is *"year"*, then + 1. If _largestUnitRequiresDateUntilLookup_ is *true*, or _settings_.[[SmallestUnit]] is one of *"year"*, *"month"*, *"week"*, then 1. Set _calendarRec_.[[DateUntil]] to ? GetMethod(_dateTime_.[[Calendar]], *"dateUntil"*). 1. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _settings_.[[LargestUnit]], _resolvedOptions_, _calendarRec_). 1. If _settings_.[[SmallestUnit]] is *"nanosecond"* and _settings_.[[RoundingIncrement]] is 1, then