From 5df5d82c6f1b06b22cd25bd7e2e698ba6eebcce3 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 10 Aug 2020 16:58:49 -0700 Subject: [PATCH] Allow difference() to return negative durations This undoes the work in commit d84653ebe0c51b04d0344f19ef8be7a10ab6aad5 although it's not exactly a revert, because the situation before that was that durations were always positive. We also don't reinstate the behaviour of never returning a duration larger than 12 hours. Now, for all types, calling smaller.difference(larger) will result in a negative duration. See: #782 --- docs/absolute.md | 2 +- docs/cookbook.md | 2 +- .../getElapsedDurationSinceInstant.mjs | 44 +++---------- docs/date.md | 4 +- docs/datetime.md | 6 +- docs/time.md | 4 +- docs/yearmonth.md | 4 +- polyfill/lib/absolute.mjs | 2 - polyfill/lib/calendar.mjs | 10 +-- polyfill/lib/date.mjs | 2 - polyfill/lib/datetime.mjs | 12 ++-- polyfill/lib/ecmascript.mjs | 66 ++++++++++++++++--- polyfill/lib/time.mjs | 2 - polyfill/lib/yearmonth.mjs | 12 ++-- polyfill/test/absolute.mjs | 3 +- polyfill/test/datetime.mjs | 3 +- polyfill/test/time.mjs | 4 +- polyfill/test/yearmonth.mjs | 3 +- spec/absolute.html | 6 +- spec/date.html | 26 ++++---- spec/datetime.html | 14 ++-- spec/duration.html | 17 ++--- spec/time.html | 39 ++++++----- spec/yearmonth.html | 8 +-- 24 files changed, 157 insertions(+), 138 deletions(-) diff --git a/docs/absolute.md b/docs/absolute.md index a9d8ac3930..dd31d1f563 100644 --- a/docs/absolute.md +++ b/docs/absolute.md @@ -304,7 +304,7 @@ Temporal.now.absolute().minus(oneDay); **Returns:** a `Temporal.Duration` representing the difference between `absolute` and `other`. This method computes the difference between the two times represented by `absolute` and `other`, and returns it as a `Temporal.Duration` object. -A `RangeError` will be thrown if `other` is later than `absolute`, because `Temporal.Duration` objects cannot represent negative durations. +If `other` is later than `absolute` then the resulting duration will be negative. The `largestUnit` option controls how the resulting duration is expressed. The returned `Temporal.Duration` object will not have any nonzero fields that are larger than the unit in `largestUnit`. diff --git a/docs/cookbook.md b/docs/cookbook.md index 0651ead3e4..3d99c2ca6d 100644 --- a/docs/cookbook.md +++ b/docs/cookbook.md @@ -322,7 +322,7 @@ An example HTML form inspired by [Days Calculator](https://www.timeanddate.com/d ### Unit-constrained duration between now and a past/future zoned event -Map two Temporal.Absolute instances into an ascending/descending order indicator and a Temporal.Duration instance representing the duration between the two instants without using units coarser than specified (e.g., for presenting a meaningful countdown with vs. without using months or days). +Take the difference between two Temporal.Absolute instances as a Temporal.Duration instance (positive or negative), representing the duration between the two instants without using units coarser than specified (e.g., for presenting a meaningful countdown with vs. without using months or days). ```javascript {{cookbook/getElapsedDurationSinceInstant.mjs}} diff --git a/docs/cookbook/getElapsedDurationSinceInstant.mjs b/docs/cookbook/getElapsedDurationSinceInstant.mjs index 1db6b60164..365ec665e2 100644 --- a/docs/cookbook/getElapsedDurationSinceInstant.mjs +++ b/docs/cookbook/getElapsedDurationSinceInstant.mjs @@ -1,40 +1,16 @@ -/** - * @typedef {Object} ElapsedDuration - * @property {string} return.sign - "+" or "-" - * @property {Temporal.Duration} return.duration - Elapsed duration - */ -/** - * Compute the difference between two instants, suitable for use in a countdown, - * for example. - * - * @param {Temporal.Absolute} then - Instant since when to measure the duration - * @param {Temporal.Absolute} now - Instant until when to measure the duration - * @param {string} [largestUnit=days] - Largest time unit to have in the result - * @returns {ElapsedDuration} Time between `then` and `now` - */ -function getElapsedDurationSinceInstant(then, now, largestUnit = 'days') { - const sign = Temporal.Absolute.compare(now, then) < 0 ? '-' : '+'; - const duration = sign === '-' ? then.difference(now, { largestUnit }) : now.difference(then, { largestUnit }); - return { sign, duration }; -} +const result = Temporal.Absolute.from('2020-01-09T04:00Z').difference(Temporal.Absolute.from('2020-01-09T00:00Z'), { + largestUnit: 'hours' +}); +assert.equal(`${result}`, 'PT4H'); -const result = getElapsedDurationSinceInstant( - Temporal.Absolute.from('2020-01-09T00:00Z'), - Temporal.Absolute.from('2020-01-09T04:00Z') -); -assert.equal(`${result.sign}${result.duration}`, '+PT4H'); - -const result2 = getElapsedDurationSinceInstant( - Temporal.Absolute.from('2020-01-09T04:00Z'), - Temporal.Absolute.from('2020-01-09T00:00Z'), - 'minutes' -); -assert.equal(`${result2.sign}${result2.duration}`, '-PT240M'); +const result2 = Temporal.Absolute.from('2020-01-09T00:00Z').difference(Temporal.Absolute.from('2020-01-09T04:00Z'), { + largestUnit: 'minutes' +}); +assert.equal(`${result2}`, '-PT240M'); // Example of using it in a countdown: -const { sign, duration } = getElapsedDurationSinceInstant( - Temporal.Absolute.from('2020-04-01T13:00-07:00[America/Los_Angeles]'), +const duration = Temporal.Absolute.from('2020-04-01T13:00-07:00[America/Los_Angeles]').difference( Temporal.now.absolute() ); -`It's ${duration.toLocaleString()} ${sign < 0 ? 'until' : 'since'} the TC39 Temporal presentation`; +`It's ${duration.toLocaleString()} ${duration.sign < 0 ? 'until' : 'since'} the TC39 Temporal presentation`; diff --git a/docs/date.md b/docs/date.md index 1eddea6615..c56eb04f7a 100644 --- a/docs/date.md +++ b/docs/date.md @@ -384,7 +384,7 @@ date.minus({ months: 1 }, { disambiguation: 'reject' }) // => throws **Returns:** a `Temporal.Duration` representing the difference between `date` and `other`. This method computes the difference between the two dates represented by `date` and `other`, and returns it as a `Temporal.Duration` object. -A `RangeError` will be thrown if `other` is later than `date`, because `Temporal.Duration` objects cannot represent negative durations. +If `other` is later than `date` then the resulting duration will be negative. The `largestUnit` option controls how the resulting duration is expressed. The returned `Temporal.Duration` object will not have any nonzero fields that are larger than the unit in `largestUnit`. @@ -401,7 +401,7 @@ date = Temporal.Date.from('2019-01-31'); other = Temporal.Date.from('2006-08-24'); date.difference(other) // => P4543D date.difference(other, { largestUnit: 'years' }) // => P12Y5M7D -other.difference(date, { largestUnit: 'years' }) // => throws RangeError +other.difference(date, { largestUnit: 'years' }) // => -P12Y5M7D // If you really need to calculate the difference between two Dates in // hours, you can eliminate the ambiguity by explicitly choosing the diff --git a/docs/datetime.md b/docs/datetime.md index 9f38e31795..e9f9d42e9f 100644 --- a/docs/datetime.md +++ b/docs/datetime.md @@ -448,7 +448,7 @@ dt.minus({ months: 1 }) // => throws **Returns:** a `Temporal.Duration` representing the difference between `datetime` and `other`. This method computes the difference between the two times represented by `datetime` and `other`, and returns it as a `Temporal.Duration` object. -A `RangeError` will be thrown if `other` is later than `datetime`, because `Temporal.Duration` objects cannot represent negative durations. +If `other` is later than `datetime` then the resulting duration will be negative. The `largestUnit` option controls how the resulting duration is expressed. The returned `Temporal.Duration` object will not have any nonzero fields that are larger than the unit in `largestUnit`. @@ -463,8 +463,8 @@ Usage example: dt1 = Temporal.DateTime.from('1995-12-07T03:24:30.000003500'); dt2 = Temporal.DateTime.from('2019-01-31T15:30'); dt2.difference(dt1); // => P8456DT12H5M29.999996500S -dt2.difference(dt1), { largestUnit: 'years' }) // => P23Y1M24DT12H5M29.999996500S -dt1.difference(dt2), { largestUnit: 'years' }) // => throws RangeError +dt2.difference(dt1, { largestUnit: 'years' }) // => P23Y1M24DT12H5M29.999996500S +dt1.difference(dt2, { largestUnit: 'years' }) // => -P23Y1M24DT12H5M29.999996500S // Months and years can be different lengths [jan1, feb1, mar1] = [1, 2, 3].map(month => Temporal.DateTime.from({year: 2020, month, day: 1})); diff --git a/docs/time.md b/docs/time.md index 0ec6705ccb..7358a4e0cd 100644 --- a/docs/time.md +++ b/docs/time.md @@ -249,7 +249,7 @@ time.minus({ minutes: 5, nanoseconds: 800 }) // => 19:34:09.068345405 **Returns:** a `Temporal.Duration` representing the difference between `time` and `other`. This method computes the difference between the two times represented by `time` and `other`, and returns it as a `Temporal.Duration` object. -A `RangeError` will be thrown if `other` is later than `time`, because `Temporal.Duration` objects cannot represent negative durations. +If `other` is later than `time` then the resulting duration will be negative. The `largestUnit` parameter controls how the resulting duration is expressed. The returned `Temporal.Duration` object will not have any nonzero fields that are larger than the unit in `largestUnit`. @@ -263,7 +263,7 @@ Usage example: ```javascript time = Temporal.Time.from('20:13:20.971398099'); time.difference(Temporal.Time.from('19:39:09.068346205')) // => PT34M11.903051894S -time.difference(Temporal.Time.from('22:39:09.068346205')) // => throws RangeError +time.difference(Temporal.Time.from('22:39:09.068346205')) // => -PT2H25M49.903051894S ``` ### time.**equals**(_other_: Temporal.Time) : boolean diff --git a/docs/yearmonth.md b/docs/yearmonth.md index aa5390521e..4a04ec2096 100644 --- a/docs/yearmonth.md +++ b/docs/yearmonth.md @@ -303,7 +303,7 @@ ym.minus({years: 20, months: 4}) // => 1999-02 **Returns:** a `Temporal.Duration` representing the difference between `yearMonth` and `other`. This method computes the difference between the two months represented by `yearMonth` and `other`, and returns it as a `Temporal.Duration` object. -A `RangeError` will be thrown if `other` is later than `yearMonth`, because `Temporal.Duration` objects cannot represent negative durations. +If `other` is later than `yearMonth` then the resulting duration will be negative. The `largestUnit` option controls how the resulting duration is expressed. The returned `Temporal.Duration` object will not have any nonzero fields that are larger than the unit in `largestUnit`. @@ -318,7 +318,7 @@ ym = Temporal.YearMonth.from('2019-06'); other = Temporal.YearMonth.from('2006-08'); ym.difference(other) // => P12Y10M ym.difference(other, { largestUnit: 'months' }) // => P154M -other.difference(ym, { largestUnit: 'months' }) // => throws RangeError +other.difference(ym, { largestUnit: 'months' }) // => -P154M // If you really need to calculate the difference between two YearMonths // in days, you can eliminate the ambiguity by explicitly choosing the diff --git a/polyfill/lib/absolute.mjs b/polyfill/lib/absolute.mjs index 133bbb7e55..5ec839fb76 100644 --- a/polyfill/lib/absolute.mjs +++ b/polyfill/lib/absolute.mjs @@ -108,8 +108,6 @@ export class Absolute { if (!ES.IsTemporalAbsolute(other)) throw new TypeError('invalid Absolute object'); const largestUnit = ES.ToLargestTemporalUnit(options, 'seconds', ['years', 'months', 'weeks']); - const comparison = Absolute.compare(this, other); - if (comparison < 0) throw new RangeError('other instance cannot be larger than `this`'); const onens = GetSlot(other, EPOCHNANOSECONDS); const twons = GetSlot(this, EPOCHNANOSECONDS); const diff = twons.minus(onens); diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index 33c7e01bf2..9def604e02 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -54,9 +54,9 @@ export class Calendar { void constructor; throw new Error('not implemented'); } - difference(smaller, larger, options) { - void smaller; - void larger; + difference(one, two, options) { + void one; + void two; void options; throw new Error('not implemented'); } @@ -174,10 +174,10 @@ class ISO8601 extends Calendar { } return new constructor(year, month, day, this); } - difference(smaller, larger, options) { + difference(one, two, options) { if (!ES.IsTemporalCalendar(this)) throw new TypeError('invalid receiver'); const largestUnit = ES.ToLargestTemporalUnit(options, 'days', ['hours', 'minutes', 'seconds']); - const { years, months, weeks, days } = ES.DifferenceDate(smaller, larger, largestUnit); + const { years, months, weeks, days } = ES.DifferenceDate(one, two, largestUnit); const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration(years, months, weeks, days, 0, 0, 0, 0, 0, 0); } diff --git a/polyfill/lib/date.mjs b/polyfill/lib/date.mjs index 9a6540d8cf..bcad7fe7da 100644 --- a/polyfill/lib/date.mjs +++ b/polyfill/lib/date.mjs @@ -170,8 +170,6 @@ export class Date { if (calendar.id !== GetSlot(other, CALENDAR).id) { other = new Date(GetSlot(other, ISO_YEAR), GetSlot(other, ISO_MONTH), GetSlot(other, ISO_DAY), calendar); } - const comparison = Date.compare(this, other); - if (comparison < 0) throw new RangeError('other instance cannot be larger than `this`'); return calendar.difference(other, this, options); } equals(other) { diff --git a/polyfill/lib/datetime.mjs b/polyfill/lib/datetime.mjs index a2c7311eea..1d0e420eb3 100644 --- a/polyfill/lib/datetime.mjs +++ b/polyfill/lib/datetime.mjs @@ -383,8 +383,6 @@ export class DateTime { ); } const largestUnit = ES.ToLargestTemporalUnit(options, 'days'); - const comparison = DateTime.compare(this, other); - if (comparison < 0) throw new RangeError('other instance cannot be larger than `this`'); let { deltaDays, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceTime( other, this @@ -395,13 +393,19 @@ export class DateTime { ({ year, month, day } = ES.BalanceDate(year, month, day)); const TemporalDate = GetIntrinsic('%Temporal.Date%'); - const adjustedLarger = new TemporalDate(year, month, day, GetSlot(this, CALENDAR)); + const adjusted = new TemporalDate(year, month, day, calendar); + const otherDate = new TemporalDate( + GetSlot(other, ISO_YEAR), + GetSlot(other, ISO_MONTH), + GetSlot(other, ISO_DAY), + calendar + ); let dateLargestUnit = 'days'; if (largestUnit === 'years' || largestUnit === 'months' || largestUnit === 'weeks') { dateLargestUnit = largestUnit; } const dateOptions = ObjectAssign({}, options, { largestUnit: dateLargestUnit }); - const dateDifference = calendar.difference(other, adjustedLarger, dateOptions); + const dateDifference = calendar.difference(otherDate, adjusted, dateOptions); let days; ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceDuration( diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 230c92ebde..a5b30f4783 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -1049,6 +1049,15 @@ export const ES = ObjectAssign({}, ES2019, { return { year, month, years, months }; }, BalanceDuration: (days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit) => { + const sign = ES.DurationSign(0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + days *= sign; + hours *= sign; + minutes *= sign; + seconds *= sign; + milliseconds *= sign; + microseconds *= sign; + nanoseconds *= sign; + let deltaDays; ({ deltaDays, @@ -1083,6 +1092,14 @@ export const ES = ObjectAssign({}, ES2019, { throw new Error('assert not reached'); } + days *= sign; + hours *= sign; + minutes *= sign; + seconds *= sign; + milliseconds *= sign; + microseconds *= sign; + nanoseconds *= sign; + return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; }, @@ -1172,7 +1189,18 @@ export const ES = ObjectAssign({}, ES2019, { } }, - DifferenceDate: (smaller, larger, largestUnit = 'days') => { + DifferenceDate: (one, two, largestUnit = 'days') => { + let larger, smaller, sign; + const TemporalDate = GetIntrinsic('%Temporal.Date%'); + if (TemporalDate.compare(one, two) < 0) { + smaller = one; + larger = two; + sign = 1; + } else { + smaller = two; + larger = one; + sign = -1; + } let years = larger.year - smaller.year; let weeks = 0; let months, days; @@ -1225,15 +1253,28 @@ export const ES = ObjectAssign({}, ES2019, { default: throw new Error('assert not reached'); } + years *= sign; + months *= sign; + weeks *= sign; + days *= sign; return { years, months, weeks, days }; }, - DifferenceTime(earlier, later) { - let hours = later.hour - earlier.hour; - let minutes = later.minute - earlier.minute; - let seconds = later.second - earlier.second; - let milliseconds = later.millisecond - earlier.millisecond; - let microseconds = later.microsecond - earlier.microsecond; - let nanoseconds = later.nanosecond - earlier.nanosecond; + DifferenceTime(one, two) { + let hours = two.hour - one.hour; + let minutes = two.minute - one.minute; + let seconds = two.second - one.second; + let milliseconds = two.millisecond - one.millisecond; + let microseconds = two.microsecond - one.microsecond; + let nanoseconds = two.nanosecond - one.nanosecond; + + const sign = ES.DurationSign(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + hours *= sign; + minutes *= sign; + seconds *= sign; + milliseconds *= sign; + microseconds *= sign; + nanoseconds *= sign; + let deltaDays = 0; ({ deltaDays, @@ -1244,6 +1285,15 @@ export const ES = ObjectAssign({}, ES2019, { microsecond: microseconds, nanosecond: nanoseconds } = ES.BalanceTime(hours, minutes, seconds, milliseconds, microseconds, nanoseconds)); + + deltaDays *= sign; + hours *= sign; + minutes *= sign; + seconds *= sign; + milliseconds *= sign; + microseconds *= sign; + nanoseconds *= sign; + return { deltaDays, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; }, AddDate: (year, month, day, years, months, weeks, days, disambiguation) => { diff --git a/polyfill/lib/time.mjs b/polyfill/lib/time.mjs index 8e3ea5bdbd..2b31994228 100644 --- a/polyfill/lib/time.mjs +++ b/polyfill/lib/time.mjs @@ -218,8 +218,6 @@ export class Time { if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver'); if (!ES.IsTemporalTime(other)) throw new TypeError('invalid Time object'); const largestUnit = ES.ToLargestTemporalUnit(options, 'hours'); - const comparison = Time.compare(this, other); - if (comparison < 0) throw new RangeError('other instance cannot be larger than `this`'); let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.DifferenceTime(other, this); ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceDuration( 0, diff --git a/polyfill/lib/yearmonth.mjs b/polyfill/lib/yearmonth.mjs index ddeccb6806..feb7b8a088 100644 --- a/polyfill/lib/yearmonth.mjs +++ b/polyfill/lib/yearmonth.mjs @@ -123,15 +123,13 @@ export class YearMonth { other = new Date(GetSlot(other, ISO_YEAR), GetSlot(other, ISO_MONTH), calendar, GetSlot(other, REF_ISO_DAY)); } const largestUnit = ES.ToLargestTemporalUnit(options, 'years', ['weeks', 'days', 'hours', 'minutes', 'seconds']); - const comparison = YearMonth.compare(this, other); - if (comparison < 0) throw new RangeError('other instance cannot be larger than `this`'); - const smallerFields = ES.ToTemporalYearMonthRecord(other); - const largerFields = ES.ToTemporalYearMonthRecord(this); + const otherFields = ES.ToTemporalYearMonthRecord(other); + const thisFields = ES.ToTemporalYearMonthRecord(this); const TemporalDate = GetIntrinsic('%Temporal.Date%'); - const smaller = calendar.dateFromFields({ ...smallerFields, day: 1 }, {}, TemporalDate); - const larger = calendar.dateFromFields({ ...largerFields, day: 1 }, {}, TemporalDate); - return calendar.difference(smaller, larger, { ...options, largestUnit }); + const otherDate = calendar.dateFromFields({ ...otherFields, day: 1 }, {}, TemporalDate); + const thisDate = calendar.dateFromFields({ ...thisFields, day: 1 }, {}, TemporalDate); + return calendar.difference(otherDate, thisDate, { ...options, largestUnit }); } equals(other) { if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver'); diff --git a/polyfill/test/absolute.mjs b/polyfill/test/absolute.mjs index f28906f983..afa7698f49 100644 --- a/polyfill/test/absolute.mjs +++ b/polyfill/test/absolute.mjs @@ -435,7 +435,8 @@ describe('Absolute', () => { const earlier = Absolute.from('1976-11-18T15:23:30.123456789Z'); const later = Absolute.from('2019-10-29T10:46:38.271986102Z'); const diff = later.difference(earlier); - it('throws if out of order', () => throws(() => earlier.difference(later), RangeError)); + it(`(${earlier}).difference(${later}) == (${later}).difference(${earlier}).negated()`, () => + equal(`${earlier.difference(later)}`, `${diff.negated()}`)); it(`(${earlier}).plus(${diff}) == (${later})`, () => assert(earlier.plus(diff).equals(later))); it(`(${later}).minus(${diff}) == (${earlier})`, () => assert(later.minus(diff).equals(earlier))); it("doesn't cast argument", () => { diff --git a/polyfill/test/datetime.mjs b/polyfill/test/datetime.mjs index 259c8b05e8..9f69a7ae61 100644 --- a/polyfill/test/datetime.mjs +++ b/polyfill/test/datetime.mjs @@ -319,7 +319,8 @@ describe('DateTime', () => { const later = DateTime.from('2019-10-29T10:46:38.271986102'); ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'].forEach((largestUnit) => { const diff = later.difference(earlier, { largestUnit }); - it('throws if out of order', () => throws(() => earlier.difference(later), RangeError)); + it(`(${earlier}).difference(${later}) == (${later}).difference(${earlier}).negated()`, () => + equal(`${earlier.difference(later, { largestUnit })}`, `${diff.negated()}`)); it(`(${earlier}).plus(${diff}) == (${later})`, () => assert(earlier.plus(diff).equals(later))); it(`(${later}).minus(${diff}) == (${earlier})`, () => assert(later.minus(diff).equals(earlier))); it('symmetrical with regard to negative durations', () => { diff --git a/polyfill/test/time.mjs b/polyfill/test/time.mjs index 56171ce449..224ad08fd5 100644 --- a/polyfill/test/time.mjs +++ b/polyfill/test/time.mjs @@ -215,9 +215,7 @@ describe('Time', () => { const duration = time.difference(two); equal(`${duration}`, 'PT1H53M'); }); - it('reverse argument order will throw', () => { - throws(() => one.difference(time, { largestUnit: 'minutes' }), RangeError); - }); + it(`(${two}).difference(${time}) => -PT1H53M`, () => equal(`${two.difference(time)}`, '-PT1H53M')); it("doesn't cast argument", () => { throws(() => time.difference({ hour: 16, minute: 34 }), TypeError); throws(() => time.difference('16:34'), TypeError); diff --git a/polyfill/test/yearmonth.mjs b/polyfill/test/yearmonth.mjs index 9002efc04c..c4cc6f7b42 100644 --- a/polyfill/test/yearmonth.mjs +++ b/polyfill/test/yearmonth.mjs @@ -176,7 +176,8 @@ describe('YearMonth', () => { const nov94 = YearMonth.from('1994-11'); const jun13 = YearMonth.from('2013-06'); const diff = jun13.difference(nov94); - it('throws if out of order', () => throws(() => nov94.difference(jun13), RangeError)); + it(`${nov94}.difference(${jun13}) == ${jun13}.difference(${nov94}).negated()`, () => + equal(`${nov94.difference(jun13)}`, `${diff.negated()}`)); it(`${nov94}.plus(${diff}) == ${jun13}`, () => nov94.plus(diff).equals(jun13)); it(`${jun13}.minus(${diff}) == ${nov94}`, () => jun13.minus(diff).equals(nov94)); it("doesn't cast argument", () => { diff --git a/spec/absolute.html b/spec/absolute.html index decec58c11..d84f7efe3a 100644 --- a/spec/absolute.html +++ b/spec/absolute.html @@ -288,11 +288,7 @@

