diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js new file mode 100644 index 00000000000..75912cea426 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-object.js @@ -0,0 +1,84 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: Tests that objects can be compared for equality +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +const objectsEqualUTC = [ + new Temporal.TimeZone("UTC"), + new CustomTimeZone("UTC"), + { id: "UTC", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + new Temporal.ZonedDateTime(0n, "UTC") +]; + +const tzUTC = new Temporal.TimeZone("UTC"); + +for (const object of objectsEqualUTC) { + const result = tzUTC.equals(object); + assert.sameValue(result, true); +} + +const objectsEqual0000 = [ + new Temporal.TimeZone("+00:00"), + new Temporal.TimeZone("+0000"), + new Temporal.TimeZone("+00"), + new CustomTimeZone("+00:00"), + new CustomTimeZone("+0000"), + new CustomTimeZone("+00"), + { id: "+00:00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "+0000", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "+00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + new Temporal.ZonedDateTime(0n, "+00:00"), + new Temporal.ZonedDateTime(0n, "+0000"), + new Temporal.ZonedDateTime(0n, "+00") +]; + +const tz0000 = new Temporal.TimeZone("+00:00"); + +for (const object of objectsEqual0000) { + const result = tz0000.equals(object); + assert.sameValue(result, true); +} + +const objectsNotEqual = [ + new Temporal.TimeZone("+00:00"), + new CustomTimeZone("+00:00"), + { id: "+00:00", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "Etc/Custom", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + new Temporal.ZonedDateTime(0n, "+00:00") +]; + +for (const object of objectsNotEqual) { + const result = tzUTC.equals(object); + assert.sameValue(result, false); +} + +// Custom object IDs are compared case-sensitively +const classInstanceCustomId = new CustomTimeZone("Moon/Cheese"); +const classInstanceSameCaseCustomId = new CustomTimeZone("Moon/Cheese"); +const classInstanceDifferentCaseCustomId = new CustomTimeZone("MoOn/CHEESe"); + +const plainObjectSameCaseCustomId = { id: "Moon/Cheese", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }; +const plainObjectDifferentCaseCustomId = { + id: "MoOn/CHEESe", + getPossibleInstantsFor: null, + getOffsetNanosecondsFor: null +}; + +assert.sameValue(classInstanceCustomId.equals(classInstanceSameCaseCustomId), true); +assert.sameValue(classInstanceCustomId.equals(classInstanceDifferentCaseCustomId), false); +assert.sameValue(classInstanceCustomId.equals(plainObjectSameCaseCustomId), true); +assert.sameValue(classInstanceCustomId.equals(plainObjectDifferentCaseCustomId), false); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js new file mode 100644 index 00000000000..9e79b41bc1e --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-primitive.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Exceptions thrown if a value is passed that converts to an invalid string +features: [Temporal] +---*/ + +const primitives = [ + undefined, + null, + true, + "string", + "local", + "Z", + "-00:00[UTC]", + "+00:01.1", + "-01.1", + "1994-11-05T08:15:30+25:00", + "1994-11-05T13:15:30-25:00", + 7, + 4.2, + 12n +]; + +const tzUTC = new Temporal.TimeZone("UTC"); +for (const primitive of primitives) { + assert.throws(typeof primitive === "string" ? RangeError : TypeError, () => tzUTC.equals(primitive)); +} + +const symbol = Symbol(); +assert.throws(TypeError, () => tzUTC.equals(symbol)); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js new file mode 100644 index 00000000000..6148da71006 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/argument-valid.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Built-in time zones are compared correctly out of valid strings +features: [Temporal] +---*/ + +const validsEqual = [ + ["+0330", "+03:30"], + ["-0650", "-06:50"], + ["-08", "-08:00"], + ["\u221201:00", "-01:00"], + ["\u22120650", "-06:50"], + ["\u221208", "-08:00"], + ["1994-11-05T08:15:30-05:00", "-05:00"], + ["1994-11-05T08:15:30\u221205:00", "-05:00"], + ["1994-11-05T13:15:30Z", "UTC"] +]; + +for (const [valid, canonical] of validsEqual) { + const tzValid = Temporal.TimeZone.from(valid); + const tzCanonical = Temporal.TimeZone.from(canonical); + assert.sameValue(tzValid.equals(canonical), true); + assert.sameValue(tzCanonical.equals(valid), true); +} + +const validsNotEqual = [ + ["+0330", "+03:31"], + ["-0650", "-06:51"], + ["-08", "-08:01"], + ["\u221201:00", "-01:01"], + ["\u22120650", "-06:51"], + ["\u221208", "-08:01"], + ["1994-11-05T08:15:30-05:00", "-05:01"], + ["1994-11-05T08:15:30\u221205:00", "-05:01"] +]; + +for (const [valid, canonical] of validsNotEqual) { + const tzValid = Temporal.TimeZone.from(valid); + const tzCanonical = Temporal.TimeZone.from(canonical); + assert.sameValue(tzValid.equals(canonical), false); + assert.sameValue(tzCanonical.equals(valid), false); +} diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/branding.js b/test/built-ins/Temporal/TimeZone/prototype/equals/branding.js new file mode 100644 index 00000000000..7b5ad93e178 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/branding.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const equals = Temporal.TimeZone.prototype.equals; + +assert.sameValue(typeof equals, "function"); + +const args = ["UTC"]; + +assert.throws(TypeError, () => equals.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => equals.apply(null, args), "null"); +assert.throws(TypeError, () => equals.apply(true, args), "true"); +assert.throws(TypeError, () => equals.apply("", args), "empty string"); +assert.throws(TypeError, () => equals.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => equals.apply(1, args), "1"); +assert.throws(TypeError, () => equals.apply({}, args), "plain object"); +assert.throws(TypeError, () => equals.apply(Temporal.TimeZone, args), "Temporal.TimeZone"); +assert.throws(TypeError, () => equals.apply(Temporal.TimeZone.prototype, args), "Temporal.TimeZone.prototype"); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/builtin.js b/test/built-ins/Temporal/TimeZone/prototype/equals/builtin.js new file mode 100644 index 00000000000..8eac773cd20 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/builtin.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: > + Tests that Temporal.TimeZone.prototype.equals + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.TimeZone.prototype.equals), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.TimeZone.prototype.equals), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.TimeZone.prototype.equals), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.TimeZone.prototype.equals.hasOwnProperty("prototype"), + false, "prototype property"); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/length.js b/test/built-ins/Temporal/TimeZone/prototype/equals/length.js new file mode 100644 index 00000000000..7d4d11fa6fa --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/length.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: Temporal.TimeZone.prototype.equals.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.TimeZone.prototype.equals, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/name.js b/test/built-ins/Temporal/TimeZone/prototype/equals/name.js new file mode 100644 index 00000000000..21f452ad877 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/name.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: Temporal.TimeZone.prototype.equals.name is "equals". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.TimeZone.prototype.equals, "name", { + value: "equals", + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js b/test/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js new file mode 100644 index 00000000000..b5d9cae6508 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/not-a-constructor.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: > + Temporal.TimeZone.prototype.equals does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.TimeZone.prototype.equals(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.TimeZone.prototype.equals), false, + "isConstructor(Temporal.TimeZone.prototype.equals)"); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js b/test/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js new file mode 100644 index 00000000000..8d0dfe1e0a0 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/prop-desc.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.prototype.equals +description: The "equals" property of Temporal.TimeZone.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.TimeZone.prototype.equals, + "function", + "`typeof TimeZone.prototype.equals` is `function`" +); + +verifyProperty(Temporal.TimeZone.prototype, "equals", { + writable: true, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js new file mode 100644 index 00000000000..75b5d613c7a --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.equals +description: Time zone names are case insensitive +features: [Temporal] +---*/ + +const timeZone = 'UtC'; +const result = Temporal.TimeZone.from(timeZone); +assert.sameValue(result.equals(timeZone), true); +assert.sameValue(result.equals("+00:00"), false); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js new file mode 100644 index 00000000000..3768834301e --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-datetime.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.equals +description: Conversion of ISO date-time strings to the argument of Temporal.TimeZone.prototype.equals +features: [Temporal] +---*/ + +let tzUTC = Temporal.TimeZone.from("UTC"); +let arg = "2021-08-19T17:30"; +assert.throws(RangeError, () => tzUTC.equals(arg), "bare date-time string is not a time zone"); + +arg = "2021-08-19T17:30-07:00:01"; +assert.throws(RangeError, () => tzUTC.equals(arg), "ISO string sub-minute offset is not OK as time zone"); + +arg = "2021-08-19T17:30Z"; +tzUTC = Temporal.TimeZone.from(arg); +assert.sameValue(tzUTC.equals(arg), true, "date-time + Z is UTC time zone"); + +arg = "2021-08-19T17:30-07:00"; +tzUTC = Temporal.TimeZone.from(arg); +assert.sameValue(tzUTC.equals(arg), true, "date-time + offset is the offset time zone"); + +arg = "2021-08-19T17:30[UTC]"; +tzUTC = Temporal.TimeZone.from(arg); +assert.sameValue(tzUTC.equals(arg), true, "date-time + IANA annotation is the IANA time zone"); + +arg = "2021-08-19T17:30Z[UTC]"; +tzUTC = Temporal.TimeZone.from(arg); +assert.sameValue(tzUTC.equals(arg), true, "date-time + Z + IANA annotation is the IANA time zone"); + +arg = "2021-08-19T17:30-07:00[UTC]"; +tzUTC = Temporal.TimeZone.from(arg); +assert.sameValue(tzUTC.equals(arg), true, "date-time + offset + IANA annotation is the IANA time zone"); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js new file mode 100644 index 00000000000..01dec3fd1f8 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-string-multiple-offsets.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Time zone strings with UTC offset fractional part are not confused with time fractional part +features: [Temporal] +---*/ + +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +const result = Temporal.TimeZone.from(timeZone); +assert.sameValue(result.equals("+01:46"), true, "Time zone string determined from bracket name"); diff --git a/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js new file mode 100644 index 00000000000..745e0a1d119 --- /dev/null +++ b/test/built-ins/Temporal/TimeZone/prototype/equals/timezone-wrong-type.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"] +]; + +const tzUTC = new Temporal.TimeZone("UTC"); + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws( + typeof timeZone === "string" ? RangeError : TypeError, + () => tzUTC.equals(timeZone), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "object not implementing time zone protocol"], + [new Temporal.Calendar("iso8601"), "calendar instance"] +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws( + TypeError, + () => tzUTC.equals(timeZone), + `${description} is not a valid object and does not convert to a string` + ); +} diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/argument-object.js b/test/intl402/Temporal/TimeZone/prototype/equals/argument-object.js new file mode 100644 index 00000000000..7f77b62f409 --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/argument-object.js @@ -0,0 +1,60 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Objects with IANA IDs are compared case-insensitively with their canonical IDs +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +const classInstancesIANA = [ + new Temporal.TimeZone("Asia/Calcutta"), + new CustomTimeZone("Asia/Calcutta"), + new Temporal.TimeZone("Asia/Kolkata"), + new CustomTimeZone("Asia/Kolkata"), + new CustomTimeZone("ASIA/calcutta"), + new CustomTimeZone("Asia/KOLKATA") +]; + +const plainObjectsIANA = [ + { id: "Asia/Calcutta", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "Asia/Kolkata", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "ASIA/calcutta", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null }, + { id: "asia/kolkatA", getPossibleInstantsFor: null, getOffsetNanosecondsFor: null } +]; + +for (const object1 of classInstancesIANA) { + for (const object2 of classInstancesIANA) { + assert.sameValue(object1.equals(object2), true); + } + for (const object2 of plainObjectsIANA) { + assert.sameValue(object1.equals(object2), true); + } +} + +const classInstancesIANADifferentCanonical = [ + new Temporal.TimeZone("Asia/Colombo"), + new CustomTimeZone("Asia/Colombo"), + new Temporal.TimeZone("ASIA/colombo"), + new CustomTimeZone("ASIA/colombo"), +]; + +for (const object1 of classInstancesIANADifferentCanonical) { + for (const object2 of classInstancesIANA) { + assert.sameValue(object1.equals(object2), false); + } + for (const object2 of plainObjectsIANA) { + assert.sameValue(object1.equals(object2), false); + } +} + diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/argument-valid.js b/test/intl402/Temporal/TimeZone/prototype/equals/argument-valid.js new file mode 100644 index 00000000000..734ccf4401a --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/argument-valid.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Built-in time zones are parsed correctly out of valid strings +features: [Temporal] +---*/ + +const valids = [ + ["Africa/CAIRO", "Africa/Cairo"], + ["Asia/Ulan_Bator", "Asia/Ulaanbaatar"], + ["etc/gmt", "Etc/GMT"], + ["1994-11-05T08:15:30-05:00[America/New_York]", "America/New_York"], + ["1994-11-05T08:15:30+05:30[Asia/Calcutta]", "Asia/Calcutta"], + ["1994-11-05T08:15:30+05:30[Asia/Calcutta]", "Asia/Kolkata"], + ["1994-11-05T08:15:30+05:30[Asia/Kolkata]", "Asia/Calcutta"], + ["1994-11-05T08:15:30+05:30[Asia/Kolkata]", "Asia/Kolkata"], +]; + +for (const [valid, canonical = valid] of valids) { + const tzValid = Temporal.TimeZone.from(canonical); + const tzCanonical = Temporal.TimeZone.from(canonical); + assert.sameValue(tzValid.equals(tzCanonical), true); + assert.sameValue(tzCanonical.equals(tzValid), true); +} diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/canonical-iana-names.js b/test/intl402/Temporal/TimeZone/prototype/equals/canonical-iana-names.js new file mode 100644 index 00000000000..f686fa21b91 --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/canonical-iana-names.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Canonicalizes to evaluate time zone equality +features: [Temporal] +---*/ + +const neverEqual = new Temporal.TimeZone('Asia/Tokyo'); +const zdt = new Temporal.ZonedDateTime(0n, 'America/Los_Angeles'); +const ids = [ + ['America/Atka', 'America/Adak'], + ['America/Knox_IN', 'America/Indiana/Knox'], + ['Asia/Ashkhabad', 'Asia/Ashgabat'], + ['Asia/Dacca', 'Asia/Dhaka'], + ['Asia/Istanbul', 'Europe/Istanbul'], + ['Asia/Macao', 'Asia/Macau'], + ['Asia/Thimbu', 'Asia/Thimphu'], + ['Asia/Ujung_Pandang', 'Asia/Makassar'], + ['Asia/Ulan_Bator', 'Asia/Ulaanbaatar'] +]; + +for (const [identifier, primaryIdentifier] of ids) { + const tz1 = new Temporal.TimeZone(identifier); + const tz2 = new Temporal.TimeZone(primaryIdentifier); + + // compare objects + assert.sameValue(tz1.equals(tz2), true); + assert.sameValue(tz2.equals(tz1), true); + assert.sameValue(tz1.equals(neverEqual), false); + + // compare string IDs + assert.sameValue(tz1.equals(tz2.id), true); + assert.sameValue(tz2.equals(tz1.id), true); + assert.sameValue(tz1.equals(neverEqual.id), false); + + // compare ZonedDateTime instances + assert.sameValue(tz1.equals(zdt.withTimeZone(tz2)), true); + assert.sameValue(tz2.equals(zdt.withTimeZone(tz1)), true); + assert.sameValue(tz1.equals(zdt.withTimeZone(neverEqual)), false); + + // compare IXDTF strings + assert.sameValue(tz1.equals(zdt.withTimeZone(tz2).toString()), true); + assert.sameValue(tz2.equals(zdt.withTimeZone(tz1).toString()), true); + assert.sameValue(tz1.equals(zdt.withTimeZone(neverEqual).toString()), false); +} diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/canonical-not-equal.js b/test/intl402/Temporal/TimeZone/prototype/equals/canonical-not-equal.js new file mode 100644 index 00000000000..024499167ea --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/canonical-not-equal.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Canonical time zone identifiers are never equal to each other +features: [Temporal] +---*/ + +// supportedValuesOf only returns canonical IDs +const ids = Intl.supportedValuesOf("timeZone"); + +const forEachDistinctPair = (array, func) => { + for (let i = 0; i < array.length; i++) { + for (let j = i + 1; j < array.length; j++) { + func(array[i], array[j]); + } + } +}; + +forEachDistinctPair(ids, (id1, id2) => { + const tz = new Temporal.TimeZone(id1); + assert.sameValue(tz.equals(id2), false); +}) + diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/offset-and-iana.js b/test/intl402/Temporal/TimeZone/prototype/equals/offset-and-iana.js new file mode 100644 index 00000000000..85cc2be5993 --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/offset-and-iana.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone +description: Offset string time zones compare as expected +features: [Temporal] +---*/ + +const zdt = new Temporal.ZonedDateTime(0n, "America/Los_Angeles"); +const otz1 = new Temporal.TimeZone("+05:30"); +const otz2 = new Temporal.TimeZone("+0530"); +const tz = new Temporal.TimeZone("Asia/Kolkata"); +assert.sameValue(otz1.equals(otz2), true); +assert.sameValue(otz2.equals(otz1), true); +assert.sameValue(otz1.equals("+05:30"), true); +assert.sameValue(otz1.equals(zdt.withTimeZone(otz2)), true); +assert.sameValue(otz1.equals(zdt.withTimeZone(otz2).toString()), true); +assert.sameValue(otz1.equals(tz), false); +assert.sameValue(otz1.equals("Asia/Kolkata"), false); +assert.sameValue(otz1.equals(zdt.withTimeZone(tz)), false); +assert.sameValue(otz1.equals(zdt.withTimeZone(tz).toString()), false); diff --git a/test/intl402/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js b/test/intl402/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js new file mode 100644 index 00000000000..2d7d1d1d287 --- /dev/null +++ b/test/intl402/Temporal/TimeZone/prototype/equals/timezone-case-insensitive.js @@ -0,0 +1,626 @@ +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.timezone.from +description: Time zone names are compared case-insensitively +features: [Temporal] +---*/ + +const timeZoneIdentifiers = [ + // IANA TZDB Zone names + 'Africa/Abidjan', + 'Africa/Algiers', + 'Africa/Bissau', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/El_Aaiun', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Khartoum', + 'Africa/Lagos', + 'Africa/Maputo', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Sao_Tome', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Araguaina', + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Asuncion', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Cayenne', + 'America/Chicago', + 'America/Chihuahua', + // 'America/Ciudad_Juarez' // uncomment after Node supports this ID added in TZDB 2022g + 'America/Costa_Rica', + 'America/Cuiaba', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Fort_Nelson', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Juneau', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/New_York', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Nuuk', + 'America/Ojinaga', + 'America/Panama', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Punta_Arenas', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Sitka', + 'America/St_Johns', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Tijuana', + 'America/Toronto', + 'America/Vancouver', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'America/Yellowknife', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/Troll', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Atyrau', + 'Asia/Baghdad', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Barnaul', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Chita', + 'Asia/Choibalsan', + 'Asia/Colombo', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Famagusta', + 'Asia/Gaza', + 'Asia/Hebron', + 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Kathmandu', + 'Asia/Khandyga', + 'Asia/Kolkata', + 'Asia/Krasnoyarsk', + 'Asia/Kuching', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qostanay', + 'Asia/Qyzylorda', + 'Asia/Riyadh', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Tomsk', + 'Asia/Ulaanbaatar', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yangon', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faroe', + 'Atlantic/Madeira', + 'Atlantic/South_Georgia', + 'Atlantic/Stanley', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/Perth', + 'Australia/Sydney', + 'CET', + 'CST6CDT', + 'EET', + 'EST', + 'EST5EDT', + 'Etc/GMT', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/UTC', + 'Europe/Andorra', + 'Europe/Astrakhan', + 'Europe/Athens', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Chisinau', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Helsinki', + 'Europe/Istanbul', + 'Europe/Kaliningrad', + 'Europe/Kirov', + 'Europe/Kyiv', + 'Europe/Lisbon', + 'Europe/London', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Minsk', + 'Europe/Moscow', + 'Europe/Paris', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/Saratov', + 'Europe/Simferopol', + 'Europe/Sofia', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Ulyanovsk', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zurich', + 'HST', + 'Indian/Chagos', + 'Indian/Maldives', + 'Indian/Mauritius', + 'MET', + 'MST', + 'MST7MDT', + 'PST8PDT', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Kanton', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Marquesas', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + + // IANA TZDB Link names + 'WET', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Asmara', + 'Africa/Asmera', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Kampala', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Timbuktu', + 'America/Anguilla', + 'America/Antigua', + 'America/Argentina/ComodRivadavia', + 'America/Aruba', + 'America/Atikokan', + 'America/Atka', + 'America/Blanc-Sablon', + 'America/Buenos_Aires', + 'America/Catamarca', + 'America/Cayman', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Creston', + 'America/Curacao', + 'America/Dominica', + 'America/Ensenada', + 'America/Fort_Wayne', + 'America/Godthab', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Indianapolis', + 'America/Jujuy', + 'America/Knox_IN', + 'America/Kralendijk', + 'America/Louisville', + 'America/Lower_Princes', + 'America/Marigot', + 'America/Mendoza', + 'America/Montreal', + 'America/Montserrat', + 'America/Nassau', + 'America/Nipigon', + 'America/Pangnirtung', + 'America/Port_of_Spain', + 'America/Porto_Acre', + 'America/Rainy_River', + 'America/Rosario', + 'America/Santa_Isabel', + 'America/Shiprock', + 'America/St_Barthelemy', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Thunder_Bay', + 'America/Tortola', + 'America/Virgin', + 'Antarctica/DumontDUrville', + 'Antarctica/McMurdo', + 'Antarctica/South_Pole', + 'Antarctica/Syowa', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Ashkhabad', + 'Asia/Bahrain', + 'Asia/Brunei', + 'Asia/Calcutta', + 'Asia/Chongqing', + 'Asia/Chungking', + 'Asia/Dacca', + 'Asia/Harbin', + 'Asia/Istanbul', + 'Asia/Kashgar', + 'Asia/Katmandu', + 'Asia/Kuala_Lumpur', + 'Asia/Kuwait', + 'Asia/Macao', + 'Asia/Muscat', + 'Asia/Phnom_Penh', + 'Asia/Rangoon', + 'Asia/Saigon', + 'Asia/Tel_Aviv', + 'Asia/Thimbu', + 'Asia/Ujung_Pandang', + 'Asia/Ulan_Bator', + 'Asia/Vientiane', + 'Atlantic/Faeroe', + 'Atlantic/Jan_Mayen', + 'Atlantic/Reykjavik', + 'Atlantic/St_Helena', + 'Australia/ACT', + 'Australia/Canberra', + 'Australia/Currie', + 'Australia/LHI', + 'Australia/NSW', + 'Australia/North', + 'Australia/Queensland', + 'Australia/South', + 'Australia/Tasmania', + 'Australia/Victoria', + 'Australia/West', + 'Australia/Yancowinna', + 'Brazil/Acre', + 'Brazil/DeNoronha', + 'Brazil/East', + 'Brazil/West', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Canada/Saskatchewan', + 'Canada/Yukon', + 'Chile/Continental', + 'Chile/EasterIsland', + 'Cuba', + 'Egypt', + 'Eire', + 'Etc/GMT+0', + 'Etc/GMT-0', + 'Etc/GMT0', + 'Etc/Greenwich', + 'Etc/UCT', + 'Etc/Universal', + 'Etc/Zulu', + 'Europe/Amsterdam', + 'Europe/Belfast', + 'Europe/Bratislava', + 'Europe/Busingen', + 'Europe/Copenhagen', + 'Europe/Guernsey', + 'Europe/Isle_of_Man', + 'Europe/Jersey', + 'Europe/Kiev', + 'Europe/Ljubljana', + 'Europe/Luxembourg', + 'Europe/Mariehamn', + 'Europe/Monaco', + 'Europe/Nicosia', + 'Europe/Oslo', + 'Europe/Podgorica', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Skopje', + 'Europe/Stockholm', + 'Europe/Tiraspol', + 'Europe/Uzhgorod', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Zagreb', + 'Europe/Zaporozhye', + 'GB', + 'GB-Eire', + 'GMT', + 'GMT+0', + 'GMT-0', + 'GMT0', + 'Greenwich', + 'Hongkong', + 'Iceland', + 'Indian/Antananarivo', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Iran', + 'Israel', + 'Jamaica', + 'Japan', + 'Kwajalein', + 'Libya', + 'Mexico/BajaNorte', + 'Mexico/BajaSur', + 'Mexico/General', + 'NZ', + 'NZ-CHAT', + 'Navajo', + 'PRC', + 'Pacific/Chuuk', + 'Pacific/Enderbury', + 'Pacific/Funafuti', + 'Pacific/Johnston', + 'Pacific/Majuro', + 'Pacific/Midway', + 'Pacific/Pohnpei', + 'Pacific/Ponape', + 'Pacific/Saipan', + 'Pacific/Samoa', + 'Pacific/Truk', + 'Pacific/Wake', + 'Pacific/Wallis', + 'Pacific/Yap', + 'Poland', + 'Portugal', + 'ROC', + 'ROK', + 'Singapore', + 'Turkey', + 'UCT', + 'US/Alaska', + 'US/Aleutian', + 'US/Arizona', + 'US/Central', + 'US/East-Indiana', + 'US/Eastern', + 'US/Hawaii', + 'US/Indiana-Starke', + 'US/Michigan', + 'US/Mountain', + 'US/Pacific', + 'US/Pacific-New', + 'US/Samoa', + 'UTC', + 'Universal', + 'W-SU', + 'Zulu' +]; + +// We want to test all available named time zone identifiers (both primary and non-primary), +// but no ECMAScript built-in API exposes that list. So we use a union of two sources: +// 1. A hard-coded list of Zone and Link identifiers from the 2022g version of IANA TZDB. +// 2. Canonical IDs exposed by Intl.supportedValuesOf('timeZone'), which ensures that IDs +// added to TZDB later than 2022g will be tested. (New IDs are almost always added as primary.) +const ids = [...new Set([...timeZoneIdentifiers, ...Intl.supportedValuesOf('timeZone')])]; +for (const id of ids) { + const lower = id.toLowerCase(); + const upper = id.toUpperCase(); + const tz = new Temporal.TimeZone(id); + assert.sameValue(tz.equals(id), true, `Time zone "${id}" compared to string "${upper}"`); + assert.sameValue(tz.equals(id), true, `Time zone "${id}" compared to string "${lower}"`); +} +