Skip to content

Commit

Permalink
Add equal() method
Browse files Browse the repository at this point in the history
This adds an equal() method to Absolute, Date, DateTime, MonthDay, Time,
and YearMonth. Currently `a.equal(b)` is equivalent to
`Temporal.X.compare(a, b) === 0`, but after calendars are added, equal()
should additionally return false if the calendars in the two objects are
different.

Duration doesn't have an equal() method at this time because, does
`{ minutes: 1, seconds: 30 }` equal `{ seconds: 90 }`? TimeZone doesn't
have one either because it's simple enough to compare the .name
property.
  • Loading branch information
ptomato committed May 19, 2020
1 parent 63d36e9 commit 4a7d73e
Show file tree
Hide file tree
Showing 43 changed files with 751 additions and 21 deletions.
21 changes: 21 additions & 0 deletions docs/absolute.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,27 @@ epoch.inTimeZone(utc).difference(billion.inTimeZone(utc), { largestUnit: 'years'
// => P31Y8M8DT1H46M40S
```

### absolute.**equal**(_other_: Temporal.Absolute) : boolean

**Parameters:**
- `other` (`Temporal.Absolute`): Another time to compare.

**Returns:** `true` if `absolute` and `other` are equal, or `false` if not.

Compares two `Temporal.Absolute` objects for equality.

This function exists because it's not possible to compare using `absolute == other` or `absolute === other`, due to ambiguity in the primitive representation and between Temporal types.

If you don't need to know the order in which the two times occur, then this function may be less typing and more efficient than `Temporal.Absolute.compare`.

Example usage:
```javascript
one = Temporal.Absolute.fromEpochSeconds(1.0e9);
two = Temporal.Absolute.fromEpochSeconds(1.1e9);
one.equal(two) // => false
one.equal(one) // => true
```

### absolute.**toString**(_timeZone_?: Temporal.TimeZone | string) : string

**Parameters:**
Expand Down
22 changes: 22 additions & 0 deletions docs/date.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,28 @@ date.withTime(midnight).difference(other.withTime(midnight), { largestUnit: 'hou
// => PT109032H
```

### date.**equal**(_other_: Temporal.Date) : boolean

**Parameters:**
- `other` (`Temporal.Date`): Another date to compare.

**Returns:** `true` if `date` and `other` are equal, or `false` if not.

Compares two `Temporal.Date` objects for equality.

This function exists because it's not possible to compare using `date == other` or `date === other`, due to ambiguity in the primitive representation and between Temporal types.

The difference with `Temporal.Date.compare` is that this function will return `false` if the two dates are expressed in different calendar systems.
Even if you are using the same calendar system, if you don't need to know the order in which the two dates occur, then this function may be less typing and more efficient than `Temporal.Date.compare`.

Example usage:
```javascript
date = Temporal.Date.from('2006-08-24');
other = Temporal.Date.from('2019-01-31');
date.equal(other) // => false
date.equal(date) // => true
```

### date.**toString**() : string

**Returns:** a string in the ISO 8601 date format representing `date`.
Expand Down
22 changes: 22 additions & 0 deletions docs/datetime.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,28 @@ feb1.difference(mar1); // => P29D
feb1.difference(mar1, { largestUnit: 'months' }); // => P1M
```

### datetime.**equal**(_other_: Temporal.DateTime) : boolean

**Parameters:**
- `other` (`Temporal.DateTime`): Another date/time to compare.

**Returns:** `true` if `datetime` and `other` are equal, or `false` if not.

Compares two `Temporal.DateTime` objects for equality.

This function exists because it's not possible to compare using `datetime == other` or `datetime === other`, due to ambiguity in the primitive representation and between Temporal types.

The difference with `Temporal.DateTime.compare` is that this function will return `false` if the two dates/times are expressed in different calendar systems.
Even if you are using the same calendar system, if you don't need to know the order in which the two dates/times occur, then this function may be less typing and more efficient than `Temporal.DateTime.compare`.

Example usage:
```javascript
dt1 = Temporal.DateTime.from('1995-12-07T03:24:30.000003500');
dt2 = Temporal.DateTime.from('2019-01-31T15:30');
dt1.equal(dt2) // => false
dt1.equal(dt1) // => true
```

### datetime.**toString**() : string

**Returns:** a string in the ISO 8601 date format representing `datetime`.
Expand Down
21 changes: 21 additions & 0 deletions docs/monthday.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,27 @@ md.with({ day: 31 }) // => 11-30
Temporal.MonthDay.from('02-01').with({ day: 31 }); // => 02-29
```

### monthDay.**equal**(_other_: Temporal.MonthDay) : boolean

**Parameters:**
- `other` (`Temporal.MonthDay`): Another month-day to compare.

**Returns:** `true` if `monthDay` and `other` are equal, or `false` if not.

Compares two `Temporal.MonthDay` objects for equality.

This function exists because it's not possible to compare using `monthDay == other` or `monthDay === other`, due to ambiguity in the primitive representation and between Temporal types.

Note that this function will return `false` if the two inputs are expressed in different calendar systems.

Example usage:
```javascript
dt1 = Temporal.DateTime.from('1995-12-07T03:24:30.000003500');
dt2 = Temporal.DateTime.from('2019-01-31T15:30');
dt1.equal(dt2) // => false
dt1.equal(dt1) // => true
```

### monthDay.**toString**() : string

**Returns:** a string in the ISO 8601 date format representing `monthDay`.
Expand Down
21 changes: 21 additions & 0 deletions docs/time.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,27 @@ time.difference(Temporal.Time.from('20:13:20.971398099')) // => PT34M11.9030518
Temporal.Time.from('01:00').difference(Temporal.Time.from('23:00')) // => P2H
```

### time.**equal**(_other_: Temporal.Time) : boolean

**Parameters:**
- `other` (`Temporal.Time`): Another time to compare.

**Returns:** `true` if `time` and `other` are equal, or `false` if not.

Compares two `Temporal.Time` objects for equality.

This function exists because it's not possible to compare using `time == other` or `time === other`, due to ambiguity in the primitive representation and between Temporal types.

If you don't need to know the order in which the two dates occur, then this function may be less typing and more efficient than `Temporal.Time.compare`.

Example usage:
```javascript
time = Temporal.Time.from('19:39:09.068346205');
other = Temporal.Time.from('20:13:20.971398099');
time.equal(other) // => false
time.equal(time) // => true
```

### time.**toString**() : string

**Returns:** a string in the ISO 8601 time format representing `time`.
Expand Down
22 changes: 22 additions & 0 deletions docs/yearmonth.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,28 @@ ym.difference(other, { largestUnit: 'months' }) // => P154M
ym.withDay(1).difference(other.withDay(1), { largestUnit: 'days' }); // => P4687D
```

### yearMonth.**equal**(_other_: Temporal.YearMonth) : boolean

**Parameters:**
- `other` (`Temporal.YearMonth`): Another month to compare.

**Returns:** `true` if `yearMonth` and `other` are equal, or `false` if not.

Compares two `Temporal.YearMonth` objects for equality.

This function exists because it's not possible to compare using `yearMonth == other` or `yearMonth === other`, due to ambiguity in the primitive representation and between Temporal types.

Note that this function will return `false` if the two months are expressed in different calendar systems.
Even if you are using the same calendar system, if you don't need to know the order in which the two months occur, then this function may be less typing and more efficient than `Temporal.YearMonth.compare`.

Example usage:
```javascript
ym = Temporal.YearMonth.from('2019-06');
other = Temporal.YearMonth.from('2006-08');
ym.equal(other) // => false
ym.equal(ym) // => true
```

### yearMonth.**toString**() : string

**Returns:** a string in the ISO 8601 date format representing `yearMonth`.
Expand Down
7 changes: 7 additions & 0 deletions polyfill/lib/absolute.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ export class Absolute {
);
return new Duration(0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
equal(other) {
if (!ES.IsTemporalAbsolute(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalAbsolute(other)) throw new TypeError('invalid Absolute object');
const one = GetSlot(this, EPOCHNANOSECONDS);
const two = GetSlot(other, EPOCHNANOSECONDS);
return bigInt(one).equals(two);
}
toString(temporalTimeZoneLike = 'UTC') {
if (!ES.IsTemporalAbsolute(this)) throw new TypeError('invalid receiver');
const timeZone = ES.ToTemporalTimeZone(temporalTimeZoneLike);
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/date.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ export class Date {
const Duration = GetIntrinsic('%Temporal.Duration%');
return new Duration(years, months, days, 0, 0, 0, 0, 0, 0);
}
equal(other) {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalDate(other)) throw new TypeError('invalid Date object');
for (const slot of [YEAR, MONTH, DAY]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}
toString() {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
let year = ES.ISOYearString(GetSlot(this, YEAR));
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/datetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,16 @@ export class DateTime {
const Duration = GetIntrinsic('%Temporal.Duration%');
return new Duration(years, months, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
equal(other) {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalDateTime(other)) throw new TypeError('invalid Date object');
for (const slot of [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}
toString() {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
let year = ES.ISOYearString(GetSlot(this, YEAR));
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/monthday.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ export class MonthDay {
if (!ES.IsTemporalMonthDay(result)) throw new TypeError('invalid result');
return result;
}
equal(other) {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalMonthDay(other)) throw new TypeError('invalid MonthDay object');
for (const slot of [MONTH, DAY]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}
toString() {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
let month = ES.ISODateTimePartString(GetSlot(this, MONTH));
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/time.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ export class Time {
const Duration = GetIntrinsic('%Temporal.Duration%');
return new Duration(0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
equal(other) {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalTime(other)) throw new TypeError('invalid Time object');
for (const slot of [HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}

toString() {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/yearmonth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ export class YearMonth {
const Duration = GetIntrinsic('%Temporal.Duration%');
return new Duration(years, months);
}
equal(other) {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
if (!ES.IsTemporalYearMonth(other)) throw new TypeError('invalid YearMonth object');
for (const slot of [YEAR, MONTH]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}
toString() {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
let year = ES.ISOYearString(GetSlot(this, YEAR));
Expand Down
19 changes: 19 additions & 0 deletions polyfill/test/Absolute/prototype/equal/argument-wrong-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-temporal.absolute.prototype.equal
features: [Symbol]
---*/

const instance = Temporal.Absolute.fromEpochSeconds(0);

assert.throws(TypeError, () => instance.equal(undefined), "undefined");
assert.throws(TypeError, () => instance.equal(null), "null");
assert.throws(TypeError, () => instance.equal(true), "true");
assert.throws(TypeError, () => instance.equal(""), "empty string");
assert.throws(TypeError, () => instance.equal(Symbol()), "symbol");
assert.throws(TypeError, () => instance.equal(1), "1");
assert.throws(TypeError, () => instance.equal({}), "plain object");
assert.throws(TypeError, () => instance.equal(Temporal.Absolute), "Temporal.Absolute");
assert.throws(TypeError, () => instance.equal(Temporal.Absolute.prototype), "Temporal.Absolute.prototype");
21 changes: 21 additions & 0 deletions polyfill/test/Absolute/prototype/equal/branding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-temporal.absolute.prototype.equal
features: [Symbol]
---*/

const equal = Temporal.Absolute.prototype.equal;

assert.sameValue(typeof equal, "function");

assert.throws(TypeError, () => equal.call(undefined), "undefined");
assert.throws(TypeError, () => equal.call(null), "null");
assert.throws(TypeError, () => equal.call(true), "true");
assert.throws(TypeError, () => equal.call(""), "empty string");
assert.throws(TypeError, () => equal.call(Symbol()), "symbol");
assert.throws(TypeError, () => equal.call(1), "1");
assert.throws(TypeError, () => equal.call({}), "plain object");
assert.throws(TypeError, () => equal.call(Temporal.Absolute), "Temporal.Absolute");
assert.throws(TypeError, () => equal.call(Temporal.Absolute.prototype), "Temporal.Absolute.prototype");
19 changes: 19 additions & 0 deletions polyfill/test/Absolute/prototype/equal/prop-desc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
includes: [propertyHelper.js]
---*/

const { Absolute } = Temporal;
assert.sameValue(
typeof Absolute.prototype.equal,
"function",
"`typeof Absolute.prototype.equal` is `function`"
);

verifyProperty(Absolute.prototype, "equal", {
writable: true,
enumerable: false,
configurable: true,
});
19 changes: 19 additions & 0 deletions polyfill/test/Date/prototype/equal/argument-wrong-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-temporal.date.prototype.equal
features: [Symbol]
---*/

const instance = Temporal.Date.from({ year: 2000, month: 5, day: 2 });

assert.throws(TypeError, () => instance.equal(undefined), "undefined");
assert.throws(TypeError, () => instance.equal(null), "null");
assert.throws(TypeError, () => instance.equal(true), "true");
assert.throws(TypeError, () => instance.equal(""), "empty string");
assert.throws(TypeError, () => instance.equal(Symbol()), "symbol");
assert.throws(TypeError, () => instance.equal(1), "1");
assert.throws(TypeError, () => instance.equal({}), "plain object");
assert.throws(TypeError, () => instance.equal(Temporal.Date), "Temporal.Date");
assert.throws(TypeError, () => instance.equal(Temporal.Date.prototype), "Temporal.Date.prototype");
21 changes: 21 additions & 0 deletions polyfill/test/Date/prototype/equal/branding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
esid: sec-temporal.date.prototype.equal
features: [Symbol]
---*/

const equal = Temporal.Date.prototype.equal;

assert.sameValue(typeof equal, "function");

assert.throws(TypeError, () => equal.call(undefined), "undefined");
assert.throws(TypeError, () => equal.call(null), "null");
assert.throws(TypeError, () => equal.call(true), "true");
assert.throws(TypeError, () => equal.call(""), "empty string");
assert.throws(TypeError, () => equal.call(Symbol()), "symbol");
assert.throws(TypeError, () => equal.call(1), "1");
assert.throws(TypeError, () => equal.call({}), "plain object");
assert.throws(TypeError, () => equal.call(Temporal.Date), "Temporal.Date");
assert.throws(TypeError, () => equal.call(Temporal.Date.prototype), "Temporal.Date.prototype");
18 changes: 18 additions & 0 deletions polyfill/test/Date/prototype/equal/prop-desc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2020 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.

/*---
includes: [propertyHelper.js]
---*/

assert.sameValue(
typeof Temporal.Date.prototype.equal,
"function",
"`typeof Date.prototype.equal` is `function`"
);

verifyProperty(Temporal.Date.prototype, "equal", {
writable: true,
enumerable: false,
configurable: true,
});
Loading

0 comments on commit 4a7d73e

Please sign in to comment.