Temporal.Absolute.prototype.difference ( _other_ [ , _options_ ] )

1. Perform ? RequireInternalSlot(_absolute_, [[InitializedTemporalAbsolute]]). 1. Perform ? RequireInternalSlot(_other_, [[InitializedTemporalAbsolute]]). 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"years"*, *"months"*, *"weeks"* », *"seconds"*). - 1. If ! CompareTemporalAbsolute(_absolute_, _other_) < 0, then - 1. Throw a *RangeError* exception. - 1. Let _greater_ be _absolute_. - 1. Let _smaller_ be _otherAbsolute_. - 1. Let _ns_ be _greater_.[[Nanoseconds]] - _smaller_.[[Nanoseconds]]. + 1. Let _ns_ be _absolute_.[[Nanoseconds]] - _otherAbsolute_.[[Nanoseconds]]. 1. Let _result_ be ! BalanceDuration(0, 0, 0, 0, 0, 0, _ns_, _largestUnit_). 1. Return ? CreateTemporalDuration(0, 0, 0, _result_.[[Days]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]). diff --git a/spec/date.html b/spec/date.html index bcdc38fd04..15fdfe91d8 100644 --- a/spec/date.html +++ b/spec/date.html @@ -369,11 +369,7 @@

Temporal.Date.prototype.difference ( _other_ [ , _options_ ] )

1. Perform ? RequireInternalSlot(_temporalDate_, [[InitializedTemporalDate]]). 1. Perform ? RequireInternalSlot(_other_, [[InitializedTemporalDate]]). 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"hours"*, *"minutes"*, *"seconds"* », *"days"*). - 1. If ! CompareTemporalDate(_temporalDate_, _other_) < 0, then - 1. Throw a *RangeError* exception. - 1. Let _greater_ be _temporalDate_. - 1. Let _smaller_ be _other_. - 1. Let _result_ be ? DifferenceDate(_smaller_, _greater_, _largestUnit_). + 1. Let _result_ be ? DifferenceDate(_other_, _temporalDate_, _largestUnit_). 1. Return ? CreateTemporalDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], 0, 0, 0, 0, 0, 0). @@ -520,11 +516,19 @@

CreateTemporalDateFromStatic ( _constructor_, _year_, _month_, _day_ )

-

DifferenceDate ( _smaller_, _greater_, _largestUnit_ )

+

DifferenceDate ( _one_, _two_, _largestUnit_ )

1. Assert: _largestUnit_ is one of *"years"*, *"months"*, *"weeks"*, *"days"*, *"hours"*, *"minutes"*, or *"seconds"*. 1. If _largestUnit_ is not *"years"*, *"months"*, or *"weeks"*, then 1. Set _largestUnit_ to `"days"`. + 1. If ! CompareTemporalDate(_one_, _two_) < 0, then + 1. Let _smaller_ be _one_. + 1. Let _greater_ be _two_. + 1. Let _sign_ be 1. + 1. Else, + 1. Let _smaller_ be _two_. + 1. Let _greater_ be _one_. + 1. Let _sign_ be −1. 1. Let _years_ be _greater_.[[ISOYear]] − _smaller_.[[ISOYear]]. 1. If _largestUnit_ is *"days"* or *"weeks"*, then 1. Let _weeks_ be 0. @@ -539,8 +543,8 @@

DifferenceDate ( _smaller_, _greater_, _largestUnit_ )

1. Return the Record { [[Years]]: 0, [[Months]]: 0, - [[Weeks]]: _weeks_, - [[Days]]: _days_ + [[Weeks]]: _weeks_ × _sign_, + [[Days]]: _days_ × _sign_ }. 1. Let _months_ be _greater_.[[ISOMonth]] − _smaller_.[[ISOMonth]]. 1. Let _balanceResult_ be ? BalanceDurationDate(_years_, _months_, _smaller_.[[ISOYear]], _smaller_.[[ISOMonth]], _smaller_.[[ISODay]]). @@ -559,10 +563,10 @@

DifferenceDate ( _smaller_, _greater_, _largestUnit_ )

1. Set _months_ to _years_ × 12. 1. Set _years_ to 0. 1. Return the Record { - [[Years]]: _years_, - [[Months]]: _months_, + [[Years]]: _years_ × _sign_, + [[Months]]: _months_ × _sign_, [[Weeks]]: 0, - [[Days]]: _days_ + [[Days]]: _days_ × _sign_ }.
diff --git a/spec/datetime.html b/spec/datetime.html index 2654c362cb..065038bbfd 100644 --- a/spec/datetime.html +++ b/spec/datetime.html @@ -423,17 +423,13 @@

Temporal.DateTime.prototype.difference ( _other_ [ , _options_ ] )

1. Perform ? RequireInternalSlot(_dateTime_, [[InitializedTemporalDateTime]]). 1. Perform ? RequireInternalSlot(_other_, [[InitializedTemporalDateTime]]). 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « », *"days"*). - 1. If ! CompareTemporalDateTime(_dateTime_, _other_) < 0, then - 1. Throw a *RangeError* exception. - 1. Let _greater_ be _dateTime_. - 1. Let _smaller_ be _other_. - 1. Let _timeDifference_ be ! DifferenceTime(_smaller_, _greater_). + 1. Let _timeDifference_ be ! DifferenceTime(_other_, _dateTime_). 1. Let _balanceResult_ be ? BalanceDate(_greater_.[[ISOYear]], _greater_.[[ISOMonth]], _greater_.[[ISODay]] + _timeDifference_.[[Days]]). 1. Let _dateDifference_ be ! DifferenceDate(_smaller_, _balanceResult_, _largestUnit_). 1. Let _days_ be _dateDifference_.[[Days]]. - 1. Let _hours_ be _timeDifference_.[[Hour]]. - 1. Let _minutes_ be _timeDifference_.[[Minute]]. - 1. Let _seconds_ be _timeDifference_.[[Second]]. + 1. Let _hours_ be _timeDifference_.[[Hours]]. + 1. Let _minutes_ be _timeDifference_.[[Minutes]]. + 1. Let _seconds_ be _timeDifference_.[[Seconds]]. 1. If _largestUnit_ is `"hours"`, `"minutes"`, or `"seconds"`, then 1. Set _hours_ to _hours_ + 24 × _days_. 1. Set _days_ to 0. @@ -443,7 +439,7 @@

Temporal.DateTime.prototype.difference ( _other_ [ , _options_ ] )

1. If _largestUnit_ is `"seconds"`, then 1. Set _seconds_ to _seconds_ + 60 × _minutes_. 1. Set _minutes_ to 0. - 1. Return ? CreateTemporalDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _days_, _hours_, _minutes_, _seconds_, _timeDifference_.[[Millisecond]], _timeDifference_.[[Microsecond]], _timeDifference_.[[Nanosecond]]). + 1. Return ? CreateTemporalDuration(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _days_, _hours_, _minutes_, _seconds_, _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]]). diff --git a/spec/duration.html b/spec/duration.html index bb79e09182..7ec1df752c 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -590,7 +590,8 @@

CreateTemporalDurationFromStatic ( _constructor_, _years_, _months_, _weeks_

BalanceDuration ( _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, _largestUnit_ )

1. Assert: _largestUnit_ is one of *"days"*, *"hours"*, *"minutes"*, or *"seconds"*. - 1. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Let _sign_ be ! DurationSign(0, 0, 0, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Let _bt_ be ? BalanceTime(abs(_hours_), abs(_minutes_), abs(_seconds_), abs(_milliseconds_), abs(_microseconds_), abs(_nanoseconds_)). 1. Increment _days_ by _bt_.[[Days]]. 1. Set _hours_ to _bt_.[[Hour]]. 1. Set _minutes_ to _bt_.[[Minute]]. @@ -605,13 +606,13 @@

BalanceDuration ( _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _mi 1. Increment _seconds_ by 60 × _minutes_. 1. Set _minutes_ to 0. 1. Return the new Record { - [[Days]]: _days_, - [[Hours]]: _hours_, - [[Minutes]]: _minutes_, - [[Seconds]]: _seconds_, - [[Milliseconds]]: _bt_.[[Millisecond]], - [[Microseconds]]: _bt_.[[Microsecond]], - [[Nanoseconds]]: _bt_.[[Nanosecond]]. + [[Days]]: _days_ × _sign_, + [[Hours]]: _hours_ × _sign_, + [[Minutes]]: _minutes_ × _sign_, + [[Seconds]]: _seconds_ × _sign_, + [[Milliseconds]]: _bt_.[[Millisecond]] × _sign_, + [[Microseconds]]: _bt_.[[Microsecond]] × _sign_, + [[Nanoseconds]]: _bt_.[[Nanosecond]] × _sign_ }. diff --git a/spec/time.html b/spec/time.html index ffc36cca3b..5215ce236a 100644 --- a/spec/time.html +++ b/spec/time.html @@ -293,15 +293,8 @@

Temporal.Time.prototype.difference ( _other_ [ , _options_ ] )

1. Perform ? RequireInternalSlot(_temporalTime_, [[InitializedTemporalTime]]). 1. Perform ? RequireInternalSlot(_other_, [[InitializedTemporalTime]]). 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « », *"hours"*). - 1. If ! CompareTemporalTime(_temporalTime_, _other_) < 0, then - 1. Throw a *RangeError* exception. - 1. Let _greater_ be _temporalTime_. - 1. Let _smaller_ be _other_. - 1. Let _result_ be ! DifferenceTime(_smaller_, _greater_). - 1. If _result_.[[Hour]] ≥ 12, then - 1. Set _result_ to ! BalanceDuration(0, 24 − _result_.[[Hour]], −_result_.[[Minute]], −_result_.[[Second]], −_result_.[[Millisecond]], −_result_.[[Microsecond]], −_result_.[[Nanosecond]], _largestUnit_). - 1. Else, - 1. Set _result_ to ! BalanceDuration(0, _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _largestUnit_). + 1. Let _result_ be ! DifferenceTime(_other_, _temporalTime_). + 1. Set _result_ to ! BalanceDuration(0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]], _largestUnit_). 1. Return ? CreateTemporalDuration(0, 0, 0, 0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]).
@@ -414,15 +407,25 @@

Temporal.Time.prototype.valueOf ( )

Abstract operations

-

DifferenceTime ( _smaller_, _greater_ )

- - 1. Let _hour_ be _greater_.[[Hour]] - _smaller_.[[Hour]]. - 1. Let _minute_ be _greater_.[[Minute]] - _smaller_.[[Minute]]. - 1. Let _second_ be _greater_.[[Second]] - _smaller_.[[Second]]. - 1. Let _millisecond_ be _greater_.[[Millisecond]] - _smaller_.[[Millisecond]]. - 1. Let _microsecond_ be _greater_.[[Microsecond]] - _smaller_.[[Microsecond]]. - 1. Let _nanosecond_ be _greater_.[[Nanosecond]] - _smaller_.[[Nanosecond]]. - 1. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). +

DifferenceTime ( _one_, _two_ )

+ + 1. Let _hours_ be _two_.[[Hour]] - _one_.[[Hour]]. + 1. Let _minutes_ be _two_.[[Minute]] - _one_.[[Minute]]. + 1. Let _seconds_ be _two_.[[Second]] - _one_.[[Second]]. + 1. Let _milliseconds_ be _two_.[[Millisecond]] - _one_.[[Millisecond]]. + 1. Let _microseconds_ be _two_.[[Microsecond]] - _one_.[[Microsecond]]. + 1. Let _nanoseconds_ be _two_.[[Nanosecond]] - _one_.[[Nanosecond]]. + 1. Let _sign_ be ! DurationSign(0, 0, 0, 0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Return the new Record { + [[Days]]: _bt_.[[Days]] × _sign_, + [[Hours]]: _bt_.[[Hour]] × _sign_, + [[Minutes]]: _bt_.[[Minute]] × _sign_, + [[Seconds]]: _bt_.[[Second]] × _sign_, + [[Milliseconds]]: _bt_.[[Millisecond]] × _sign_, + [[Microseconds]]: _bt_.[[Microsecond]] × _sign_, + [[Nanoseconds]]: _bt_.[[Nanosecond]] × _sign_ + }.
diff --git a/spec/yearmonth.html b/spec/yearmonth.html index 58096d5be1..62a0ec396d 100644 --- a/spec/yearmonth.html +++ b/spec/yearmonth.html @@ -267,12 +267,8 @@

Temporal.YearMonth.prototype.difference ( _other_ [ , _options_ ] )

1. Perform ? RequireInternalSlot(_yearMonth_, [[InitializedTemporalYearMonth]]). 1. Perform ? RequireInternalSlot(_other_, [[InitializedTemporalYearMonth]]). 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « *"weeks"*, *"days"*, *"hours"*, *"minutes"*, *"seconds"* », *"years"*). - 1. If ! CompareTemporalYearMonth(_yearMonth_, _other_) < 0, then - 1. Throw a *RangeError* exception. - 1. Let _greater_ be _yearMonth_. - 1. Let _smaller_ be _other_. - 1. Let _years_ be _greater_.[[ISOYear]] - _smaller_.[[ISOYear]]. - 1. Let _months_ be _greater_.[[ISOMonth]] - _smaller_.[[ISOMonth]]. + 1. Let _years_ be _yearMonth_.[[ISOYear]] - _other_.[[ISOYear]]. + 1. Let _months_ be _yearMonth_.[[ISOMonth]] - _other_.[[ISOMonth]]. 1. If _months_ < 0, then 1. Set _years_ to _years_ - 1. 1. Set _months_ to _months_ + 12.