diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/built-ins/Temporal/ZonedDateTime | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/ZonedDateTime')
1245 files changed, 36738 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js new file mode 100644 index 0000000000..6184820ef9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/builtin.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Tests that Temporal.ZonedDateTime meets the requirements for built-in objects +info: | + 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.ZonedDateTime), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime), + Function.prototype, "prototype"); + +assert.sameValue(typeof Temporal.ZonedDateTime.prototype, + "object", "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js new file mode 100644 index 0000000000..80ff0f9703 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-case-insensitive.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Calendar names are case-insensitive +features: [Temporal] +---*/ + +const arg = "iSo8601"; + +const result = new Temporal.ZonedDateTime(0n, "UTC", arg); +assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js new file mode 100644 index 0000000000..14b85249a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-number.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: A number is not allowed to be a calendar +features: [Temporal] +---*/ + +const numbers = [ + 1, + -19761118, + 19761118, + 1234567890, +]; + +for (const arg of numbers) { + assert.throws( + TypeError, + () => new Temporal.ZonedDateTime(0n, "UTC", arg), + "A number is not a valid ISO string for Calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js new file mode 100644 index 0000000000..f411804a50 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-string.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.constructor +description: A calendar ID is valid input for Calendar +features: [Temporal] +---*/ + +const arg = "iso8601"; + +const result = new Temporal.ZonedDateTime(0n, "UTC", arg); +assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js new file mode 100644 index 0000000000..21d3b71e75 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-temporal-object.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal-totemporalcalendar step 1.b: + b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(2000, 5, 2); +const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const plainMonthDay = new Temporal.PlainMonthDay(5, 2); +const plainYearMonth = new Temporal.PlainYearMonth(2000, 5); +const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => { + const actual = []; + const expected = []; + + const calendar = arg.getISOFields().calendar; + + Object.defineProperty(arg, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + const result = new Temporal.ZonedDateTime(0n, "UTC", arg); + assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar"); + + assert.compareArray(actual, expected, "calendar getter not called"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js new file mode 100644 index 0000000000..ecc264b47d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-undefined.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Calendar argument defaults to the built-in ISO 8601 calendar +features: [BigInt, Temporal] +---*/ + +const args = [957270896987654321n, new Temporal.TimeZone("UTC")]; + +Object.defineProperty(Temporal.Calendar, "from", { + get() { + throw new Test262Error("Should not get Calendar.from"); + }, +}); + +const explicit = new Temporal.ZonedDateTime(...args, undefined); +assert.sameValue(explicit.getISOFields().calendar, "iso8601", "calendar slot should store a string"); + +const implicit = new Temporal.ZonedDateTime(...args); +assert.sameValue(implicit.getISOFields().calendar, "iso8601", "calendar slot should store a string"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js new file mode 100644 index 0000000000..82cf2a3c83 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for Calendar +features: [BigInt, Symbol, Temporal] +---*/ + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => new Temporal.ZonedDateTime(0n, "UTC", arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => new Temporal.ZonedDateTime(0n, "UTC", arg), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..7a7d87020f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const timeZone = "UTC"; +const datetime = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" }; +Temporal.ZonedDateTime.compare(arg, datetime); +Temporal.ZonedDateTime.compare(datetime, arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..82854f00eb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,138 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Correct time zone calls are made when converting a ZonedDateTime-like property + bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone1 = TemporalHelpers.timeZoneObserver(actual, "one.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const timeZone2 = TemporalHelpers.timeZoneObserver(actual, "two.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const calendar1 = TemporalHelpers.calendarObserver(actual, "one.calendar"); +const calendar2 = TemporalHelpers.calendarObserver(actual, "two.calendar"); + +const expectedOne = [ + // GetTemporalCalendarSlotValueWithISODefault + "has one.calendar.dateAdd", + "has one.calendar.dateFromFields", + "has one.calendar.dateUntil", + "has one.calendar.day", + "has one.calendar.dayOfWeek", + "has one.calendar.dayOfYear", + "has one.calendar.daysInMonth", + "has one.calendar.daysInWeek", + "has one.calendar.daysInYear", + "has one.calendar.fields", + "has one.calendar.id", + "has one.calendar.inLeapYear", + "has one.calendar.mergeFields", + "has one.calendar.month", + "has one.calendar.monthCode", + "has one.calendar.monthDayFromFields", + "has one.calendar.monthsInYear", + "has one.calendar.weekOfYear", + "has one.calendar.year", + "has one.calendar.yearMonthFromFields", + "has one.calendar.yearOfWeek", + // lookup + "get one.calendar.dateFromFields", + "get one.calendar.fields", + // CalendarFields + "call one.calendar.fields", + // ToTemporalTimeZoneSlotValue + "has one.timeZone.getOffsetNanosecondsFor", + "has one.timeZone.getPossibleInstantsFor", + "has one.timeZone.id", + // InterpretTemporalDateTimeFields + "call one.calendar.dateFromFields", + // lookup + "get one.timeZone.getOffsetNanosecondsFor", + "get one.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call one.timeZone.getPossibleInstantsFor", +]; + +const expectedTwo = [ + // GetTemporalCalendarSlotValueWithISODefault + "has two.calendar.dateAdd", + "has two.calendar.dateFromFields", + "has two.calendar.dateUntil", + "has two.calendar.day", + "has two.calendar.dayOfWeek", + "has two.calendar.dayOfYear", + "has two.calendar.daysInMonth", + "has two.calendar.daysInWeek", + "has two.calendar.daysInYear", + "has two.calendar.fields", + "has two.calendar.id", + "has two.calendar.inLeapYear", + "has two.calendar.mergeFields", + "has two.calendar.month", + "has two.calendar.monthCode", + "has two.calendar.monthDayFromFields", + "has two.calendar.monthsInYear", + "has two.calendar.weekOfYear", + "has two.calendar.year", + "has two.calendar.yearMonthFromFields", + "has two.calendar.yearOfWeek", + // lookup + "get two.calendar.dateFromFields", + "get two.calendar.fields", + // CalendarFields + "call two.calendar.fields", + // ToTemporalTimeZoneSlotValue + "has two.timeZone.getOffsetNanosecondsFor", + "has two.timeZone.getPossibleInstantsFor", + "has two.timeZone.id", + // InterpretTemporalDateTimeFields + "call two.calendar.dateFromFields", + // lookup + "get two.timeZone.getOffsetNanosecondsFor", + "get two.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call two.timeZone.getPossibleInstantsFor", +]; + +Temporal.ZonedDateTime.compare( + { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: timeZone1, calendar: calendar1 }, + { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: timeZone2, calendar: calendar2 }, +); + +const expectedSpringForward = expectedOne.concat([ + // DisambiguatePossibleInstants + "call one.timeZone.getOffsetNanosecondsFor", + "call one.timeZone.getOffsetNanosecondsFor", + "call one.timeZone.getPossibleInstantsFor", +], expectedTwo, [ + // DisambiguatePossibleInstants + "call two.timeZone.getOffsetNanosecondsFor", + "call two.timeZone.getOffsetNanosecondsFor", + "call two.timeZone.getPossibleInstantsFor", +]); +assert.compareArray(actual, expectedSpringForward, "order of operations converting property bags at skipped wall-clock time"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.compare( + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: timeZone1, calendar: calendar1 }, + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: timeZone2, calendar: calendar2 }, +); + +const expectedFallBack = expectedOne.concat(expectedTwo); +assert.compareArray(actual, expectedFallBack, "order of operations converting property bags at repeated wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..ea05affac6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: The calendar name is case-insensitive +features: [Temporal] +---*/ + +const calendar = "IsO8601"; + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(0n, timeZone); + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result1 = Temporal.ZonedDateTime.compare(arg, datetime); +assert.sameValue(result1, 0, "Calendar is case-insensitive (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(datetime, arg); +assert.sameValue(result2, 0, "Calendar is case-insensitive (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..9a09ea68b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Leap second is a valid ISO string for a calendar in a property bag +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(217_123_200_000_000_000n, timeZone); +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1976, monthCode: "M11", day: 18, timeZone, calendar }; +const result1 = Temporal.ZonedDateTime.compare(arg, datetime); +assert.sameValue(result1, 0, "leap second is a valid ISO string for calendar (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(datetime, arg); +assert.sameValue(result2, 0, "leap second is a valid ISO string for calendar (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..3171b189a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-number.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(0n, timeZone); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1970, monthCode: "M01", day: 1, calendar, timeZone }; + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + "A number is not a valid ISO string for calendar (first argument)" + ); + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + "A number is not a valid ISO string for calendar (second argument)" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..873234eab5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-string.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: A calendar ID is valid input for Calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields"); +Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateFromFields should not be looked up"); + }, +}); + +const calendar = "iso8601"; + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; + +const result1 = Temporal.ZonedDateTime.compare(arg, datetime); +assert.sameValue(result1, 0, `Calendar created from string "${arg}" (first argument)`); + +const result2 = Temporal.ZonedDateTime.compare(datetime, arg); +assert.sameValue(result2, 0, `Calendar created from string "${arg}" (second argument)`); + +Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..9d70107461 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === "string" ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `${description} does not convert to a valid ISO string (first argument)` + ); + assert.throws( + typeof calendar === "string" ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `${description} does not convert to a valid ISO string (second argument)` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(arg, datetime), `${description} is not a valid property bag and does not convert to a string (first argument)`); + assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, arg), `${description} is not a valid property bag and does not convert to a string (second argument)`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..734ad65e6a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + "reject minus zero as extended year (first argument)" + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + "reject minus zero as extended year (second argument)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..1e963166ba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.compare +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone1 = new SkippedDateTime(); +const arg1 = { year: 2000, month: 5, day: 2, timeZone: timeZone1, calendar: nonBuiltinISOCalendar }; +const timeZone2 = new SkippedDateTime(); +const arg2 = { year: 2000, month: 5, day: 2, timeZone: timeZone2, calendar: nonBuiltinISOCalendar }; + +Temporal.ZonedDateTime.compare(arg1, arg2); + +assert.sameValue(timeZone1.calls, 2, "getPossibleInstantsFor should have been called 2 times on first time zone"); +assert.sameValue(timeZone2.calls, 2, "getPossibleInstantsFor should have been called 2 times on second time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..704149ba69 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-invalid-offset-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +badOffsets.forEach((offset) => { + const arg = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `"${offset} is not a valid offset string (second argument)` + ); + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `"${offset} is not a valid offset string (second argument)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js new file mode 100644 index 0000000000..e1261ae9df --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-offset-not-agreeing-with-timezone.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Property bag with offset property is rejected if offset does not agree with time zone +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); +const datetime = new Temporal.ZonedDateTime(0n, timeZone); + +const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone }; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(properties, datetime), "offset property not matching time zone is rejected (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, properties), "offset property not matching time zone is rejected (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..aa8fba749d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, Infinity, -Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime)); + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..202927c9b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +Temporal.TimeZone.prototype.getPossibleInstantsFor = function () { + return []; +}; + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..6671777aa7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_001, 86400_000_000_001].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime)); + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..64a00cb41b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, hour: 12, timeZone }, datetime), + `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})` + ); + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.compare(datetime, { year: 2000, month: 5, day: 2, hour: 12, timeZone }), + `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..858ba851c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance), "bare date-time string is not a time zone (arg 1)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone (arg 2)"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone (arg 1)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone (arg 2)` + ); +}); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T17:30-07:00", + "2021-08-19T17:30[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", +].forEach((timeZone) => { + Temporal.ZonedDateTime.compare({ year: 2000, month: 5, day: 2, timeZone }, instance); + Temporal.ZonedDateTime.compare(instance, { year: 2000, month: 5, day: 2, timeZone }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..568263d96e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1588377600_000_000_000n, "UTC"); + +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +const result1 = Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance); +assert.sameValue(result1, 0, "leap second is a valid ISO string for TimeZone (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone }); +assert.sameValue(result2, 0, "leap second is a valid ISO string for TimeZone (second argument)"); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance), "leap second in time zone name not valid (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..8ce09f90e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-multiple-offsets.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1588371240_000_000_000n, "+01:46"); + +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +const result1 = Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, instance); +assert.sameValue(result1, 0, "Time zone string determined from bracket name (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(instance, { year: 2020, month: 5, day: 2, timeZone }); +assert.sameValue(result2, 0, "Time zone string determined from bracket name (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..5de43c258c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime), + "reject minus zero as extended year (first argument)" + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }), + "reject minus zero as extended year (second argument)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js new file mode 100644 index 0000000000..6b62326966 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +["UTC", "+01:30"].forEach((timeZone) => { + const epoch = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone(timeZone)); + + // These should be valid input and not throw + Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, epoch); + Temporal.ZonedDateTime.compare(epoch, { year: 2020, month: 5, day: 2, timeZone }); +}); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..ec7b15d19c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-wrong-type.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime), + `${description} does not convert to a valid ISO string (first argument)` + ); + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }), + `${description} does not convert to a valid ISO string (second argument)` + ); +} + +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, () => Temporal.ZonedDateTime.compare({ year: 2020, month: 5, day: 2, timeZone }, datetime), `${description} is not a valid object and does not convert to a string (first argument)`); + assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(datetime, { year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string (second argument)`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..62c5d7c653 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-calendar-annotation.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"], + ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.compare(arg, arg); + + assert.sameValue( + result, + 0, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..9a945a0b99 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-critical-unknown-annotation.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `reject unknown annotation with critical flag: ${arg} (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `reject unknown annotation with critical flag: ${arg} (second argument)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..565f8a1a69 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-date-with-utc-offset.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: UTC offset not valid with format that does not include a time +features: [Temporal] +---*/ + +const validStrings = [ + "1970-01-01T00Z[UTC]", + "1970-01-01T00Z[!UTC]", + "1970-01-01T00+00:00[UTC]", + "1970-01-01T00+00:00[!UTC]", +]; + +for (const arg of validStrings) { + const result = Temporal.ZonedDateTime.compare(arg, arg); + + assert.sameValue( + result, + 0, + `"${arg}" is a valid UTC offset with time for ZonedDateTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30[America/St_Johns]", +]; +const datetime = new Temporal.ZonedDateTime(0n, "UTC"); + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `"${arg}" UTC offset without time is not valid for ZonedDateTime (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `"${arg}" UTC offset without time is not valid for ZonedDateTime (second argument)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..4e14c60039 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-calendar.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `reject more than one calendar annotation if any critical: ${arg} (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `reject more than one calendar annotation if any critical: ${arg} (second argument)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..e94aa815e0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-multiple-time-zone.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(arg, datetime), + `reject more than one time zone annotation: ${arg} (first argument)` + ); + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, arg), + `reject more than one time zone annotation: ${arg} (second argument)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js new file mode 100644 index 0000000000..1f7ffdae89 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-separators.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Time separator in string argument can vary +features: [Temporal] +---*/ + +const epoch = new Temporal.ZonedDateTime(0n, "UTC"); +const tests = [ + ["1970-01-01T00:00+00:00[UTC]", "uppercase T"], + ["1970-01-01t00:00+00:00[UTC]", "lowercase T"], + ["1970-01-01 00:00+00:00[UTC]", "space between date and time"], +]; + +tests.forEach(([arg, description]) => { + assert.sameValue( + Temporal.ZonedDateTime.compare(arg, epoch), + 0, + `variant time separators (${description}), first argument` + ); + + assert.sameValue( + Temporal.ZonedDateTime.compare(epoch, arg), + 0, + `variant time separators (${description}), second argument` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..ade9a8c0ac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-time-zone-annotation.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC]", "named, with no offset"], + ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"], + ["1970-01-01T00:00[+00:00]", "numeric, with no offset"], + ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"], + ["1970-01-01T00:00Z[UTC]", "named, with Z"], + ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"], + ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"], + ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"], + ["1970-01-01T00:00+00:00[UTC]", "named, with offset"], + ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"], + ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"], + ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.compare(arg, arg); + + assert.sameValue( + result, + 0, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..b91d5a940a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-string-unknown-annotation.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Various forms of unknown annotation +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"], + ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"], + ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.compare(arg, arg); + + assert.sameValue( + result, + 0, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js new file mode 100644 index 0000000000..2ee36a11a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/argument-wrong-type.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for ZonedDateTime +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const other = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [undefined, "undefined"], + [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"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(arg, other), + `${description} does not convert to a valid ISO string (first argument)` + ); + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.compare(other, arg), + `${description} does not convert to a valid ISO string (second argument)` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(arg, other), `${description} is not a valid property bag and does not convert to a string (first argument)`); + assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(other, arg), `${description} is not a valid property bag and does not convert to a string (second argument)`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js new file mode 100644 index 0000000000..e21ff70287 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/builtin.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Tests that Temporal.ZonedDateTime.compare meets the requirements for built-in objects +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.ZonedDateTime.compare), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.compare), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.compare), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.compare.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..3132abdbdd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const timeZone = new Temporal.TimeZone("UTC"); +const arg1 = { year: 2000, month: 5, day: 2, timeZone, calendar }; +const arg2 = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +Temporal.ZonedDateTime.compare(arg1, arg2); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (first argument)"); + +calendar.dateFromFieldsCallCount = 0; + +Temporal.ZonedDateTime.compare(arg2, arg1); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js new file mode 100644 index 0000000000..1a7101d1b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-fields-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.compare steps 1–2: + 1. Set _one_ to ? ToTemporalZonedDateTime(_one_). + 2. Set _two_ to ? ToTemporalZonedDateTime(_two_). + sec-temporal-totemporalzoneddatetime step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar1 = TemporalHelpers.calendarFieldsIterable(); +const calendar2 = TemporalHelpers.calendarFieldsIterable(); +Temporal.ZonedDateTime.compare( + { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: calendar1 }, + { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: calendar2 }, +); + +assert.sameValue(calendar1.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar1.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar1.iteratorExhausted[0], "iterated through the whole iterable"); +assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js new file mode 100644 index 0000000000..b3c5353d7f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/calendar-temporal-object.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.compare steps 1–2: + 1. Set _one_ to ? ToTemporalZonedDateTime(_one_). + 2. Set _two_ to ? ToTemporalZonedDateTime(_two_). + sec-temporal-totemporalzoneddatetime step 2.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + Temporal.ZonedDateTime.compare( + { year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject }, + { year: 2001, month: 6, day: 3, timeZone: "UTC", calendar: temporalObject }, + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..15661422e1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/constructor-in-calendar-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.compare +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const datetime = Temporal.ZonedDateTime.from({ year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris' }); + +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js new file mode 100644 index 0000000000..ca8e7ba69b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/duplicate-calendar-fields.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.compare +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + + +for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const timeZone = 'Europe/Paris' + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + const datetime = Temporal.ZonedDateTime.from({year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris'}); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime)); + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js new file mode 100644 index 0000000000..f2c7bb3e29 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/exhaustive.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Tests for compare() with each possible outcome +features: [Temporal] +---*/ + +const tz1 = "UTC"; +const tz2 = "-00:30"; +const cal1 = "iso8601"; +const cal2 = new (class extends Temporal.Calendar { id = "custom"; })("iso8601"); + +assert.sameValue( + Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, tz1, cal1), new Temporal.ZonedDateTime(500_000_000_000_000_000n, tz2, cal2)), + 1, + ">" +); +assert.sameValue( + Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(-1000n, tz1, cal1), new Temporal.ZonedDateTime(1000n, tz2, cal2)), + -1, + "<" +); +assert.sameValue( + Temporal.ZonedDateTime.compare(new Temporal.ZonedDateTime(123_456_789n, tz1, cal1), new Temporal.ZonedDateTime(123_456_789n, tz2, cal2)), + 0, + "=" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..34ffba6689 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/infinity-throws-rangeerror.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in a property bag for either argument is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.compare +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const other = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: inf }, other), `${prop} property cannot be ${inf}`); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: inf }), `${prop} property cannot be ${inf}`); + + const calls1 = []; + const obj1 = TemporalHelpers.toPrimitiveObserver(calls1, inf, prop); + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare({ ...base, [prop]: obj1 }, other)); + assert.compareArray(calls1, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + + const calls2 = []; + const obj2 = TemporalHelpers.toPrimitiveObserver(calls2, inf, prop); + assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(other, { ...base, [prop]: obj2 })); + assert.compareArray(calls2, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js new file mode 100644 index 0000000000..8e677091ce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/leap-second.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Leap second is a valid ISO string for ZonedDateTime +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, new Temporal.TimeZone("UTC")); + +let arg = "2016-12-31T23:59:60+00:00[UTC]"; +const result1 = Temporal.ZonedDateTime.compare(arg, datetime); +assert.sameValue(result1, 0, "leap second is a valid ISO string for ZonedDateTime (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(datetime, arg); +assert.sameValue(result2, 0, "leap second is a valid ISO string for ZonedDateTime (second argument)"); + +arg = "2000-05-02T12:34:56+23:59[+23:59:60]"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime), "leap second in time zone name not valid (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg), "leap second in time zone name not valid (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js new file mode 100644 index 0000000000..24b9580c43 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Temporal.ZonedDateTime.compare.length is 2 +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.ZonedDateTime.compare, "length", { + value: 2, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js new file mode 100644 index 0000000000..27b064c466 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Temporal.ZonedDateTime.compare.name is "compare" +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.ZonedDateTime.compare, "name", { + value: "compare", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js new file mode 100644 index 0000000000..41e48e7713 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/not-a-constructor.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Temporal.ZonedDateTime.compare 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.ZonedDateTime.compare(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.compare), false, + "isConstructor(Temporal.ZonedDateTime.compare)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js new file mode 100644 index 0000000000..5d712d2d69 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js @@ -0,0 +1,192 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Properties on objects passed to compare() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get one.calendar", + "has one.calendar.dateAdd", + "has one.calendar.dateFromFields", + "has one.calendar.dateUntil", + "has one.calendar.day", + "has one.calendar.dayOfWeek", + "has one.calendar.dayOfYear", + "has one.calendar.daysInMonth", + "has one.calendar.daysInWeek", + "has one.calendar.daysInYear", + "has one.calendar.fields", + "has one.calendar.id", + "has one.calendar.inLeapYear", + "has one.calendar.mergeFields", + "has one.calendar.month", + "has one.calendar.monthCode", + "has one.calendar.monthDayFromFields", + "has one.calendar.monthsInYear", + "has one.calendar.weekOfYear", + "has one.calendar.year", + "has one.calendar.yearMonthFromFields", + "has one.calendar.yearOfWeek", + "get one.calendar.dateFromFields", + "get one.calendar.fields", + "call one.calendar.fields", + // PrepareTemporalFields + "get one.day", + "get one.day.valueOf", + "call one.day.valueOf", + "get one.hour", + "get one.hour.valueOf", + "call one.hour.valueOf", + "get one.microsecond", + "get one.microsecond.valueOf", + "call one.microsecond.valueOf", + "get one.millisecond", + "get one.millisecond.valueOf", + "call one.millisecond.valueOf", + "get one.minute", + "get one.minute.valueOf", + "call one.minute.valueOf", + "get one.month", + "get one.month.valueOf", + "call one.month.valueOf", + "get one.monthCode", + "get one.monthCode.toString", + "call one.monthCode.toString", + "get one.nanosecond", + "get one.nanosecond.valueOf", + "call one.nanosecond.valueOf", + "get one.offset", + "get one.offset.toString", + "call one.offset.toString", + "get one.second", + "get one.second.valueOf", + "call one.second.valueOf", + "get one.timeZone", + "get one.year", + "get one.year.valueOf", + "call one.year.valueOf", + "has one.timeZone.getOffsetNanosecondsFor", + "has one.timeZone.getPossibleInstantsFor", + "has one.timeZone.id", + // InterpretTemporalDateTimeFields + "call one.calendar.dateFromFields", + // InterpretISODateTimeOffset + "get one.timeZone.getOffsetNanosecondsFor", + "get one.timeZone.getPossibleInstantsFor", + "call one.timeZone.getPossibleInstantsFor", + "call one.timeZone.getOffsetNanosecondsFor", + // Same set of operations, for the other argument: + "get two.calendar", + "has two.calendar.dateAdd", + "has two.calendar.dateFromFields", + "has two.calendar.dateUntil", + "has two.calendar.day", + "has two.calendar.dayOfWeek", + "has two.calendar.dayOfYear", + "has two.calendar.daysInMonth", + "has two.calendar.daysInWeek", + "has two.calendar.daysInYear", + "has two.calendar.fields", + "has two.calendar.id", + "has two.calendar.inLeapYear", + "has two.calendar.mergeFields", + "has two.calendar.month", + "has two.calendar.monthCode", + "has two.calendar.monthDayFromFields", + "has two.calendar.monthsInYear", + "has two.calendar.weekOfYear", + "has two.calendar.year", + "has two.calendar.yearMonthFromFields", + "has two.calendar.yearOfWeek", + "get two.calendar.dateFromFields", + "get two.calendar.fields", + "call two.calendar.fields", + // PrepareTemporalFields + "get two.day", + "get two.day.valueOf", + "call two.day.valueOf", + "get two.hour", + "get two.hour.valueOf", + "call two.hour.valueOf", + "get two.microsecond", + "get two.microsecond.valueOf", + "call two.microsecond.valueOf", + "get two.millisecond", + "get two.millisecond.valueOf", + "call two.millisecond.valueOf", + "get two.minute", + "get two.minute.valueOf", + "call two.minute.valueOf", + "get two.month", + "get two.month.valueOf", + "call two.month.valueOf", + "get two.monthCode", + "get two.monthCode.toString", + "call two.monthCode.toString", + "get two.nanosecond", + "get two.nanosecond.valueOf", + "call two.nanosecond.valueOf", + "get two.offset", + "get two.offset.toString", + "call two.offset.toString", + "get two.second", + "get two.second.valueOf", + "call two.second.valueOf", + "get two.timeZone", + "get two.year", + "get two.year.valueOf", + "call two.year.valueOf", + "has two.timeZone.getOffsetNanosecondsFor", + "has two.timeZone.getPossibleInstantsFor", + "has two.timeZone.id", + // InterpretTemporalDateTimeFields + "call two.calendar.dateFromFields", + // InterpretISODateTimeOffset + "get two.timeZone.getOffsetNanosecondsFor", + "get two.timeZone.getPossibleInstantsFor", + "call two.timeZone.getPossibleInstantsFor", + "call two.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const one = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "one.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "one.timeZone"), +}, "one"); + +const two = TemporalHelpers.propertyBagObserver(actual, { + year: 2014, + month: 7, + monthCode: "M07", + day: 19, + hour: 12, + minute: 34, + second: 56, + millisecond: 123, + microsecond: 456, + nanosecond: 789, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "two.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "two.timeZone"), +}, "two"); + +Temporal.ZonedDateTime.compare(one, two); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js new file mode 100644 index 0000000000..bfe1ea9659 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: The "compare" property of Temporal.ZonedDateTime +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.compare, + "function", + "`typeof ZonedDateTime.compare` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime, "compare", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js new file mode 100644 index 0000000000..b0d2d1427d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/proto-in-calendar-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.compare +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const datetime = Temporal.ZonedDateTime.from({ year: 2023, month: 5, monthCode: 'M05', day: 15, timeZone: 'Europe/Paris' }); + +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime)); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..72195dda75 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/read-time-fields-before-datefromfields.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.compare steps 1–2: + 1. Set _one_ to ? ToTemporalZonedDateTime(_one_). + 2. Set _two_ to ? ToTemporalZonedDateTime(_two_). + sec-temporal-totemporalzoneddatetime step 2.j: + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const result = Temporal.ZonedDateTime.compare( + { year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar }, + { year: 2000, month: 5, day: 2, hour: 6, minute: 54, second: 32, millisecond: 123, microsecond: 456, nanosecond: 789, timeZone: "UTC", calendar }, +); + +// will be 0 if the time fields are coerced to their max values due to Infinity +assert.sameValue(result, 1, "comparison result"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..d4f71338e4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.compare steps 1–2: + 1. Set _one_ to ? ToTemporalZonedDateTime(_one_). + 2. Set _two_ to ? ToTemporalZonedDateTime(_two_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected1 = [ + "2000-05-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + Temporal.ZonedDateTime.compare( + { year: 2000, month: 5, day: 2, timeZone }, + { year: 2001, month: 6, day: 3, timeZone: "UTC" }, + ); +}, expected1); + +// Same, but on the other operand + +const expected2 = [ + "2001-06-03T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + Temporal.ZonedDateTime.compare( + { year: 2000, month: 5, day: 2, timeZone: "UTC" }, + { year: 2001, month: 6, day: 3, timeZone }, + ); +}, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js new file mode 100644 index 0000000000..4bdd4cae93 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Negative zero, as an extended year, fails +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +const invalidStrings = [ + "-0000000-01-01T00:02Z[UTC]", + "-0000000-01-01T00:02+00:00[UTC]", + "-0000000-01-01T00:02:00.000000000+00:00[UTC]", +]; + +invalidStrings.forEach((arg) => { + assert.throws(RangeError, + () => Temporal.ZonedDateTime.compare(arg, instance), + "cannot use negative zero as extended year (first argument)" + ); + assert.throws(RangeError, + () => Temporal.ZonedDateTime.compare(instance, arg), + "cannot use negative zero as extended year (second argument)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js new file mode 100644 index 0000000000..a99ebbbc16 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string-multiple-offsets.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(30_000_000_000n, "+01:35"); +let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]"; + +assert.sameValue( + Temporal.ZonedDateTime.compare(str, datetime), + 0, + "Time zone determined from bracket name (first argument)" +); +assert.sameValue( + Temporal.ZonedDateTime.compare(datetime, str), + 0, + "Time zone determined from bracket name (second argument)" +); + +str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]"; +assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(str, datetime), + "Trailing zeroes not allowed for sub-minute time zone identifiers (first argument)" +); +assert.throws( + RangeError, + () => Temporal.ZonedDateTime.compare(datetime, str), + "Trailing zeroes not allowed for sub-minute time zone identifiers (second argument)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js new file mode 100644 index 0000000000..73fa389248 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/compare/zoneddatetime-string.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.compare +description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances +features: [Temporal] +---*/ + +const epoch = new Temporal.ZonedDateTime(0n, "UTC"); +const hourBefore = new Temporal.ZonedDateTime(-3600_000_000_000n, "UTC"); + +let str = "1970-01-01T00:00"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "bare date-time string is not a ZonedDateTime (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "bare date-time string is not a ZonedDateTime (second argument)"); +str = "1970-01-01T00:00Z"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + Z is not a ZonedDateTime (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + Z is not a ZonedDateTime (second argument)"); +str = "1970-01-01T00:00+01:00"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset is not a ZonedDateTime (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset is not a ZonedDateTime (second argument)"); + +str = "1970-01-01T00:00[+01:00]"; +const result1 = Temporal.ZonedDateTime.compare(str, hourBefore); +assert.sameValue(result1, 0, "date-time + IANA annotation preserves wall time in the time zone (first argument)"); +const result2 = Temporal.ZonedDateTime.compare(hourBefore, str); +assert.sameValue(result2, 0, "date-time + IANA annotation preserves wall time in the time zone (second argument)"); + +str = "1970-01-01T00:00Z[+01:00]"; +const result3 = Temporal.ZonedDateTime.compare(str, epoch); +assert.sameValue(result3, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (first argument)"); +const result4 = Temporal.ZonedDateTime.compare(epoch, str); +assert.sameValue(result4, 0, "date-time + Z + IANA annotation preserves exact time in the time zone (second argument)"); + +str = "1970-01-01T00:00+01:00[+01:00]"; +const result5 = Temporal.ZonedDateTime.compare(str, hourBefore); +assert.sameValue(result5, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (first argument)"); +const result6 = Temporal.ZonedDateTime.compare(hourBefore, str); +assert.sameValue(result6, 0, "date-time + offset + IANA annotation ensures both exact and wall time match (second argument)"); + +str = "1970-01-01T00:00-04:15[+01:00]"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(str, epoch), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (first argument)"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(epoch, str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js new file mode 100644 index 0000000000..af174cc625 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/constructor.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Temporal.ZonedDateTime constructor cannot be called as a function +info: | + 1. If NewTarget is undefined, throw a TypeError exception. +features: [Temporal] +---*/ + +assert.throws(TypeError, () => Temporal.ZonedDateTime(0n, "UTC")); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..18f1c49471 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const timeZone = "UTC"; +const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" }; +Temporal.ZonedDateTime.from(arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..87d3c908ac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,138 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Correct time zone calls are made when converting a ZonedDateTime-like property + bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const calendar = TemporalHelpers.calendarObserver(actual, "calendar"); + +const expected = [ + // GetTemporalCalendarSlotValueWithISODefault + "has calendar.dateAdd", + "has calendar.dateFromFields", + "has calendar.dateUntil", + "has calendar.day", + "has calendar.dayOfWeek", + "has calendar.dayOfYear", + "has calendar.daysInMonth", + "has calendar.daysInWeek", + "has calendar.daysInYear", + "has calendar.fields", + "has calendar.id", + "has calendar.inLeapYear", + "has calendar.mergeFields", + "has calendar.month", + "has calendar.monthCode", + "has calendar.monthDayFromFields", + "has calendar.monthsInYear", + "has calendar.weekOfYear", + "has calendar.year", + "has calendar.yearMonthFromFields", + "has calendar.yearOfWeek", + // lookup in ToTemporalZonedDateTime + "get calendar.dateFromFields", + "get calendar.fields", + // CalendarFields + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "call calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", +]; + +Temporal.ZonedDateTime.from( + { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "use" } +); +assert.compareArray(actual, expected, "order of operations converting property bag at skipped wall-clock time with offset: use"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "ignore" } +); +assert.compareArray(actual, expected.concat([ + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getPossibleInstantsFor", +]), "order of operations converting property bag at skipped wall-clock time with offset: ignore"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 4, day: 2, hour: 2, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "prefer" } +); +assert.compareArray(actual, expected.concat([ + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getPossibleInstantsFor", +]), "order of operations converting property bag at skipped wall-clock time with offset: prefer"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "use" } +); +assert.compareArray(actual, expected, "order of operations converting property bag at repeated wall-clock time with offset: use"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "ignore" } +); +assert.compareArray(actual, expected.concat([ + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", +]), "order of operations converting property bag at repeated wall-clock time with offset: ignore"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "prefer" } +); +assert.compareArray(actual, expected.concat([ + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", +]), "order of operations converting property bag at repeated wall-clock time with offset: prefer"); +actual.splice(0); // clear + +Temporal.ZonedDateTime.from( + { year: 2000, month: 10, day: 29, hour: 1, minute: 30, offset: "-08:00", timeZone, calendar }, + { offset: "reject" } +); +assert.compareArray(actual, expected.concat([ + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", +]), "order of operations converting property bag at repeated wall-clock time with offset: reject"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..82314be3f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: The calendar name is case-insensitive +features: [Temporal] +---*/ + +const calendar = "IsO8601"; + +const timeZone = new Temporal.TimeZone("UTC"); +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = Temporal.ZonedDateTime.from(arg); +assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..8ee6bd1929 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Leap second is a valid ISO string for a calendar in a property bag +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = Temporal.ZonedDateTime.from(arg); +assert.sameValue( + result.calendarId, + "iso8601", + "leap second is a valid ISO string for calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..ecc9b11882 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-number.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.from(arg), + "Numbers cannot be used as a calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..943c87ea45 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-string.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: A calendar ID is valid input for Calendar +features: [Temporal] +---*/ + +const calendar = "iso8601"; + +const timeZone = new Temporal.TimeZone("UTC"); +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = Temporal.ZonedDateTime.from(arg); +assert.sameValue(result.calendarId, "iso8601", `Calendar created from string "${calendar}"`); +assert.sameValue(result.getISOFields().calendar, "iso8601", "calendar slot stores a string"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..54838e3b88 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.from(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => Temporal.ZonedDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..d736edeb21 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..41fdcd8e56 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar }; + +Temporal.ZonedDateTime.from(arg); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..83d3754b1e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-invalid-offset-string.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); + +const offsetOptions = ['use', 'prefer', 'ignore', 'reject']; + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +offsetOptions.forEach((offsetOption) => { + badOffsets.forEach((offset) => { + const arg = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.from(arg, { offset: offsetOption }), + `"${offset} is not a valid offset string (with offset option ${offsetOption})` + ); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js new file mode 100644 index 0000000000..fe08a8c7e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-offset-not-agreeing-with-timezone.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Property bag with offset property is rejected if offset does not agree with time zone +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); + +const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone }; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(properties), "offset property not matching time zone is rejected"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ec3e613a44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..aee08d30bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.from +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => { + const timeZone = new Temporal.TimeZone("UTC"); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no disambiguation case)` + ); + + const badTimeZone = { + getPossibleInstantsFor() { return []; }, + getOffsetNanosecondsFor: notCallable, + }; + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone: badTimeZone }, { offset: "ignore" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=ignore and no possible instants case)` + ); + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, offset: "+00:00", timeZone: badTimeZone }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no possible instants case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..1909eec0b4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + + assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..91995cd454 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + + assert.throws( + TypeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, timeZone }), + `invalid offset: ${String(wrongOffset)} (${typeof wrongOffset})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..53a73434f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone` + ); +}); + +timeZone = "2021-08-19T17:30Z"; +const result1 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone"); + +timeZone = "2021-08-19T17:30-07:00"; +const result2 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone"); + +timeZone = "2021-08-19T17:30[UTC]"; +const result3 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30Z[UTC]"; +const result4 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30-07:00[UTC]"; +const result5 = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..aa0c1f9f2c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-leap-second.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone"); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..0139a3ca0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-multiple-offsets.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..ef70bf8463 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-year-zero.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js new file mode 100644 index 0000000000..dbc83dc9ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +["UTC", "+01:30"].forEach((timeZone) => { + const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); + assert.sameValue(result.getISOFields().timeZone, timeZone, `Time zone created from string "${timeZone}"`); +}); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..31c0c2193c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, 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, () => Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..1aacf68105 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-calendar-annotation.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"], + ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.from(arg); + + assert.sameValue(result.calendarId, "iso8601", `calendar annotation (${description})`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..3f97fce22b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-critical-unknown-annotation.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..cfccdfcfb6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-date-with-utc-offset.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: UTC offset not valid with format that does not include a time +features: [Temporal] +---*/ + +const validStrings = [ + "1970-01-01T00Z[UTC]", + "1970-01-01T00Z[!UTC]", + "1970-01-01T00+00:00[UTC]", + "1970-01-01T00+00:00[!UTC]", +]; + +for (const arg of validStrings) { + const result = Temporal.ZonedDateTime.from(arg); + + assert.sameValue( + result.timeZoneId, + "UTC", + `"${arg}" is a valid UTC offset with time for ZonedDateTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `"${arg}" UTC offset without time is not valid for ZonedDateTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..28132f7b22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-calendar.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..c20aa8f769 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-multiple-time-zone.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js new file mode 100644 index 0000000000..80ac89f0cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-separators.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Time separator in string argument can vary +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00+00:00[UTC]", "uppercase T"], + ["1970-01-01t00:00+00:00[UTC]", "lowercase T"], + ["1970-01-01 00:00+00:00[UTC]", "space between date and time"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.from(arg); + + assert.sameValue( + result.timeZoneId, + "UTC", + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..bf8780dad0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-time-zone-annotation.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC]", "UTC", "named, with no offset"], + ["1970-01-01T00:00[!UTC]", "UTC", "named, with ! and no offset"], + ["1970-01-01T00:00[+00:00]", "+00:00", "numeric, with no offset"], + ["1970-01-01T00:00[!+00:00]", "+00:00", "numeric, with ! and no offset"], + ["1970-01-01T00:00Z[UTC]", "UTC", "named, with Z"], + ["1970-01-01T00:00Z[!UTC]", "UTC", "named, with Z and !"], + ["1970-01-01T00:00Z[+00:00]", "+00:00", "numeric, with Z"], + ["1970-01-01T00:00Z[!+00:00]", "+00:00", "numeric, with Z and !"], + ["1970-01-01T00:00+00:00[UTC]", "UTC", "named, with offset"], + ["1970-01-01T00:00+00:00[!UTC]", "UTC", "named, with offset and !"], + ["1970-01-01T00:00+00:00[+00:00]", "+00:00", "numeric, with offset"], + ["1970-01-01T00:00+00:00[!+00:00]", "+00:00", "numeric, with offset and !"], +]; + +tests.forEach(([arg, expectedZone, description]) => { + const result = Temporal.ZonedDateTime.from(arg); + + assert.sameValue( + result.timeZoneId, + expectedZone, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..121bfe98f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-string-unknown-annotation.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Various forms of unknown annotation +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"], + ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"], + ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +tests.forEach(([arg, description]) => { + const result = Temporal.ZonedDateTime.from(arg); + + assert.sameValue( + result.epochNanoseconds, + 0n, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js new file mode 100644 index 0000000000..874ca33f46 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for ZonedDateTime +features: [BigInt, Symbol, Temporal] +---*/ + +const primitiveTests = [ + [undefined, "undefined"], + [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"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => Temporal.ZonedDateTime.from(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => Temporal.ZonedDateTime.from(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.js new file mode 100644 index 0000000000..bfd60836f6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/argument-zoneddatetime.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: A ZonedDateTime object is copied, not returned directly +features: [Temporal] +---*/ + +const orig = new Temporal.ZonedDateTime(946684800_000_000_010n, new Temporal.TimeZone("UTC")); +const result = Temporal.ZonedDateTime.from(orig); + +assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "ZonedDateTime is copied"); +assert.sameValue(result.timeZone, orig.timeZone, "time zone is the same"); +assert.sameValue(result.getISOFields().calendar, orig.getISOFields().calendar, "calendar is the same"); + +assert.notSameValue( + result, + orig, + "When a ZonedDateTime is given, the returned value is not the original ZonedDateTime" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js new file mode 100644 index 0000000000..a30a941797 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/balance-negative-time-units.js @@ -0,0 +1,91 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-addtime step 8: + 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-adddatetime step 1: + 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-builtintimezonegetinstantfor step 13.a: + a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*). + sec-temporal-interpretisodatetimeoffset steps 4–10: + 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then + a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_). + ... + ... + 6. Assert: _offset_ is *"prefer"* or *"reject"*. + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + ... + 9. If _offset_ is *"reject"*, throw a *RangeError* exception. + 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal.zoneddatetime.from step 3: + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const shiftInstant = new Temporal.Instant(3661_001_001_001n); +const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); + +// This code path is encountered if offset is `ignore` or `prefer`, +// disambiguation is `earlier` and the shift is a spring-forward change +Temporal.ZonedDateTime.from({ + year: 1970, + month: 1, + day: 1, + hour: 1, + minute: 1, + second: 1, + millisecond: 1, + microsecond: 1, + nanosecond: 1, + timeZone: tz1, +}, { offset: "ignore", disambiguation: "earlier" }); + +const expected1 = [ + "1970-01-01T01:01:01.001001001", + "1970-01-01T01:01:01.001000999", +]; +assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected1); + +const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); + +Temporal.ZonedDateTime.from({ + year: 1970, + month: 1, + day: 1, + hour: 1, + minute: 1, + second: 1, + millisecond: 1, + microsecond: 1, + nanosecond: 1, + timeZone: tz2, +}, { offset: "prefer", disambiguation: "earlier" }); + +const expected2 = [ + "1970-01-01T01:01:01.001001001", + "1970-01-01T01:01:01.001000999", +]; +assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js new file mode 100644 index 0000000000..16e2a98a2b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/builtin.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Tests that Temporal.ZonedDateTime.from meets the requirements for built-in objects +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.ZonedDateTime.from), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.from), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.from), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.from.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..7746505700 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const timeZone = new Temporal.TimeZone("UTC"); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar }; +Temporal.ZonedDateTime.from(arg); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js new file mode 100644 index 0000000000..1d2a2354c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-fields-iterable.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.from step 3: + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). + sec-temporal-totemporalzoneddatetime step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js new file mode 100644 index 0000000000..bf244bde0b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.from step 3: + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). + sec-temporal-totemporalzoneddatetime step 2.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => { + const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone: "UTC", calendar: temporalObject }); + assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..a4b9393817 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/constructor-in-calendar-fields.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js new file mode 100644 index 0000000000..5880c713bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-invalid-string.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown when disambiguation option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal-totemporalzoneddatetime step 5: + 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. ... + b. Perform ? ToTemporalDisambiguation(_options_). + c. ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { disambiguation: "other string" })); + +const timeZone = new Temporal.TimeZone("UTC"); +const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 }; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { disambiguation: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js new file mode 100644 index 0000000000..b8b643694b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-undefined.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Fallback value for disambiguation option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal-totemporalzoneddatetime step 5: + 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const springForwardFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 30 }; +const fallBackFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 30 }; + +[ + [springForwardFields, 954671400_000_000_000n], + [fallBackFields, 972808200_000_000_000n], +].forEach(([fields, expected]) => { + const explicit = Temporal.ZonedDateTime.from(fields, { disambiguation: undefined }); + assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible (later)"); + + // See options-undefined.js for {} +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js new file mode 100644 index 0000000000..7807c1e4a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/disambiguation-wrong-type.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Type conversions for disambiguation option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal-totemporalzoneddatetime step 5: + 5. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. ... + b. Perform ? ToTemporalDisambiguation(_options_). + c. ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible", + (disambiguation) => Temporal.ZonedDateTime.from(datetime, { disambiguation }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr), +); + +const timeZone = new Temporal.TimeZone("UTC"); +const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 }; +TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible", + (disambiguation) => Temporal.ZonedDateTime.from(propertyBag, { disambiguation }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js new file mode 100644 index 0000000000..a849d5c07d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/duplicate-calendar-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + + +for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const timeZone = 'Europe/Paris' + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + + assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..73e3cd5a1c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.from +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + ["constrain", "reject"].forEach((overflow) => { + assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => Temporal.ZonedDateTime.from({ ...base, [prop]: obj }, { overflow })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js new file mode 100644 index 0000000000..0ece307056 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/leap-second.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Leap second is a valid ISO string for ZonedDateTime +features: [Temporal] +---*/ + +let arg = "2016-12-31T23:59:60+00:00[UTC]"; +const result = Temporal.ZonedDateTime.from(arg); +assert.sameValue( + result.epochNanoseconds, + 1_483_228_799_000_000_000n, + "leap second is a valid ISO string for ZonedDateTime" +); + +arg = "2000-05-02T12:34:56+23:59[+23:59:60]"; +assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + "leap second in time zone name not valid" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js new file mode 100644 index 0000000000..5fae89d3f3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Temporal.ZonedDateTime.from.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.ZonedDateTime.from, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js new file mode 100644 index 0000000000..914cb3142b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Temporal.ZonedDateTime.from.name is "from" +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.ZonedDateTime.from, "name", { + value: "from", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js new file mode 100644 index 0000000000..151912997a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/not-a-constructor.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Temporal.ZonedDateTime.from 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.ZonedDateTime.from(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.from), false, + "isConstructor(Temporal.ZonedDateTime.from)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js new file mode 100644 index 0000000000..48ce190761 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-primitive.js @@ -0,0 +1,52 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: options properties are extracted with string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "get options.disambiguation.toString", + "call options.disambiguation.toString", + "get options.offset.toString", + "call options.offset.toString", + "get options.overflow.toString", + "call options.overflow.toString", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { + disambiguation: "compatible", + offset: "ignore", + overflow: "reject", +}, "options"); + +const result = Temporal.ZonedDateTime.from("2001-09-09T01:46:40+00:00[UTC]", options); +assert.compareArray(actual, expected, "Successful call"); +assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n); + +actual.splice(0); // empty it for the next check +const failureExpected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; +assert.throws(TypeError, () => Temporal.ZonedDateTime.from(7, options)); +assert.compareArray(actual, failureExpected, "Failing call"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js new file mode 100644 index 0000000000..db9ae868af --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/observable-get-overflow-argument-string-invalid.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: options properties are extracted with ISO-invalid string argument. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "ownKeys options", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", +]; + +let actual = []; +const options = TemporalHelpers.propertyBagObserver(actual, { + disambiguation: "compatible", + offset: "ignore", + overflow: "reject", +}, "options"); + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T25:60:60+99:99[UTC]", options)); +assert.compareArray(actual, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js new file mode 100644 index 0000000000..e87884357b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown when offset option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal-totemporalzoneddatetime step 6: + 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + ... + c. Perform ? ToTemporalOffset(_options_, *"reject"*). + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(datetime, { offset: "other string" })); + +const timeZone = new Temporal.TimeZone("UTC"); +const propertyBag = { timeZone, year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 }; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js new file mode 100644 index 0000000000..5309b80807 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-overrides-critical-flag.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: > + The offset option always overrides the critical flag in a time zone annotation +features: [Temporal] +---*/ + +const useResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "use" }); +assert.sameValue( + useResult.epochNanoseconds, + 1665193020000000000n, + "exact time is unchanged with offset = use, despite critical flag" +); + +const ignoreResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "ignore" }); +assert.sameValue( + ignoreResult.epochNanoseconds, + 1665167820000000000n, + "wall time is unchanged with offset = ignore, despite critical flag" +); + +const preferResult = Temporal.ZonedDateTime.from("2022-10-07T18:37-07:00[!UTC]", { offset: "prefer" }); +assert.sameValue( + useResult.epochNanoseconds, + 1665193020000000000n, + "offset is recalculated with offset = prefer, despite critical flag" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js new file mode 100644 index 0000000000..acb64f48c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Fallback value for offset option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal-totemporalzoneddatetime step 6: + 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + ... + c. Perform ? ToTemporalOffset(_options_, *"reject"*). + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("-04:00"); +const propertyBag = { timeZone, offset: "+01:00", year: 2020, month: 2, day: 16, hour: 23, minute: 45 }; + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { offset: undefined }), "default offset is reject"); +// See options-undefined.js for {} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js new file mode 100644 index 0000000000..157c789707 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/offset-wrong-type.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Type conversions for offset option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal-totemporalzoneddatetime step 6: + 6. Let _offset_ be ? ToTemporalOffset(_options_, *"reject"*). + sec-temporal.zoneddatetime.from step 2: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + ... + c. Perform ? ToTemporalOffset(_options_, *"reject"*). + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("offset", "reject", + (offset) => Temporal.ZonedDateTime.from(datetime, { offset }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr), +); + +const timeZone = new Temporal.TimeZone("UTC"); +const propertyBag = { timeZone, offset: "+00:00", year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321 }; +TemporalHelpers.checkStringOptionWrongType("offset", "reject", + (offset) => Temporal.ZonedDateTime.from(propertyBag, { offset }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js new file mode 100644 index 0000000000..a1c9e66d59 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-object.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: Empty object may be used as options +features: [Temporal] +---*/ + +assert.sameValue( + Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, {}).epochNanoseconds, 217123200000000000n, "UTC", + "options may be an empty plain object" +); + +assert.sameValue( + Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, () => {}).epochNanoseconds, 217123200000000000n, "UTC", + "options may be an empty function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js new file mode 100644 index 0000000000..f6fb1c42d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-undefined.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +includes: [temporalHelpers.js] +description: Verify that undefined options are handled correctly. +features: [Temporal] +---*/ + +const overflowFields = { year: 2000, month: 13, day: 2, timeZone: "UTC" }; + +const overflowExplicit = Temporal.ZonedDateTime.from(overflowFields, undefined); +assert.sameValue(overflowExplicit.month, 12, "default overflow is constrain"); + +const overflowPropertyImplicit = Temporal.ZonedDateTime.from(overflowFields, {}); +assert.sameValue(overflowPropertyImplicit.month, 12, "default overflow is constrain"); + +const overflowImplicit = Temporal.ZonedDateTime.from(overflowFields); +assert.sameValue(overflowImplicit.month, 12, "default overflow is constrain"); + +const timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const disambiguationEarlierFields = { timeZone, year: 2000, month: 10, day: 29, hour: 1, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }; +const disambiguationLaterFields = { timeZone, year: 2000, month: 4, day: 2, hour: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[ + [disambiguationEarlierFields, 972808496987654321n], + [disambiguationLaterFields, 954671696987654321n], +].forEach(([fields, expected]) => { + const explicit = Temporal.ZonedDateTime.from(fields, undefined); + assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible"); + + const propertyImplicit = Temporal.ZonedDateTime.from(fields, {}); + assert.sameValue(propertyImplicit.epochNanoseconds, expected, "default disambiguation is compatible"); + + const implicit = Temporal.ZonedDateTime.from(fields); + assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible"); +}); + +const offsetFields = { year: 2000, month: 5, day: 2, offset: "+23:59", timeZone: "UTC" }; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, undefined), "default offset is reject"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields, {}), "default offset is reject"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(offsetFields), "default offset is reject"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js new file mode 100644 index 0000000000..060df72e6f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/options-wrong-type.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +for (const value of badOptions) { + assert.throws(TypeError, () => Temporal.ZonedDateTime.from({ year: 1976, month: 11, day: 18, timeZone: "UTC" }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js new file mode 100644 index 0000000000..96dacb4a2d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/order-of-operations.js @@ -0,0 +1,132 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Properties on objects passed to from() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.extra", + "get options.extra", + // ToTemporalCalendar + "get item.calendar", + "has item.calendar.dateAdd", + "has item.calendar.dateFromFields", + "has item.calendar.dateUntil", + "has item.calendar.day", + "has item.calendar.dayOfWeek", + "has item.calendar.dayOfYear", + "has item.calendar.daysInMonth", + "has item.calendar.daysInWeek", + "has item.calendar.daysInYear", + "has item.calendar.fields", + "has item.calendar.id", + "has item.calendar.inLeapYear", + "has item.calendar.mergeFields", + "has item.calendar.month", + "has item.calendar.monthCode", + "has item.calendar.monthDayFromFields", + "has item.calendar.monthsInYear", + "has item.calendar.weekOfYear", + "has item.calendar.year", + "has item.calendar.yearMonthFromFields", + "has item.calendar.yearOfWeek", + "get item.calendar.dateFromFields", + "get item.calendar.fields", + "call item.calendar.fields", + // PrepareTemporalFields + "get item.day", + "get item.day.valueOf", + "call item.day.valueOf", + "get item.hour", + "get item.hour.valueOf", + "call item.hour.valueOf", + "get item.microsecond", + "get item.microsecond.valueOf", + "call item.microsecond.valueOf", + "get item.millisecond", + "get item.millisecond.valueOf", + "call item.millisecond.valueOf", + "get item.minute", + "get item.minute.valueOf", + "call item.minute.valueOf", + "get item.month", + "get item.month.valueOf", + "call item.month.valueOf", + "get item.monthCode", + "get item.monthCode.toString", + "call item.monthCode.toString", + "get item.nanosecond", + "get item.nanosecond.valueOf", + "call item.nanosecond.valueOf", + "get item.offset", + "get item.offset.toString", + "call item.offset.toString", + "get item.second", + "get item.second.valueOf", + "call item.second.valueOf", + "get item.timeZone", + "get item.year", + "get item.year.valueOf", + "call item.year.valueOf", + "has item.timeZone.getOffsetNanosecondsFor", + "has item.timeZone.getPossibleInstantsFor", + "has item.timeZone.id", + // InterpretTemporalDateTimeFields + "get options.disambiguation.toString", + "call options.disambiguation.toString", + "get options.offset.toString", + "call options.offset.toString", + "get options.overflow.toString", + "call options.overflow.toString", + "call item.calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get item.timeZone.getOffsetNanosecondsFor", + "get item.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call item.timeZone.getPossibleInstantsFor", + "call item.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const from = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "item.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "item.timeZone"), +}, "item"); + +function createOptionsObserver({ overflow = "constrain", disambiguation = "compatible", offset = "reject" } = {}) { + return TemporalHelpers.propertyBagObserver(actual, { + overflow, + disambiguation, + offset, + extra: "property", + }, "options"); +} + +Temporal.ZonedDateTime.from(from, createOptionsObserver()); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js new file mode 100644 index 0000000000..4131d5674c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-invalid-string.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: RangeError thrown when overflow option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-totemporalzoneddatetime steps 2–3: + 2. If Type(_item_) is Object, then + ... + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + 3. Else, + a. Perform ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.from steps 2–3: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Perform ? ToTemporalOverflow(_options_). + ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +features: [Temporal] +---*/ + +const validValues = [ + new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"), + { year: 2000, month: 5, day: 2, hour: 12, timeZone: "UTC" }, + "2001-09-09T01:46:40.987654321+00:00[UTC]", +]; + +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const value of validValues) { + for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(value, { overflow }), + `invalid overflow ("${overflow}")` + ); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js new file mode 100644 index 0000000000..f125f1c935 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-undefined.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Fallback value for overflow option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-totemporalzoneddatetime steps 2–3: + 2. If Type(_item_) is Object, then + ... + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + 3. Else, + a. Perform ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.from steps 2–3: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Perform ? ToTemporalOverflow(_options_). + ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +features: [Temporal] +---*/ + +const validValues = [ + new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"), + "2001-09-09T01:46:40.987654321+00:00[UTC]", +]; +validValues.forEach((value) => { + const explicit = Temporal.ZonedDateTime.from(value, { overflow: undefined }); + assert.sameValue(explicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored"); + const implicit = Temporal.ZonedDateTime.from(value, {}); + assert.sameValue(implicit.epochNanoseconds, 1_000_000_000_987_654_321n, "overflow is ignored"); +}); + +const propertyBag = { year: 2000, month: 15, day: 34, hour: 12, timeZone: "UTC" }; +const explicit = Temporal.ZonedDateTime.from(propertyBag, { overflow: undefined }); +assert.sameValue(explicit.epochNanoseconds, 978_264_000_000_000_000n, "default overflow is constrain"); + +// See options-undefined for {} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js new file mode 100644 index 0000000000..90a17e7e90 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/overflow-wrong-type.js @@ -0,0 +1,66 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Type conversions for overflow option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-totemporalzoneddatetime steps 2–3: + 2. If Type(_item_) is Object, then + ... + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + 3. Else, + a. Perform ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.from steps 2–3: + 2. If Type(_item_) is Object and _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Perform ? ToTemporalOverflow(_options_). + ... + d. Return ... + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const validValues = [ + new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"), + "2001-09-09T01:46:40.987654321+00:00[UTC]", +]; +validValues.forEach((value) => TemporalHelpers.checkStringOptionWrongType("overflow", "constrain", + (overflow) => Temporal.ZonedDateTime.from(value, { overflow }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_987_654_321n, descr), +)); + +// See TemporalHelpers.checkStringOptionWrongType(); this code path has +// different expectations for observable calls +const propertyBag = { year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, timeZone: "UTC" }; + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: null }), "null"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: true }), "true"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: false }), "false"); +assert.throws(TypeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: Symbol() }), "symbol"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2n }), "bigint"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: 2 }), "number"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(propertyBag, { overflow: {} }), "plain object"); + +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). +const expected = [ + "get overflow.toString", + "call overflow.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); +const result = Temporal.ZonedDateTime.from(propertyBag, { overflow: observer }); +assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, "object with toString"); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js new file mode 100644 index 0000000000..672d99a46b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: The "from" property of Temporal.ZonedDateTime +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.from, + "function", + "`typeof ZonedDateTime.from` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime, "from", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js new file mode 100644 index 0000000000..f05165d2ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/proto-in-calendar-fields.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..bf5aeb22d8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/read-time-fields-before-datefromfields.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.from step 3: + 3. Return ? ToTemporalDateTime(_item_, _options_). + sec-temporal-totemporalzoneddatetime step 2.j: + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar }); + +assert.sameValue(datetime.hour, 12, "hour value"); +assert.sameValue(datetime.minute, 34, "minute value"); +assert.sameValue(datetime.second, 56, "second value"); +assert.sameValue(datetime.millisecond, 987, "millisecond value"); +assert.sameValue(datetime.microsecond, 654, "microsecond value"); +assert.sameValue(datetime.nanosecond, 321, "nanosecond value"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js new file mode 100644 index 0000000000..dd7cb1462d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/subclassing-ignored.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: The receiver is never called when calling from() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnoredStatic( + Temporal.ZonedDateTime, + "from", + ["2000-01-01T00:00:00.00000001+00:00[UTC]"], + (result) => { + assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result"); + assert.sameValue(result.year, 2000, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js new file mode 100644 index 0000000000..e07176d96a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-case-insensitive.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.from +description: Time zone names are case insensitive +features: [Temporal] +---*/ + +const timeZone = 'uTc'; +const result = Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..94a04acb78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.from step 3: + 3. Return ? ToTemporalZonedDateTime(_item_, _options_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-05-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + Temporal.ZonedDateTime.from({ year: 2000, month: 5, day: 2, timeZone }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js new file mode 100644 index 0000000000..b1789110a8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/year-zero.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-0000000-01-01T00:02Z[UTC]", + "-0000000-01-01T00:02+00:00[UTC]", + "-0000000-01-01T00:02:00.000000000+00:00[UTC]", +]; + +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js new file mode 100644 index 0000000000..966415633f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string-multiple-offsets.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset +features: [Temporal] +---*/ + +let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]"; + +const result = Temporal.ZonedDateTime.from(str); +assert.sameValue(result.timeZoneId, "+01:35", "ISO offset, sub-minute offset trailing-zeroes"); + +str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]"; +assert.throws( + RangeError, + () => Temporal.ZonedDateTime.from(str), + "Trailing zeroes not allowed for sub-minute time zone identifiers" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js new file mode 100644 index 0000000000..c96cf60ffb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/from/zoneddatetime-string.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.from +description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances +features: [Temporal] +---*/ + +let str = "1970-01-01T00:00"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "bare date-time string is not a ZonedDateTime"); +str = "1970-01-01T00:00Z"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + Z is not a ZonedDateTime"); +str = "1970-01-01T00:00+01:00"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset is not a ZonedDateTime"); + +str = "1970-01-01T00:00[+01:00]"; +const result1 = Temporal.ZonedDateTime.from(str); +assert.sameValue(result1.epochNanoseconds, -3600_000_000_000n, "date-time + IANA annotation preserves wall time in the time zone"); +assert.sameValue(result1.timeZoneId, "+01:00", "IANA annotation is not ignored"); + +str = "1970-01-01T00:00Z[+01:00]"; +const result2 = Temporal.ZonedDateTime.from(str); +assert.sameValue(result2.epochNanoseconds, 0n, "date-time + Z + IANA annotation preserves exact time in the time zone"); +assert.sameValue(result2.timeZoneId, "+01:00", "IANA annotation is not ignored"); + +str = "1970-01-01T00:00+01:00[+01:00]"; +const result3 = Temporal.ZonedDateTime.from(str); +assert.sameValue(result3.epochNanoseconds, -3600_000_000_000n, "date-time + offset + IANA annotation ensures both exact and wall time match"); +assert.sameValue(result3.timeZoneId, "+01:00", "IANA annotation is not ignored"); + +str = "1970-01-01T00:00-04:15[+01:00]"; +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); +assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { offset: "reject" }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch (explicit reject option)"); +const result4 = Temporal.ZonedDateTime.from(str, { offset: "ignore" }); +assert.sameValue(result4.epochNanoseconds, -3600_000_000_000n, "date-time + wrong offset + IANA annotation preserves wall time in the time zone (offset: ignore option)"); +assert.sameValue(result4.timeZoneId, "+01:00", "IANA annotation is not ignored"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js new file mode 100644 index 0000000000..c07bca45c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Temporal.ZonedDateTime.length is 2 +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.ZonedDateTime, "length", { + value: 2, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js new file mode 100644 index 0000000000..354baa210b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/missing-arguments.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: TypeError thrown when constructor invoked with no argument +features: [Temporal] +---*/ + +assert.throws(TypeError, () => new Temporal.ZonedDateTime()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js new file mode 100644 index 0000000000..3b471b5c74 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Temporal.ZonedDateTime.name is "ZonedDateTime" +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.ZonedDateTime, "name", { + value: "ZonedDateTime", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js new file mode 100644 index 0000000000..0a2e8d4f7a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: The "ZonedDateTime" property of Temporal +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime, + "function", + "`typeof ZonedDateTime` is `function`" +); + +verifyProperty(Temporal, "ZonedDateTime", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js new file mode 100644 index 0000000000..438624bc1a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-max.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Maximum allowed duration +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const maxCases = [ + ["P273790Y8M12D", "string with max years"], + [{ years: 273790, months: 8, days: 12 }, "property bag with max years"], + ["P3285488M12D", "string with max months"], + [{ months: 3285488, days: 12 }, "property bag with max months"], + ["P14285714W2D", "string with max weeks"], + [{ weeks: 14285714, days: 2 }, "property bag with max weeks"], + ["P100000000D", "string with max days"], + [{ days: 100000000 }, "property bag with max days"], + ["PT2400000000H", "string with max hours"], + [{ hours: 2400000000 }, "property bag with max hours"], + ["PT144000000000M", "string with max minutes"], + [{ minutes: 144000000000 }, "property bag with max minutes"], + ["PT8640000000000S", "string with max seconds"], + [{ seconds: 8640000000000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = instance.add(arg); + assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`); +} + +const minCases = [ + ["-P273790Y8M11D", "string with min years"], + [{ years: -273790, months: -8, days: -11 }, "property bag with min years"], + ["-P3285488M11D", "string with min months"], + [{ months: -3285488, days: -11 }, "property bag with min months"], + ["-P14285714W2D", "string with min weeks"], + [{ weeks: -14285714, days: -2 }, "property bag with min weeks"], + ["-P100000000D", "string with min days"], + [{ days: -100000000 }, "property bag with min days"], + ["-PT2400000000H", "string with min hours"], + [{ hours: -2400000000 }, "property bag with min hours"], + ["-PT144000000000M", "string with min minutes"], + [{ minutes: -144000000000 }, "property bag with min minutes"], + ["-PT8640000000000S", "string with min seconds"], + [{ seconds: -8640000000000 }, "property bag with min seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = instance.add(arg); + assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js new file mode 100644 index 0000000000..b7d9b32907 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-duration-out-of-range.js @@ -0,0 +1,75 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Duration-like argument that is out of range +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const cases = [ + // 2^32 = 4294967296 + ["P4294967296Y", "string with years > max"], + [{ years: 4294967296 }, "property bag with years > max"], + ["-P4294967296Y", "string with years < min"], + [{ years: -4294967296 }, "property bag with years < min"], + ["P4294967296M", "string with months > max"], + [{ months: 4294967296 }, "property bag with months > max"], + ["-P4294967296M", "string with months < min"], + [{ months: -4294967296 }, "property bag with months < min"], + ["P4294967296W", "string with weeks > max"], + [{ weeks: 4294967296 }, "property bag with weeks > max"], + ["-P4294967296W", "string with weeks < min"], + [{ weeks: -4294967296 }, "property bag with weeks < min"], + + // ceil(max safe integer / 86400) = 104249991375 + ["P104249991375D", "string with days > max"], + [{ days: 104249991375 }, "property bag with days > max"], + ["P104249991374DT24H", "string where hours balance into days > max"], + [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"], + ["-P104249991375D", "string with days < min"], + [{ days: -104249991375 }, "property bag with days < min"], + ["-P104249991374DT24H", "string where hours balance into days < min"], + [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"], + + // ceil(max safe integer / 3600) = 2501999792984 + ["PT2501999792984H", "string with hours > max"], + [{ hours: 2501999792984 }, "property bag with hours > max"], + ["PT2501999792983H60M", "string where minutes balance into hours > max"], + [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"], + ["-PT2501999792984H", "string with hours < min"], + [{ hours: -2501999792984 }, "property bag with hours < min"], + ["-PT2501999792983H60M", "string where minutes balance into hours < min"], + [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"], + + // ceil(max safe integer / 60) = 150119987579017 + ["PT150119987579017M", "string with minutes > max"], + [{ minutes: 150119987579017 }, "property bag with minutes > max"], + ["PT150119987579016M60S", "string where seconds balance into minutes > max"], + [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"], + ["-PT150119987579017M", "string with minutes < min"], + [{ minutes: -150119987579017 }, "property bag with minutes < min"], + ["-PT150119987579016M60S", "string where seconds balance into minutes < min"], + [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"], + + // 2^53 = 9007199254740992 + ["PT9007199254740992S", "string with seconds > max"], + [{ seconds: 9007199254740992 }, "property bag with seconds > max"], + [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"], + [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"], + [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"], + ["-PT9007199254740992S", "string with seconds < min"], + [{ seconds: -9007199254740992 }, "property bag with seconds < min"], + [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"], + [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"], + [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"], +]; + +for (const [arg, descr] of cases) { + assert.throws(RangeError, () => instance.add(arg), `${descr} is out of range`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js new file mode 100644 index 0000000000..7cd3400b6f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +assert.throws( + TypeError, + () => instance.add({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.add({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.add({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js new file mode 100644 index 0000000000..1a85571cac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-mixed-sign.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +["constrain", "reject"].forEach((overflow) => { + assert.throws( + RangeError, + () => instance.add({ hours: 1, minutes: -30 }, { overflow }), + `mixed positive and negative values always throw (overflow = "${overflow}")` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js new file mode 100644 index 0000000000..352605e388 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-not-object.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Passing a primitive other than string to add() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +assert.throws(TypeError, () => instance.add(undefined), "undefined"); +assert.throws(TypeError, () => instance.add(null), "null"); +assert.throws(TypeError, () => instance.add(true), "boolean"); +assert.throws(RangeError, () => instance.add(""), "empty string"); +assert.throws(TypeError, () => instance.add(Symbol()), "Symbol"); +assert.throws(TypeError, () => instance.add(7), "number"); +assert.throws(TypeError, () => instance.add(7n), "bigint"); +assert.throws(TypeError, () => instance.add([]), "array"); +assert.throws(TypeError, () => instance.add(() => {}), "function"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js new file mode 100644 index 0000000000..f619de4aed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-singular-properties.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.add(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..9a129e78a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Strings with fractional duration units are rounded with the correct rounding mode +features: [Temporal] +---*/ + +const epoch = new Temporal.ZonedDateTime(0n, "UTC"); + +assert.sameValue(epoch.add("PT1.03125H").epochNanoseconds, 3712_500_000_000n, + "positive fractional units rounded with correct rounding mode"); +assert.sameValue(epoch.add("-PT1.03125H").epochNanoseconds, -3712_500_000_000n, + "negative fractional units rounded with correct rounding mode"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..9207d8d957 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/argument-string-negative-fractional-units.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Strings with fractional duration units are treated with the correct sign +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const resultHours = instance.add("-PT24.567890123H"); +assert.sameValue(resultHours.epochNanoseconds, 999_911_555_595_557_200n, "negative fractional hours"); + +const resultMinutes = instance.add("-PT1440.567890123M"); +assert.sameValue(resultMinutes.epochNanoseconds, 999_913_565_926_592_620n, "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js new file mode 100644 index 0000000000..434707b74c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const add = Temporal.ZonedDateTime.prototype.add; + +assert.sameValue(typeof add, "function"); + +const args = [new Temporal.Duration(5)]; + +assert.throws(TypeError, () => add.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => add.apply(null, args), "null"); +assert.throws(TypeError, () => add.apply(true, args), "true"); +assert.throws(TypeError, () => add.apply("", args), "empty string"); +assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => add.apply(1, args), "1"); +assert.throws(TypeError, () => add.apply({}, args), "plain object"); +assert.throws(TypeError, () => add.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => add.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..c6d01713a3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd"); +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateAdd should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.add(new Temporal.Duration(1)); + +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..b145bfd25c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.add(new Temporal.Duration(1)); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js new file mode 100644 index 0000000000..d075aa0649 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: > + Tests that Temporal.ZonedDateTime.prototype.add + 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.ZonedDateTime.prototype.add), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.add), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.add), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.add.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js new file mode 100644 index 0000000000..9cc7fd7766 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/calendar-dateadd.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: ZonedDateTime.prototype.add should call dateAdd with the appropriate values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(plainDate, duration, options) { + ++calls; + TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument"); + TemporalHelpers.assertDuration(duration, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument"); + assert.sameValue(typeof options, "object", "options argument: type"); + assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype"); + return super.dateAdd(plainDate, duration, options); + } +} + +const zonedDateTime = new Temporal.ZonedDateTime(1584189296_987654321n, + new Temporal.TimeZone("UTC"), new CustomCalendar()); +const result = zonedDateTime.add({ months: 10, hours: 14 }); +assert.sameValue(result.epochNanoseconds, 1610678096_987654321n); +assert.sameValue(calls, 1, "should have called dateAdd"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js new file mode 100644 index 0000000000..5b3b5b0b0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Temporal.ZonedDateTime.prototype.add.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.ZonedDateTime.prototype.add, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js new file mode 100644 index 0000000000..e343ddaa3c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Temporal.ZonedDateTime.prototype.add.name is "add". +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.ZonedDateTime.prototype.add, "name", { + value: "add", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js new file mode 100644 index 0000000000..b52a6329bd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.add(new Temporal.Duration(0, 0, 0, 1)); +assert.sameValue(result.epochNanoseconds, -13763364_999_999_999n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..73165990f4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.add({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.add({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js new file mode 100644 index 0000000000..75195269d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: > + Temporal.ZonedDateTime.prototype.add 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.ZonedDateTime.prototype.add(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.add), false, + "isConstructor(Temporal.ZonedDateTime.prototype.add)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js new file mode 100644 index 0000000000..c38ecfff0a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-object.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.add({ years: 1 }, {}); +assert.sameValue( + result1.epochNanoseconds, 31536000000000000n, "UTC", + "options may be an empty plain object" +); + +const result2 = instance.add({ years: 1 }, () => {}); +assert.sameValue( + result2.epochNanoseconds, 31536000000000000n, "UTC", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js new file mode 100644 index 0000000000..307f1006f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-undefined.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(949322096_987_654_321n, "UTC"); +const duration = { months: 1 }; + +const explicit = datetime.add(duration, undefined); +assert.sameValue(explicit.month, 2, "default overflow is constrain"); +assert.sameValue(explicit.day, 29, "default overflow is constrain"); + +const implicit = datetime.add(duration); +assert.sameValue(implicit.month, 2, "default overflow is constrain"); +assert.sameValue(implicit.day, 29, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js new file mode 100644 index 0000000000..395864ed9d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.add({ years: 1 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js new file mode 100644 index 0000000000..26fc1c9299 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js @@ -0,0 +1,84 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Properties on objects passed to add() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDurationRecord + "get duration.days", + "get duration.days.valueOf", + "call duration.days.valueOf", + "get duration.hours", + "get duration.hours.valueOf", + "call duration.hours.valueOf", + "get duration.microseconds", + "get duration.microseconds.valueOf", + "call duration.microseconds.valueOf", + "get duration.milliseconds", + "get duration.milliseconds.valueOf", + "call duration.milliseconds.valueOf", + "get duration.minutes", + "get duration.minutes.valueOf", + "call duration.minutes.valueOf", + "get duration.months", + "get duration.months.valueOf", + "call duration.months.valueOf", + "get duration.nanoseconds", + "get duration.nanoseconds.valueOf", + "call duration.nanoseconds.valueOf", + "get duration.seconds", + "get duration.seconds.valueOf", + "call duration.seconds.valueOf", + "get duration.weeks", + "get duration.weeks.valueOf", + "call duration.weeks.valueOf", + "get duration.years", + "get duration.years.valueOf", + "call duration.years.valueOf", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + // AddZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + "call this.calendar.dateAdd", + // ... inside Calendar.p.dateAdd + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", + // AddZonedDateTime again + "call this.timeZone.getPossibleInstantsFor", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +const duration = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "duration"); + +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); + +instance.add(duration, options); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js new file mode 100644 index 0000000000..b46102175e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-invalid-string.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: RangeError thrown when overflow option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.add step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => datetime.add(duration, { overflow }), + `invalid overflow ("${overflow}")` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js new file mode 100644 index 0000000000..9a2c129c68 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Fallback value for overflow option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.add step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-1n, "UTC"); +const duration = new Temporal.Duration(0, 2); + +const explicit = datetime.add(duration, { overflow: undefined }); +assert.sameValue(explicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain"); +const implicit = datetime.add(duration, {}); +assert.sameValue(implicit.epochNanoseconds, 5097599_999_999_999n, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js new file mode 100644 index 0000000000..7eeecf0643 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/overflow-wrong-type.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Type conversions for overflow option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.add step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); +TemporalHelpers.checkStringOptionWrongType("overflow", "constrain", + (overflow) => datetime.add(duration, { overflow }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_086_400_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js new file mode 100644 index 0000000000..b0332ada78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: The "add" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.add, + "function", + "`typeof ZonedDateTime.prototype.add` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "add", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js new file mode 100644 index 0000000000..a8205a7ac3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "add", + [{ nanoseconds: 5 }], + (result) => { + assert.sameValue(result.epochNanoseconds, 15n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 15, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ceb2748710 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.add(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..d26e405e47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.add(duration), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..8359fc25fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.add(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..90b8633f64 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/add/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.add +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.add(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js new file mode 100644 index 0000000000..b4a5359155 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.calendarid +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const calendarId = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "calendarId").get; + +assert.sameValue(typeof calendarId, "function"); + +assert.throws(TypeError, () => calendarId.call(undefined), "undefined"); +assert.throws(TypeError, () => calendarId.call(null), "null"); +assert.throws(TypeError, () => calendarId.call(true), "true"); +assert.throws(TypeError, () => calendarId.call(""), "empty string"); +assert.throws(TypeError, () => calendarId.call(Symbol()), "symbol"); +assert.throws(TypeError, () => calendarId.call(1), "1"); +assert.throws(TypeError, () => calendarId.call({}), "plain object"); +assert.throws(TypeError, () => calendarId.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => calendarId.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..57e69d096f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.calendarid +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.calendarId; + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js new file mode 100644 index 0000000000..3f846c190d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.calendarid +description: The "calendarId" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "calendarId"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/calendarId/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js new file mode 100644 index 0000000000..469f48b369 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/constructor.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.constructor +description: Test for Temporal.ZonedDateTime.prototype.constructor. +info: The initial value of Temporal.ZonedDateTime.prototype.constructor is %Temporal.ZonedDateTime%. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.ZonedDateTime.prototype, "constructor", { + value: Temporal.ZonedDateTime, + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js new file mode 100644 index 0000000000..d60c094ecd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/balance-negative-time-units.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.day +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.day step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(86400_000_000_001n, tz); + +assert.sameValue(datetime.day, 1); +assert.sameValue(datetime.hour, 23); +assert.sameValue(datetime.minute, 59); +assert.sameValue(datetime.second, 59); +assert.sameValue(datetime.millisecond, 999); +assert.sameValue(datetime.microsecond, 999); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js new file mode 100644 index 0000000000..bdd693e577 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const day = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "day").get; + +assert.sameValue(typeof day, "function"); + +assert.throws(TypeError, () => day.call(undefined), "undefined"); +assert.throws(TypeError, () => day.call(null), "null"); +assert.throws(TypeError, () => day.call(true), "true"); +assert.throws(TypeError, () => day.call(""), "empty string"); +assert.throws(TypeError, () => day.call(Symbol()), "symbol"); +assert.throws(TypeError, () => day.call(1), "1"); +assert.throws(TypeError, () => day.call({}), "plain object"); +assert.throws(TypeError, () => day.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => day.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..4588bd3268 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.day +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dayOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "day"); +Object.defineProperty(Temporal.Calendar.prototype, "day", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("day should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.day; + +Object.defineProperty(Temporal.Calendar.prototype, "day", dayOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..9c13663214 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.day +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.day; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js new file mode 100644 index 0000000000..2b66c43581 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: Custom calendar tests for day(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + day(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "day arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.day; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js new file mode 100644 index 0000000000..a24650e719 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: The "day" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "day"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..984b2b5151 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.day); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..e9866ca2f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.day, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..9c32c09188 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.day); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..360c9485bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.day); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js new file mode 100644 index 0000000000..4ea14ef7fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/day/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.day +description: Validate result returned from calendar day() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + day() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.day, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js new file mode 100644 index 0000000000..5dc562474e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const dayOfWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfWeek").get; + +assert.sameValue(typeof dayOfWeek, "function"); + +assert.throws(TypeError, () => dayOfWeek.call(undefined), "undefined"); +assert.throws(TypeError, () => dayOfWeek.call(null), "null"); +assert.throws(TypeError, () => dayOfWeek.call(true), "true"); +assert.throws(TypeError, () => dayOfWeek.call(""), "empty string"); +assert.throws(TypeError, () => dayOfWeek.call(Symbol()), "symbol"); +assert.throws(TypeError, () => dayOfWeek.call(1), "1"); +assert.throws(TypeError, () => dayOfWeek.call({}), "plain object"); +assert.throws(TypeError, () => dayOfWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => dayOfWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..a694538559 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.dayofweek +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dayOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfWeek"); +Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dayOfWeek should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.dayOfWeek; + +Object.defineProperty(Temporal.Calendar.prototype, "dayOfWeek", dayOfWeekOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..e4ebad62de --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.dayofweek +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.dayOfWeek; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js new file mode 100644 index 0000000000..3904dc8f92 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: Custom calendar tests for dayOfWeek(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dayOfWeek(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "dayOfWeek arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.dayOfWeek; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js new file mode 100644 index 0000000000..4092408b2f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: The "dayOfWeek" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfWeek"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..d96b7aeee8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.dayOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..0656f5826b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.dayOfWeek, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..61678b900c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.dayOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..787e54216f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.dayOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js new file mode 100644 index 0000000000..b8d1a08b7f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfWeek/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofweek +description: Validate result returned from calendar dayOfWeek() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + dayOfWeek() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.dayOfWeek, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js new file mode 100644 index 0000000000..2aeb7d146f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const dayOfYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfYear").get; + +assert.sameValue(typeof dayOfYear, "function"); + +assert.throws(TypeError, () => dayOfYear.call(undefined), "undefined"); +assert.throws(TypeError, () => dayOfYear.call(null), "null"); +assert.throws(TypeError, () => dayOfYear.call(true), "true"); +assert.throws(TypeError, () => dayOfYear.call(""), "empty string"); +assert.throws(TypeError, () => dayOfYear.call(Symbol()), "symbol"); +assert.throws(TypeError, () => dayOfYear.call(1), "1"); +assert.throws(TypeError, () => dayOfYear.call({}), "plain object"); +assert.throws(TypeError, () => dayOfYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => dayOfYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..e33ff51617 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.dayofyear +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dayOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dayOfYear"); +Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dayOfYear should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.dayOfYear; + +Object.defineProperty(Temporal.Calendar.prototype, "dayOfYear", dayOfYearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..b9eb0b3f5e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.dayofyear +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.dayOfYear; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js new file mode 100644 index 0000000000..7823d2e9f8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: Custom calendar tests for dayOfYear(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dayOfYear(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "dayOfYear arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.dayOfYear; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js new file mode 100644 index 0000000000..f43bc5310e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: The "dayOfYear" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "dayOfYear"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..7b4c325aa1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.dayOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..2adbe4f754 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.dayOfYear, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..a8ab2fc73f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.dayOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7479cf1bc7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.dayOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js new file mode 100644 index 0000000000..b2f7a1bb7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/dayOfYear/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.dayofyear +description: Validate result returned from calendar dayOfYear() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + dayOfYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.dayOfYear, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js new file mode 100644 index 0000000000..cfa594980e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const daysInMonth = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInMonth").get; + +assert.sameValue(typeof daysInMonth, "function"); + +assert.throws(TypeError, () => daysInMonth.call(undefined), "undefined"); +assert.throws(TypeError, () => daysInMonth.call(null), "null"); +assert.throws(TypeError, () => daysInMonth.call(true), "true"); +assert.throws(TypeError, () => daysInMonth.call(""), "empty string"); +assert.throws(TypeError, () => daysInMonth.call(Symbol()), "symbol"); +assert.throws(TypeError, () => daysInMonth.call(1), "1"); +assert.throws(TypeError, () => daysInMonth.call({}), "plain object"); +assert.throws(TypeError, () => daysInMonth.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => daysInMonth.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..4ef8abe93b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinmonth +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const daysInMonthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInMonth"); +Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("daysInMonth should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInMonth; + +Object.defineProperty(Temporal.Calendar.prototype, "daysInMonth", daysInMonthOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..037834f04f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinmonth +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInMonth; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js new file mode 100644 index 0000000000..c6c23338df --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: Custom calendar tests for daysInMonth(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + daysInMonth(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInMonth arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.daysInMonth; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js new file mode 100644 index 0000000000..4597630f20 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: The "daysInMonth" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInMonth"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ab7334b0f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInMonth); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..9cca406d6a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.daysInMonth, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..e64f3b4966 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInMonth); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..f61a214c3e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.daysInMonth); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js new file mode 100644 index 0000000000..d54b1355f3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInMonth/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinmonth +description: Validate result returned from calendar daysInMonth() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + daysInMonth() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.daysInMonth, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js new file mode 100644 index 0000000000..6dcdc66d0a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const daysInWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInWeek").get; + +assert.sameValue(typeof daysInWeek, "function"); + +assert.throws(TypeError, () => daysInWeek.call(undefined), "undefined"); +assert.throws(TypeError, () => daysInWeek.call(null), "null"); +assert.throws(TypeError, () => daysInWeek.call(true), "true"); +assert.throws(TypeError, () => daysInWeek.call(""), "empty string"); +assert.throws(TypeError, () => daysInWeek.call(Symbol()), "symbol"); +assert.throws(TypeError, () => daysInWeek.call(1), "1"); +assert.throws(TypeError, () => daysInWeek.call({}), "plain object"); +assert.throws(TypeError, () => daysInWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => daysInWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..a411c0305a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinweek +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const daysInWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInWeek"); +Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("daysInWeek should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInWeek; + +Object.defineProperty(Temporal.Calendar.prototype, "daysInWeek", daysInWeekOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..aae90307fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinweek +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInWeek; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js new file mode 100644 index 0000000000..c47b290cd3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: Custom calendar tests for daysInWeek(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + daysInWeek(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInWeek arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.daysInWeek; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js new file mode 100644 index 0000000000..8681bb5439 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: The "daysInWeek" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInWeek"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..a590eea359 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..813b712cc8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.daysInWeek, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..1a4eaa2717 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..a16ca75372 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.daysInWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js new file mode 100644 index 0000000000..41ae7b68f9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInWeek/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinweek +description: Validate result returned from calendar daysInWeek() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + daysInWeek() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.daysInWeek, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js new file mode 100644 index 0000000000..1d18d79ffd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/basic.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: Checking days in year for a "normal" case (non-undefined, non-boundary case, etc.) +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).daysInYear, + 366, "leap year"); +assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).daysInYear, + 365, "non-leap year"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js new file mode 100644 index 0000000000..7435cd0e62 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const daysInYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInYear").get; + +assert.sameValue(typeof daysInYear, "function"); + +assert.throws(TypeError, () => daysInYear.call(undefined), "undefined"); +assert.throws(TypeError, () => daysInYear.call(null), "null"); +assert.throws(TypeError, () => daysInYear.call(true), "true"); +assert.throws(TypeError, () => daysInYear.call(""), "empty string"); +assert.throws(TypeError, () => daysInYear.call(Symbol()), "symbol"); +assert.throws(TypeError, () => daysInYear.call(1), "1"); +assert.throws(TypeError, () => daysInYear.call({}), "plain object"); +assert.throws(TypeError, () => daysInYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => daysInYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..ca8317011d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinyear +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const daysInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "daysInYear"); +Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("daysInYear should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInYear; + +Object.defineProperty(Temporal.Calendar.prototype, "daysInYear", daysInYearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..0584ccaf41 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.daysinyear +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.daysInYear; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js new file mode 100644 index 0000000000..21f10a4aec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: Custom calendar tests for daysInYear(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + daysInYear(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "daysInYear arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.daysInYear; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js new file mode 100644 index 0000000000..01bfaeabfc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: The "daysInYear" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "daysInYear"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..721f9d20bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..070b74b163 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.daysInYear, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..b3560091be --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.daysInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7cb221a3e8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.daysInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js new file mode 100644 index 0000000000..f706d136ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/daysInYear/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.daysinyear +description: Validate result returned from calendar daysInYear() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + daysInYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.daysInYear, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js new file mode 100644 index 0000000000..7614771a58 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmicroseconds +description: Basic tests for epochMicroseconds. +features: [BigInt, Temporal] +---*/ + +const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC"); +assert.sameValue(afterEpoch.epochMicroseconds, 217175010_123_456n, "epochMicroseconds post epoch"); +assert.sameValue(typeof afterEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint"); + +const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC"); +assert.sameValue(beforeEpoch.epochMicroseconds, -217175010_876_544n, "epochMicroseconds pre epoch"); +assert.sameValue(typeof beforeEpoch.epochMicroseconds, "bigint", "epochMicroseconds value is a bigint"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js new file mode 100644 index 0000000000..b3c21df8ff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmicroseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const epochMicroseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMicroseconds").get; + +assert.sameValue(typeof epochMicroseconds, "function"); + +assert.throws(TypeError, () => epochMicroseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => epochMicroseconds.call(null), "null"); +assert.throws(TypeError, () => epochMicroseconds.call(true), "true"); +assert.throws(TypeError, () => epochMicroseconds.call(""), "empty string"); +assert.throws(TypeError, () => epochMicroseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => epochMicroseconds.call(1), "1"); +assert.throws(TypeError, () => epochMicroseconds.call({}), "plain object"); +assert.throws(TypeError, () => epochMicroseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => epochMicroseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js new file mode 100644 index 0000000000..148ab30b80 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmicroseconds +description: The "epochMicroseconds" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMicroseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js new file mode 100644 index 0000000000..6a463a167b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmilliseconds +description: Basic tests for epochMilliseconds. +features: [BigInt, Temporal] +---*/ + +const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC"); +assert.sameValue(afterEpoch.epochMilliseconds, 217175010_123, "epochMilliseconds post epoch"); +assert.sameValue(typeof afterEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number"); + +const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC"); +assert.sameValue(beforeEpoch.epochMilliseconds, -217175010_877, "epochMilliseconds pre epoch"); +assert.sameValue(typeof beforeEpoch.epochMilliseconds, "number", "epochMilliseconds value is a number"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js new file mode 100644 index 0000000000..225ceb6860 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmilliseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const epochMilliseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMilliseconds").get; + +assert.sameValue(typeof epochMilliseconds, "function"); + +assert.throws(TypeError, () => epochMilliseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => epochMilliseconds.call(null), "null"); +assert.throws(TypeError, () => epochMilliseconds.call(true), "true"); +assert.throws(TypeError, () => epochMilliseconds.call(""), "empty string"); +assert.throws(TypeError, () => epochMilliseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => epochMilliseconds.call(1), "1"); +assert.throws(TypeError, () => epochMilliseconds.call({}), "plain object"); +assert.throws(TypeError, () => epochMilliseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => epochMilliseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js new file mode 100644 index 0000000000..35a00eaa9a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochmilliseconds +description: The "epochMilliseconds" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochMilliseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js new file mode 100644 index 0000000000..f0237adf44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/basic.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochnanoseconds +description: Basic tests for epochNanoseconds. +features: [BigInt, Temporal] +---*/ + +const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC"); +assert.sameValue(afterEpoch.epochNanoseconds, 217175010_123_456_789n, "epochNanoseconds post epoch"); +assert.sameValue(typeof afterEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint"); + +const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC"); +assert.sameValue(beforeEpoch.epochNanoseconds, -217175010_876_543_211n, "epochNanoseconds pre epoch"); +assert.sameValue(typeof beforeEpoch.epochNanoseconds, "bigint", "epochNanoseconds value is a bigint"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js new file mode 100644 index 0000000000..6cff52795b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochnanoseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const epochNanoseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochNanoseconds").get; + +assert.sameValue(typeof epochNanoseconds, "function"); + +assert.throws(TypeError, () => epochNanoseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => epochNanoseconds.call(null), "null"); +assert.throws(TypeError, () => epochNanoseconds.call(true), "true"); +assert.throws(TypeError, () => epochNanoseconds.call(""), "empty string"); +assert.throws(TypeError, () => epochNanoseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => epochNanoseconds.call(1), "1"); +assert.throws(TypeError, () => epochNanoseconds.call({}), "plain object"); +assert.throws(TypeError, () => epochNanoseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => epochNanoseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js new file mode 100644 index 0000000000..e1339f4e60 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochnanoseconds +description: The "epochNanoseconds" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochNanoseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochNanoseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js new file mode 100644 index 0000000000..4e1015d977 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochseconds +description: Basic tests for epochSeconds. +features: [BigInt, Temporal] +---*/ + +const afterEpoch = new Temporal.ZonedDateTime(217175010_123_456_789n, "UTC"); +assert.sameValue(afterEpoch.epochSeconds, 217175010, "epochSeconds post epoch"); +assert.sameValue(typeof afterEpoch.epochSeconds, "number", "epochSeconds value is a number"); + +const beforeEpoch = new Temporal.ZonedDateTime(-217175010_876_543_211n, "UTC"); +assert.sameValue(beforeEpoch.epochSeconds, -217175011, "epochSeconds pre epoch"); +assert.sameValue(typeof beforeEpoch.epochSeconds, "number", "epochSeconds value is a number"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js new file mode 100644 index 0000000000..85f7a3e463 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const epochSeconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochSeconds").get; + +assert.sameValue(typeof epochSeconds, "function"); + +assert.throws(TypeError, () => epochSeconds.call(undefined), "undefined"); +assert.throws(TypeError, () => epochSeconds.call(null), "null"); +assert.throws(TypeError, () => epochSeconds.call(true), "true"); +assert.throws(TypeError, () => epochSeconds.call(""), "empty string"); +assert.throws(TypeError, () => epochSeconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => epochSeconds.call(1), "1"); +assert.throws(TypeError, () => epochSeconds.call({}), "plain object"); +assert.throws(TypeError, () => epochSeconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => epochSeconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js new file mode 100644 index 0000000000..cc70ea2d58 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.epochseconds +description: The "epochSeconds" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "epochSeconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..5434ebff4d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" }; +instance.equals(arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..8cc0eeae30 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Correct time zone calls are made when converting a ZonedDateTime-like property + bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const calendar = TemporalHelpers.calendarObserver(actual, "calendar"); + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.equals(arg); + +const expected = [ + // GetTemporalCalendarSlotValueWithISODefault + "has calendar.dateAdd", + "has calendar.dateFromFields", + "has calendar.dateUntil", + "has calendar.day", + "has calendar.dayOfWeek", + "has calendar.dayOfYear", + "has calendar.daysInMonth", + "has calendar.daysInWeek", + "has calendar.daysInYear", + "has calendar.fields", + "has calendar.id", + "has calendar.inLeapYear", + "has calendar.mergeFields", + "has calendar.month", + "has calendar.monthCode", + "has calendar.monthDayFromFields", + "has calendar.monthsInYear", + "has calendar.weekOfYear", + "has calendar.year", + "has calendar.yearMonthFromFields", + "has calendar.yearOfWeek", + // lookup in ToTemporalZonedDateTime + "get calendar.dateFromFields", + "get calendar.fields", + // CalendarFields + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "call calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", +]; + +const expectedSpringForward = expected.concat([ + // DisambiguatePossibleInstants + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.equals(arg); + +assert.compareArray( + actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime + expected, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..7700d1ab41 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: The calendar name is case-insensitive +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "IsO8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.equals(arg); +assert.sameValue(result, true, "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..453535b8f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Leap second is a valid ISO string for a calendar in a property bag +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.equals(arg); +assert.sameValue( + result, + true, + "leap second is a valid ISO string for calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..a5fdb1d028 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-number.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; + assert.throws( + TypeError, + () => instance.equals(arg), + "Numbers cannot be used as a calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..b12b721272 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-string.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: A calendar ID is valid input for Calendar +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "iso8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.equals(arg); +assert.sameValue(result, true, `Calendar created from string "${calendar}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..dc6dcf3094 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === 'string' ? RangeError : TypeError, + () => instance.equals(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..c0230f2f3e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.equals(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..ddda8b9c96 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar }; + +const instance = new Temporal.ZonedDateTime(0n, timeZone); +instance.equals(arg); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..c3dda0033e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-invalid-offset-string.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +badOffsets.forEach((offset) => { + const arg = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => instance.equals(arg), + `"${offset} is not a valid offset string` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js new file mode 100644 index 0000000000..a986ff58d1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-offset-not-agreeing-with-timezone.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Property bag with offset property is rejected if offset does not agree with time zone +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone }; +assert.throws(RangeError, () => instance.equals(properties), "offset property not matching time zone is rejected"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js new file mode 100644 index 0000000000..d8001c7719 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-case-insensitive.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: The time zone identifier is case-insensitive +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const bag = { year: 1970, monthCode: "M01", day: 1, timeZone: "utC" }; +const result1 = instance.equals(bag); +assert.sameValue(result1, true, "Time zone is case-insensitive with property bag argument"); + +const str = "1970-01-01[UtC]"; +const result2 = instance.equals(str); +assert.sameValue(result2, true, "Time zone is case-insensitive with string argument"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ab429616bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..84360d9b7b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + timeZone.getPossibleInstantsFor = function () { + return []; + }; + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..a8e6622c8b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..0839506f4c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(TypeError, () => datetime.equals({ year: 2000, month: 5, day: 2, hour: 12, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js new file mode 100644 index 0000000000..82dc1d0519 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-id-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const datetime = new Temporal.ZonedDateTime(0n, "UTC"); + assert.throws(TypeError, () => datetime.equals({ year: 1970, month: 1, day: 1, timeZone })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js new file mode 100644 index 0000000000..eef37bd42f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-normalize-offset-strings.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Offset time zone identifiers are compared using their normal form, ignoring syntax differences in offset strings +features: [Temporal] +---*/ + +const tests = [ + { idToTest: "+0000", description: "colon-less" }, + { idToTest: "+00", description: "hours-only" } +]; + +for (const test of tests) { + const {idToTest, description} = test; + const instance = new Temporal.ZonedDateTime(0n, "+00:00"); + + const bag1 = { year: 1970, monthCode: "M01", day: 1, timeZone: idToTest }; + assert.sameValue(instance.equals(bag1), true, `Offset time zones are equal despite ${description} syntax in property bag argument`); + + const str = "1970-01-01[+00:00]"; + assert.sameValue(instance.equals(str), true, `Offset time zones are equal despite ${description} syntax in ISO string argument`); + + const getPossibleInstantsFor = (pdt) => [Temporal.Instant.from(`${pdt.toString()}Z`)] + const plainObj = { id: idToTest, getPossibleInstantsFor, getOffsetNanosecondsFor: () => 0 }; + const bag2 = { year: 1970, monthCode: "M01", day: 1, timeZone: plainObj }; + assert.sameValue(instance.equals(bag2), true, `Offset time zones are equal despite ${description} syntax in plain object time zone ID`); + + class CustomTimeZone extends Temporal.TimeZone { + constructor() { + super(idToTest); + } + get id() { return idToTest; } + } + const customTimeZoneInstance = new CustomTimeZone(); + assert.sameValue(customTimeZoneInstance.id, idToTest); + const bag3 = { year: 1970, monthCode: "M01", day: 1, timeZone: customTimeZoneInstance }; + assert.sameValue(instance.equals(bag3), true, `Offset time zones are equal despite ${description} syntax in custom object time zone ID`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..bdf04fbafe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-datetime.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let expectedTimeZone = "UTC"; +const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone); + +let timeZone = "2021-02-19T17:30"; +assert.throws(RangeError, () => instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw. They should produce +// expectedTimeZone, so additionally the operation should return true, because +// the property bag will produce an instance that's equal to the receiver. + +timeZone = "2021-02-19T17:30Z"; +assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone }), "date-time + Z is UTC time zone"); + +expectedTimeZone = "-08:00"; +const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-02-19T17:30-08:00"; +assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset is the offset time zone"); + +const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-02-19T17:30[-08:00]"; +assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + IANA annotation is the IANA time zone"); + +timeZone = "2021-02-19T17:30Z[-08:00]"; +assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + Z + IANA annotation is the IANA time zone"); + +timeZone = "2021-02-19T17:30-08:00[-08:00]"; +assert(instance3.equals({ year: 1969, month: 12, day: 31, hour: 16, timeZone }), "date-time + offset + IANA annotation is the IANA time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..30a964479e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-leap-second.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1588377600_000_000_000n, new Temporal.TimeZone("UTC")); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +assert(instance.equals({ year: 2020, month: 5, day: 2, timeZone }), "leap second is a valid ISO string for TimeZone"); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..8987fd3b5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-multiple-offsets.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const expectedTimeZone = "+01:46"; +const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone); +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +// This operation should produce expectedTimeZone, so the following should +// be equal due to the time zones being different on the receiver and +// the argument. + +const properties = { year: 1970, month: 1, day: 1, hour: 1, minute: 46 }; +assert(instance.equals({ ...properties, timeZone }), "time zone string should produce expected time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..48cafc8c09 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string-year-zero.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js new file mode 100644 index 0000000000..45c5c3c1bd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-string.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +assert(instance1.equals({ year: 1970, month: 1, day: 1, timeZone: "UTC" }), "Time zone created from string 'UTC'"); + +const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30")); +assert(instance2.equals({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }), "Time zone created from string '-01:30'"); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..49a778ec3d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => instance.equals({ year: 2020, month: 5, day: 2, 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, () => instance.equals({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..9ef4d9a5b3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-calendar-annotation.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"], + ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.equals(arg); + + assert.sameValue( + result, + true, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..fb0d1d457c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-critical-unknown-annotation.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.equals(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..3a4a83d679 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-date-with-utc-offset.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: UTC offset not valid with format that does not include a time +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const validStrings = [ + "1970-01-01T00Z[UTC]", + "1970-01-01T00Z[!UTC]", + "1970-01-01T00+00:00[UTC]", + "1970-01-01T00+00:00[!UTC]", +]; + +for (const arg of validStrings) { + const result = instance.equals(arg); + + assert.sameValue( + result, + true, + `"${arg}" is a valid UTC offset with time for ZonedDateTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.equals(arg), + `"${arg}" UTC offset without time is not valid for ZonedDateTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..d46ea8d2b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-calendar.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.equals(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..494248df4c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-multiple-time-zone.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.equals(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js new file mode 100644 index 0000000000..1d02e64ae6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-separators.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Time separator in string argument can vary +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00+00:00[UTC]", "uppercase T"], + ["1970-01-01t00:00+00:00[UTC]", "lowercase T"], + ["1970-01-01 00:00+00:00[UTC]", "space between date and time"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.equals(arg); + + assert.sameValue( + result, + true, + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..94d43d9c67 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-time-zone-annotation.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC]", "UTC", "named, with no offset"], + ["1970-01-01T00:00[!UTC]", "UTC", "named, with ! and no offset"], + ["1970-01-01T00:00[+00:00]", "+00:00", "numeric, with no offset"], + ["1970-01-01T00:00[!+00:00]", "+00:00", "numeric, with ! and no offset"], + ["1970-01-01T00:00Z[UTC]", "UTC", "named, with Z"], + ["1970-01-01T00:00Z[!UTC]", "UTC", "named, with Z and !"], + ["1970-01-01T00:00Z[+00:00]", "+00:00", "numeric, with Z"], + ["1970-01-01T00:00Z[!+00:00]", "+00:00", "numeric, with Z and !"], + ["1970-01-01T00:00+00:00[UTC]", "UTC", "named, with offset"], + ["1970-01-01T00:00+00:00[!UTC]", "UTC", "named, with offset and !"], + ["1970-01-01T00:00+00:00[+00:00]", "+00:00", "numeric, with offset"], + ["1970-01-01T00:00+00:00[!+00:00]", "+00:00", "numeric, with offset and !"], +]; + +tests.forEach(([arg, expectedZone, description]) => { + const timeZone = new Temporal.TimeZone(expectedZone); + const instance = new Temporal.ZonedDateTime(0n, timeZone); + const result = instance.equals(arg); + + assert.sameValue( + result, + true, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..832d4472b3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-unknown-annotation.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Various forms of unknown annotation +features: [Temporal] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"], + ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"], + ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.equals(arg); + + assert.sameValue( + result, + true, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js new file mode 100644 index 0000000000..87d65dda7b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-wrong-type.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for ZonedDateTime +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [undefined, "undefined"], + [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"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.equals(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.equals(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/branding.js new file mode 100644 index 0000000000..3a3999338b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const equals = Temporal.ZonedDateTime.prototype.equals; + +assert.sameValue(typeof equals, "function"); + +const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("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.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => equals.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..0412da051c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.equals(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC")); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..4c98559604 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.equals(new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC")); + +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js new file mode 100644 index 0000000000..866556891b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Tests that Temporal.ZonedDateTime.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.ZonedDateTime.prototype.equals), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.equals), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.equals), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.equals.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..4596ac881b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar }; +instance.equals(arg); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js new file mode 100644 index 0000000000..81dbe43ab2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-fields-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.equals step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar1 = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1); +const calendar2 = TemporalHelpers.calendarFieldsIterable(); +datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 }); + +assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called"); +assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js new file mode 100644 index 0000000000..34a6f9be8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.prototype.equals step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..6dc6ba7ff5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/constructor-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.equals(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js new file mode 100644 index 0000000000..f21747aacf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/duplicate-calendar-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const timeZone = 'Europe/Paris' + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + const instance = new Temporal.ZonedDateTime(0n, timeZone); + + assert.throws(RangeError, () => instance.equals(arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..82539840f2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.prototype.equals +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.equals({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.equals({ ...base, [prop]: obj })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js new file mode 100644 index 0000000000..d66e7731ae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/leap-second.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Leap second is a valid ISO string for ZonedDateTime +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone); + +let arg = "2016-12-31T23:59:60+00:00[UTC]"; +const result = instance.equals(arg); +assert.sameValue( + result, + true, + "leap second is a valid ISO string for ZonedDateTime" +); + +arg = "2000-05-02T12:34:56+23:59[+23:59:60]"; +assert.throws( + RangeError, + () => instance.equals(arg), + "leap second in time zone name not valid" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js new file mode 100644 index 0000000000..8981fa9ccd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.equals, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js new file mode 100644 index 0000000000..7deb2d03c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Temporal.ZonedDateTime.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.ZonedDateTime.prototype.equals, "name", { + value: "equals", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js new file mode 100644 index 0000000000..f8e17fcd3a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: > + Temporal.ZonedDateTime.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.ZonedDateTime.prototype.equals(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.equals), false, + "isConstructor(Temporal.ZonedDateTime.prototype.equals)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js new file mode 100644 index 0000000000..ff48724957 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js @@ -0,0 +1,121 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Properties on objects passed to equals() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get other.calendar", + "has other.calendar.dateAdd", + "has other.calendar.dateFromFields", + "has other.calendar.dateUntil", + "has other.calendar.day", + "has other.calendar.dayOfWeek", + "has other.calendar.dayOfYear", + "has other.calendar.daysInMonth", + "has other.calendar.daysInWeek", + "has other.calendar.daysInYear", + "has other.calendar.fields", + "has other.calendar.id", + "has other.calendar.inLeapYear", + "has other.calendar.mergeFields", + "has other.calendar.month", + "has other.calendar.monthCode", + "has other.calendar.monthDayFromFields", + "has other.calendar.monthsInYear", + "has other.calendar.weekOfYear", + "has other.calendar.year", + "has other.calendar.yearMonthFromFields", + "has other.calendar.yearOfWeek", + "get other.calendar.dateFromFields", + "get other.calendar.fields", + "call other.calendar.fields", + // PrepareTemporalFields + "get other.day", + "get other.day.valueOf", + "call other.day.valueOf", + "get other.hour", + "get other.hour.valueOf", + "call other.hour.valueOf", + "get other.microsecond", + "get other.microsecond.valueOf", + "call other.microsecond.valueOf", + "get other.millisecond", + "get other.millisecond.valueOf", + "call other.millisecond.valueOf", + "get other.minute", + "get other.minute.valueOf", + "call other.minute.valueOf", + "get other.month", + "get other.month.valueOf", + "call other.month.valueOf", + "get other.monthCode", + "get other.monthCode.toString", + "call other.monthCode.toString", + "get other.nanosecond", + "get other.nanosecond.valueOf", + "call other.nanosecond.valueOf", + "get other.offset", + "get other.offset.toString", + "call other.offset.toString", + "get other.second", + "get other.second.valueOf", + "call other.second.valueOf", + "get other.timeZone", + "get other.year", + "get other.year.valueOf", + "call other.year.valueOf", + "has other.timeZone.getOffsetNanosecondsFor", + "has other.timeZone.getPossibleInstantsFor", + "has other.timeZone.id", + // InterpretTemporalDateTimeFields + "call other.calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get other.timeZone.getOffsetNanosecondsFor", + "get other.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getOffsetNanosecondsFor", + // TimeZoneEquals + "get this.timeZone.id", + "get other.timeZone.id", + // CalendarEquals + "get this.calendar.id", + "get other.calendar.id", +]; +const actual = []; + +const other = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"), +}, "other"); + +const instance = new Temporal.ZonedDateTime( + 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */ + TemporalHelpers.timeZoneObserver(actual, "this.timeZone"), + TemporalHelpers.calendarObserver(actual, "this.calendar"), +); +// clear any observable operations that happen due to time zone or calendar +// calls on the constructor +actual.splice(0); + +instance.equals(other); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js new file mode 100644 index 0000000000..28b0b5c0ac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: The "equals" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.equals, + "function", + "`typeof ZonedDateTime.prototype.equals` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "equals", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js new file mode 100644 index 0000000000..6d6adb89c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/proto-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.equals(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..5827450d71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/read-time-fields-before-datefromfields.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.prototype.equals step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.j: + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); +const result = datetime.equals({ year: 2001, month: 9, day: 9, hour: 1, minute: 46, second: 40, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC", calendar }); + +assert(result, "time fields are not modified"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..48f46ac772 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.equals step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Not called on the instance's time zone + +const expected1 = []; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.equals({ year: 2005, month: 6, day: 2, timeZone: "UTC" }); +}, expected1); + +// Called on the argument's time zone + +const expected2 = [ + "2005-06-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + datetime.equals({ year: 2005, month: 6, day: 2, timeZone }); +}, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js new file mode 100644 index 0000000000..cae89644b1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/year-zero.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-0000000-01-01T00:02Z[UTC]", + "-0000000-01-01T00:02+00:00[UTC]", + "-0000000-01-01T00:02:00.000000000+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.equals(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js new file mode 100644 index 0000000000..f3a5eb6f15 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string-multiple-offsets.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:35"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]"; + +const result = instance.equals(str); +assert.sameValue(result, false, "ISO offset, sub-minute offset trailing-zeroes"); + +str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]"; +assert.throws( + RangeError, + () => instance.equals(str), + "Trailing zeroes not allowed for sub-minute time zone identifiers" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js new file mode 100644 index 0000000000..127adda14b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/equals/zoneddatetime-string.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let str = "1970-01-01T00:00"; +assert.throws(RangeError, () => instance.equals(str), "bare date-time string is not a ZonedDateTime"); +str = "1970-01-01T00:00Z"; +assert.throws(RangeError, () => instance.equals(str), "date-time + Z is not a ZonedDateTime"); +str = "1970-01-01T00:00+01:00"; +assert.throws(RangeError, () => instance.equals(str), "date-time + offset is not a ZonedDateTime"); + +str = "1970-01-01T00:00[+01:00]"; +const result1 = instance.equals(str); +assert.sameValue(result1, false, "date-time + IANA annotation preserves wall time in the time zone"); + +str = "1970-01-01T00:00Z[+01:00]"; +const result2 = instance.equals(str); +assert.sameValue(result2, true, "date-time + Z + IANA annotation preserves exact time in the time zone"); + +str = "1970-01-01T00:00+01:00[+01:00]"; +const result3 = instance.equals(str); +assert.sameValue(result3, false, "date-time + offset + IANA annotation ensures both exact and wall time match"); + +str = "1970-01-01T00:00-04:15[+01:00]"; +assert.throws(RangeError, () => instance.equals(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js new file mode 100644 index 0000000000..4814815a2d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const getCalendar = Temporal.ZonedDateTime.prototype.getCalendar; + +assert.sameValue(typeof getCalendar, "function"); + +assert.throws(TypeError, () => getCalendar.call(undefined), "undefined"); +assert.throws(TypeError, () => getCalendar.call(null), "null"); +assert.throws(TypeError, () => getCalendar.call(true), "true"); +assert.throws(TypeError, () => getCalendar.call(""), "empty string"); +assert.throws(TypeError, () => getCalendar.call(Symbol()), "symbol"); +assert.throws(TypeError, () => getCalendar.call(1), "1"); +assert.throws(TypeError, () => getCalendar.call({}), "plain object"); +assert.throws(TypeError, () => getCalendar.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => getCalendar.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js new file mode 100644 index 0000000000..0e6750f2a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: > + Tests that Temporal.ZonedDateTime.prototype.getCalendar + 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.ZonedDateTime.prototype.getCalendar), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getCalendar), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getCalendar), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.getCalendar.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js new file mode 100644 index 0000000000..9ae1bf5fd4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: Temporal.ZonedDateTime.prototype.getCalendar.length is 0 +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.ZonedDateTime.prototype.getCalendar, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js new file mode 100644 index 0000000000..30dbcb35c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: Temporal.ZonedDateTime.prototype.getCalendar.name is "getCalendar". +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.ZonedDateTime.prototype.getCalendar, "name", { + value: "getCalendar", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js new file mode 100644 index 0000000000..8bdcc3c8e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: > + Temporal.ZonedDateTime.prototype.getCalendar 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.ZonedDateTime.prototype.getCalendar(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getCalendar), false, + "isConstructor(Temporal.ZonedDateTime.prototype.getCalendar)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js new file mode 100644 index 0000000000..ad707eeb7b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getcalendar +description: The "getCalendar" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.getCalendar, + "function", + "`typeof ZonedDateTime.prototype.getCalendar` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "getCalendar", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getCalendar/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js new file mode 100644 index 0000000000..5a33aae7ce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/balance-negative-time-units.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.getisofields step 7: + 7. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// ZonedDateTime +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +const fields = datetime.getISOFields(); + +assert.sameValue(fields.isoMicrosecond, 0); +assert.sameValue(fields.isoNanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js new file mode 100644 index 0000000000..5fd93d96d4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const getISOFields = Temporal.ZonedDateTime.prototype.getISOFields; + +assert.sameValue(typeof getISOFields, "function"); + +assert.throws(TypeError, () => getISOFields.call(undefined), "undefined"); +assert.throws(TypeError, () => getISOFields.call(null), "null"); +assert.throws(TypeError, () => getISOFields.call(true), "true"); +assert.throws(TypeError, () => getISOFields.call(""), "empty string"); +assert.throws(TypeError, () => getISOFields.call(Symbol()), "symbol"); +assert.throws(TypeError, () => getISOFields.call(1), "1"); +assert.throws(TypeError, () => getISOFields.call({}), "plain object"); +assert.throws(TypeError, () => getISOFields.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => getISOFields.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..51ed036417 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.getISOFields(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js new file mode 100644 index 0000000000..53b24e2db4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: > + Tests that Temporal.ZonedDateTime.prototype.getISOFields + 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.ZonedDateTime.prototype.getISOFields), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getISOFields), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getISOFields), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.getISOFields.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js new file mode 100644 index 0000000000..c7e5fb7f8c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/custom.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: getISOFields does not call into user code. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarThrowEverything(); +const instance = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC", calendar); +const result = instance.getISOFields(); + +assert.sameValue(result.isoYear, 2001, "isoYear result"); +assert.sameValue(result.isoMonth, 9, "isoMonth result"); +assert.sameValue(result.isoDay, 10, "isoDay result"); +assert.sameValue(result.isoHour, 1, "isoHour result"); +assert.sameValue(result.isoMinute, 46, "isoMinute result"); +assert.sameValue(result.isoSecond, 40, "isoSecond result"); +assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result"); +assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result"); +assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result"); +assert.sameValue(result.offset, "+00:00", "offset result"); +assert.sameValue(result.calendar, calendar, "calendar result"); +assert.sameValue(result.timeZone, "UTC", "timeZone result"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js new file mode 100644 index 0000000000..fd916b95c7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-names.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Correct field names on the object returned from getISOFields +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC"); + +const result = datetime.getISOFields(); +assert.sameValue(result.isoYear, 2001, "isoYear result"); +assert.sameValue(result.isoMonth, 9, "isoMonth result"); +assert.sameValue(result.isoDay, 10, "isoDay result"); +assert.sameValue(result.isoHour, 1, "isoHour result"); +assert.sameValue(result.isoMinute, 46, "isoMinute result"); +assert.sameValue(result.isoSecond, 40, "isoSecond result"); +assert.sameValue(result.isoMillisecond, 987, "isoMillisecond result"); +assert.sameValue(result.isoMicrosecond, 654, "isoMicrosecond result"); +assert.sameValue(result.isoNanosecond, 321, "isoNanosecond result"); +assert.sameValue(result.offset, "+00:00", "offset result"); +assert.sameValue(result.calendar, "iso8601", "calendar result"); +assert.sameValue(result.timeZone, "UTC", "timeZone result"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js new file mode 100644 index 0000000000..7a1b879b24 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-prop-desc.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Properties on the returned object have the correct descriptor +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +const expected = [ + "calendar", + "isoDay", + "isoHour", + "isoMicrosecond", + "isoMillisecond", + "isoMinute", + "isoMonth", + "isoNanosecond", + "isoSecond", + "isoYear", + "offset", + "timeZone", +]; + +const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC"); +const result = datetime.getISOFields(); + +for (const property of expected) { + verifyProperty(result, property, { + writable: true, + enumerable: true, + configurable: true, + }); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js new file mode 100644 index 0000000000..3f312f352a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/field-traversal-order.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Properties added in correct order to object returned from getISOFields +includes: [compareArray.js] +features: [Temporal] +---*/ + +const expected = [ + "calendar", + "isoDay", + "isoHour", + "isoMicrosecond", + "isoMillisecond", + "isoMinute", + "isoMonth", + "isoNanosecond", + "isoSecond", + "isoYear", + "offset", + "timeZone", +]; + +const datetime = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC"); +const result = datetime.getISOFields(); + +assert.compareArray(Object.keys(result), expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js new file mode 100644 index 0000000000..010653ec3d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Temporal.ZonedDateTime.prototype.getISOFields.length is 0 +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.ZonedDateTime.prototype.getISOFields, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js new file mode 100644 index 0000000000..2b26a1f5b6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Temporal.ZonedDateTime.prototype.getISOFields.name is "getISOFields". +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.ZonedDateTime.prototype.getISOFields, "name", { + value: "getISOFields", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js new file mode 100644 index 0000000000..cd6f5a134d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/negative-epochnanoseconds.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [deepEqual.js] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const calendar = new Temporal.Calendar("iso8601"); +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, timeZone, calendar); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.getISOFields(); +assert.deepEqual(result, { + calendar, + isoDay: 24, + isoHour: 16, + isoMicrosecond: 0, + isoMillisecond: 0, + isoMinute: 50, + isoMonth: 7, + isoNanosecond: 1, + isoSecond: 35, + isoYear: 1969, + offset: "+00:00", + timeZone, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js new file mode 100644 index 0000000000..8f70d1c859 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: > + Temporal.ZonedDateTime.prototype.getISOFields 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.ZonedDateTime.prototype.getISOFields(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getISOFields), false, + "isConstructor(Temporal.ZonedDateTime.prototype.getISOFields)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js new file mode 100644 index 0000000000..f4e0b8687d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/offset.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: The offset property of returned object +features: [BigInt, Temporal] +---*/ + +function test(timeZoneIdentifier, expectedOffsetString, description) { + const timeZone = new Temporal.TimeZone(timeZoneIdentifier); + const datetime = new Temporal.ZonedDateTime(0n, timeZone); + const fields = datetime.getISOFields(); + assert.sameValue(fields.offset, expectedOffsetString, description); +} + +test("UTC", "+00:00", "offset of UTC is +00:00"); +test("+01:00", "+01:00", "positive offset"); +test("-05:00", "-05:00", "negative offset"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js new file mode 100644 index 0000000000..69c0c3cd05 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/order-of-operations.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: > + Properties on the calendar or time zone of the receiver of getISOFields() + are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const instance = new Temporal.ZonedDateTime( + 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */ + TemporalHelpers.timeZoneObserver(actual, "this.timeZone"), + TemporalHelpers.calendarObserver(actual, "this.calendar"), +); +// clear any observable operations that happen due to time zone or calendar +// calls on the constructor +actual.splice(0); + +instance.getISOFields(); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js new file mode 100644 index 0000000000..afb99c7d9e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: The "getISOFields" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.getISOFields, + "function", + "`typeof ZonedDateTime.prototype.getISOFields` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "getISOFields", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js new file mode 100644 index 0000000000..591c6f23c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/prototype.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: Correct prototype on the object returned from getISOFields +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_086_400_987_654_321n, "UTC"); +const result = instance.getISOFields(); +assert.sameValue(Object.getPrototypeOf(result), Object.prototype, "prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js new file mode 100644 index 0000000000..346758ebd5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/shell.js @@ -0,0 +1,353 @@ +// GENERATED, DO NOT EDIT +// file: deepEqual.js +// Copyright 2019 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Compare two values structurally +defines: [assert.deepEqual] +---*/ + +assert.deepEqual = function(actual, expected, message) { + var format = assert.deepEqual.format; + assert( + assert.deepEqual._compare(actual, expected), + `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}` + ); +}; + +assert.deepEqual.format = function(value, seen) { + switch (typeof value) { + case 'string': + return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`; + case 'number': + case 'boolean': + case 'symbol': + case 'bigint': + return value.toString(); + case 'undefined': + return 'undefined'; + case 'function': + return `[Function${value.name ? `: ${value.name}` : ''}]`; + case 'object': + if (value === null) return 'null'; + if (value instanceof Date) return `Date "${value.toISOString()}"`; + if (value instanceof RegExp) return value.toString(); + if (!seen) { + seen = { + counter: 0, + map: new Map() + }; + } + + let usage = seen.map.get(value); + if (usage) { + usage.used = true; + return `[Ref: #${usage.id}]`; + } + + usage = { id: ++seen.counter, used: false }; + seen.map.set(value, usage); + + if (typeof Set !== "undefined" && value instanceof Set) { + return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (typeof Map !== "undefined" && value instanceof Map) { + return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`; + } + if (Array.isArray ? Array.isArray(value) : value instanceof Array) { + return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`; + } + let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object'; + if (tag === 'Object' && Object.getPrototypeOf(value) === null) { + tag = '[Object: null prototype]'; + } + return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`; + default: + return typeof value; + } +}; + +assert.deepEqual._compare = (function () { + var EQUAL = 1; + var NOT_EQUAL = -1; + var UNKNOWN = 0; + + function deepEqual(a, b) { + return compareEquality(a, b) === EQUAL; + } + + function compareEquality(a, b, cache) { + return compareIf(a, b, isOptional, compareOptionality) + || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality) + || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache) + || NOT_EQUAL; + } + + function compareIf(a, b, test, compare, cache) { + return !test(a) + ? !test(b) ? UNKNOWN : NOT_EQUAL + : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache); + } + + function tryCompareStrictEquality(a, b) { + return a === b ? EQUAL : UNKNOWN; + } + + function tryCompareTypeOfEquality(a, b) { + return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN; + } + + function tryCompareToStringTagEquality(a, b) { + var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined; + var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined; + return aTag !== bTag ? NOT_EQUAL : UNKNOWN; + } + + function isOptional(value) { + return value === undefined + || value === null; + } + + function compareOptionality(a, b) { + return tryCompareStrictEquality(a, b) + || NOT_EQUAL; + } + + function isPrimitiveEquatable(value) { + switch (typeof value) { + case 'string': + case 'number': + case 'bigint': + case 'boolean': + case 'symbol': + return true; + default: + return isBoxed(value); + } + } + + function comparePrimitiveEquality(a, b) { + if (isBoxed(a)) a = a.valueOf(); + if (isBoxed(b)) b = b.valueOf(); + return tryCompareStrictEquality(a, b) + || tryCompareTypeOfEquality(a, b) + || compareIf(a, b, isNaNEquatable, compareNaNEquality) + || NOT_EQUAL; + } + + function isNaNEquatable(value) { + return typeof value === 'number'; + } + + function compareNaNEquality(a, b) { + return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL; + } + + function isObjectEquatable(value) { + return typeof value === 'object'; + } + + function compareObjectEquality(a, b, cache) { + if (!cache) cache = new Map(); + return getCache(cache, a, b) + || setCache(cache, a, b, EQUAL) // consider equal for now + || cacheComparison(a, b, tryCompareStrictEquality, cache) + || cacheComparison(a, b, tryCompareToStringTagEquality, cache) + || compareIf(a, b, isValueOfEquatable, compareValueOfEquality) + || compareIf(a, b, isToStringEquatable, compareToStringEquality) + || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache) + || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache) + || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || cacheComparison(a, b, fail, cache); + } + + function isBoxed(value) { + return value instanceof String + || value instanceof Number + || value instanceof Boolean + || typeof Symbol === 'function' && value instanceof Symbol + || typeof BigInt === 'function' && value instanceof BigInt; + } + + function isValueOfEquatable(value) { + return value instanceof Date; + } + + function compareValueOfEquality(a, b) { + return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isToStringEquatable(value) { + return value instanceof RegExp; + } + + function compareToStringEquality(a, b) { + return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality) + || NOT_EQUAL; + } + + function isArrayLikeEquatable(value) { + return (Array.isArray ? Array.isArray(value) : value instanceof Array) + || (typeof Uint8Array === 'function' && value instanceof Uint8Array) + || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray) + || (typeof Uint16Array === 'function' && value instanceof Uint16Array) + || (typeof Uint32Array === 'function' && value instanceof Uint32Array) + || (typeof Int8Array === 'function' && value instanceof Int8Array) + || (typeof Int16Array === 'function' && value instanceof Int16Array) + || (typeof Int32Array === 'function' && value instanceof Int32Array) + || (typeof Float32Array === 'function' && value instanceof Float32Array) + || (typeof Float64Array === 'function' && value instanceof Float64Array) + || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array) + || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array); + } + + function compareArrayLikeEquality(a, b, cache) { + if (a.length !== b.length) return NOT_EQUAL; + for (var i = 0; i < a.length; i++) { + if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + return EQUAL; + } + + function isStructurallyEquatable(value) { + return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference + || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference + || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference + || typeof Map === 'function' && value instanceof Map // comparable via @@iterator + || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator + } + + function compareStructuralEquality(a, b, cache) { + var aKeys = []; + for (var key in a) aKeys.push(key); + + var bKeys = []; + for (var key in b) bKeys.push(key); + + if (aKeys.length !== bKeys.length) { + return NOT_EQUAL; + } + + aKeys.sort(); + bKeys.sort(); + + for (var i = 0; i < aKeys.length; i++) { + var aKey = aKeys[i]; + var bKey = bKeys[i]; + if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) { + return NOT_EQUAL; + } + } + + return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) + || EQUAL; + } + + function isIterableEquatable(value) { + return typeof Symbol === 'function' + && typeof value[Symbol.iterator] === 'function'; + } + + function compareIteratorEquality(a, b, cache) { + if (typeof Map === 'function' && a instanceof Map && b instanceof Map || + typeof Set === 'function' && a instanceof Set && b instanceof Set) { + if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size + } + + var ar, br; + while (true) { + ar = a.next(); + br = b.next(); + if (ar.done) { + if (br.done) return EQUAL; + if (b.return) b.return(); + return NOT_EQUAL; + } + if (br.done) { + if (a.return) a.return(); + return NOT_EQUAL; + } + if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) { + if (a.return) a.return(); + if (b.return) b.return(); + return NOT_EQUAL; + } + } + } + + function compareIterableEquality(a, b, cache) { + return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache); + } + + function cacheComparison(a, b, compare, cache) { + var result = compare(a, b, cache); + if (cache && (result === EQUAL || result === NOT_EQUAL)) { + setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result)); + } + return result; + } + + function fail() { + return NOT_EQUAL; + } + + function setCache(cache, left, right, result) { + var otherCache; + + otherCache = cache.get(left); + if (!otherCache) cache.set(left, otherCache = new Map()); + otherCache.set(right, result); + + otherCache = cache.get(right); + if (!otherCache) cache.set(right, otherCache = new Map()); + otherCache.set(left, result); + } + + function getCache(cache, left, right) { + var otherCache; + var result; + + otherCache = cache.get(left); + result = otherCache && otherCache.get(right); + if (result) return result; + + otherCache = cache.get(right); + result = otherCache && otherCache.get(left); + if (result) return result; + + return UNKNOWN; + } + + return deepEqual; +})(); + +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..8ca60795a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.getISOFields()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..5b1f63a8ad --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.getISOFields(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..84ae74e176 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.getISOFields()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..6187d63d78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getISOFields/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.getisofields +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.getISOFields()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js new file mode 100644 index 0000000000..74f62bf43d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const getTimeZone = Temporal.ZonedDateTime.prototype.getTimeZone; + +assert.sameValue(typeof getTimeZone, "function"); + +assert.throws(TypeError, () => getTimeZone.call(undefined), "undefined"); +assert.throws(TypeError, () => getTimeZone.call(null), "null"); +assert.throws(TypeError, () => getTimeZone.call(true), "true"); +assert.throws(TypeError, () => getTimeZone.call(""), "empty string"); +assert.throws(TypeError, () => getTimeZone.call(Symbol()), "symbol"); +assert.throws(TypeError, () => getTimeZone.call(1), "1"); +assert.throws(TypeError, () => getTimeZone.call({}), "plain object"); +assert.throws(TypeError, () => getTimeZone.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => getTimeZone.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js new file mode 100644 index 0000000000..25806fa1db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: > + Tests that Temporal.ZonedDateTime.prototype.getTimeZone + 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.ZonedDateTime.prototype.getTimeZone), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.getTimeZone), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.getTimeZone), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.getTimeZone.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js new file mode 100644 index 0000000000..31dfb7d877 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: Temporal.ZonedDateTime.prototype.getTimeZone.length is 0 +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.ZonedDateTime.prototype.getTimeZone, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js new file mode 100644 index 0000000000..dfc2650802 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: Temporal.ZonedDateTime.prototype.getTimeZone.name is "getTimeZone". +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.ZonedDateTime.prototype.getTimeZone, "name", { + value: "getTimeZone", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js new file mode 100644 index 0000000000..c92caf8a95 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: > + Temporal.ZonedDateTime.prototype.getTimeZone 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.ZonedDateTime.prototype.getTimeZone(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.getTimeZone), false, + "isConstructor(Temporal.ZonedDateTime.prototype.getTimeZone)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js new file mode 100644 index 0000000000..f05fb77817 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.gettimezone +description: The "getTimeZone" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.getTimeZone, + "function", + "`typeof ZonedDateTime.prototype.getTimeZone` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "getTimeZone", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/getTimeZone/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js new file mode 100644 index 0000000000..30915178c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/balance-negative-time-units.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.hour +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–12: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.hour step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(3600_000_000_001n, tz); + +assert.sameValue(datetime.hour, 0); +assert.sameValue(datetime.minute, 59); +assert.sameValue(datetime.second, 59); +assert.sameValue(datetime.millisecond, 999); +assert.sameValue(datetime.microsecond, 999); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js new file mode 100644 index 0000000000..2f8b13621b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const hour = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hour").get; + +assert.sameValue(typeof hour, "function"); + +assert.throws(TypeError, () => hour.call(undefined), "undefined"); +assert.throws(TypeError, () => hour.call(null), "null"); +assert.throws(TypeError, () => hour.call(true), "true"); +assert.throws(TypeError, () => hour.call(""), "empty string"); +assert.throws(TypeError, () => hour.call(Symbol()), "symbol"); +assert.throws(TypeError, () => hour.call(1), "1"); +assert.throws(TypeError, () => hour.call({}), "plain object"); +assert.throws(TypeError, () => hour.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => hour.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..278f2627f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.hour +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.hour; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js new file mode 100644 index 0000000000..481cf5a02a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: The "hour" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hour"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..e550d8ca6a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.hour); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..15a7390489 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.hour, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..e62fbfe41b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.hour); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..12136a348c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hour/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hour +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.hour); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js new file mode 100644 index 0000000000..44fb5d444e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const hoursInDay = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hoursInDay").get; + +assert.sameValue(typeof hoursInDay, "function"); + +assert.throws(TypeError, () => hoursInDay.call(undefined), "undefined"); +assert.throws(TypeError, () => hoursInDay.call(null), "null"); +assert.throws(TypeError, () => hoursInDay.call(true), "true"); +assert.throws(TypeError, () => hoursInDay.call(""), "empty string"); +assert.throws(TypeError, () => hoursInDay.call(Symbol()), "symbol"); +assert.throws(TypeError, () => hoursInDay.call(1), "1"); +assert.throws(TypeError, () => hoursInDay.call({}), "plain object"); +assert.throws(TypeError, () => hoursInDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => hoursInDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..7c78776910 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.hoursinday +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); + +const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); +instance.hoursInDay; + +assert.sameValue(timeZone.calls, 4, "getPossibleInstantsFor should have been called 4 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js new file mode 100644 index 0000000000..93779e3265 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js @@ -0,0 +1,108 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", +]; + +// Time zone with special requirements for testing DisambiguatePossibleInstants: +// midnight 1970-01-01 and 1970-01-02 are each in the middle of a fall-back +// transition of 1 h. Midnight 1970-01-03 and 1970-01-04 are each in the middle +// of a spring-forward transition of 1 h. +// The fall-back transitions occur 30 minutes after the day boundaries at local +// time: at epoch seconds 1800 and 91800. the spring-forward transitions occur +// 30 minutes before the day boundaries at local time: at epoch seconds 167400 +// and 257400. +// This is because calculating the hours in the instance's day requires calling +// getPossibleInstantsFor on both the preceding local midnight and the following +// local midnight. +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor(instant) { + const epochNs = instant.epochNanoseconds; + if (epochNs < 1800_000_000_000n) return 0; + if (epochNs < 91800_000_000_000n) return 3600_000_000_000; + if (epochNs < 167400_000_000_000n) return 7200_000_000_000; + if (epochNs < 257400_000_000_000n) return 3600_000_000_000; + return 0; + }, + getPossibleInstantsFor(dt) { + const cmp = Temporal.PlainDateTime.compare; + + const zero = new Temporal.TimeZone("+00:00").getInstantFor(dt); + const one = new Temporal.TimeZone("+01:00").getInstantFor(dt); + const two = new Temporal.TimeZone("+02:00").getInstantFor(dt); + + const fallBackLocalOne = new Temporal.PlainDateTime(1970, 1, 1, 0, 30); + const fallBackLocalTwo = new Temporal.PlainDateTime(1970, 1, 2, 0, 30); + const springForwardLocalOne = new Temporal.PlainDateTime(1970, 1, 2, 23, 30); + const springForwardLocalTwo = new Temporal.PlainDateTime(1970, 1, 3, 23, 30); + + if (cmp(dt, fallBackLocalOne) < 0) return [zero]; + if (cmp(dt, fallBackLocalOne.add({ hours: 1 })) < 0) return [zero, one]; + if (cmp(dt, fallBackLocalTwo) < 0) return [one]; + if (cmp(dt, fallBackLocalTwo.add({ hours: 1 })) < 0) return [one, two]; + if (cmp(dt, springForwardLocalOne) < 0) return [two]; + if (cmp(dt, springForwardLocalOne.add({ hours: 1 })) < 0) return []; + if (cmp(dt, springForwardLocalTwo) < 0) return [one]; + if (cmp(dt, springForwardLocalTwo.add({ hours: 1 })) < 0) return []; + return [zero]; + }, +}); + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone, calendar); +const fallBackInstance = new Temporal.ZonedDateTime(43200_000_000_000n /* 1970-01-01T12:00 */, timeZone, calendar); +const springForwardInstance = new Temporal.ZonedDateTime(216000_000_000_000n /* 1970-01-03T12:00 */, timeZone, calendar); +actual.splice(0); // clear calls that happened in constructors + +instance.hoursInDay; +assert.compareArray(actual, expected, "order of operations with both midnights at normal wall-clock times"); +actual.splice(0); // clear + +fallBackInstance.hoursInDay; +assert.compareArray(actual, expected, "order of operations with both midnights at repeated wall-clock times"); +actual.splice(0); // clear + +springForwardInstance.hoursInDay; +assert.compareArray(actual, [ + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // Note, no call to dateAdd as addition takes place in the ISO calendar + "call this.timeZone.getPossibleInstantsFor", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // Note, no call to dateAdd here either + "call this.timeZone.getPossibleInstantsFor", +], "order of operations with both midnights at skipped wall-clock times"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..7f34328d18 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values-2.js @@ -0,0 +1,142 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2024 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: > + Hours in day is correctly rounded using precise mathematical values. +info: | + get Temporal.ZonedDateTime.prototype.hoursInDay + + ... + 14. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]]. + 15. Return 𝔽(diffNs / (3.6 × 10^12)). +features: [Temporal] +---*/ + +// Randomly generated test data. +const data = [ + { + hours: 816, + nanoseconds: 2049_187_497_660, + }, + { + hours: 7825, + nanoseconds: 1865_665_040_770, + }, + { + hours: 0, + nanoseconds: 1049_560_584_034, + }, + { + hours: 2055144, + nanoseconds: 2502_078_444_371, + }, + { + hours: 31, + nanoseconds: 1010_734_758_745, + }, + { + hours: 24, + nanoseconds: 2958_999_560_387, + }, + { + hours: 0, + nanoseconds: 342_058_521_588, + }, + { + hours: 17746, + nanoseconds: 3009_093_506_309, + }, + { + hours: 4, + nanoseconds: 892_480_914_569, + }, + { + hours: 3954, + nanoseconds: 571_647_777_618, + }, + { + hours: 27, + nanoseconds: 2322_199_502_640, + }, + { + hours: 258054064, + nanoseconds: 2782_411_891_222, + }, + { + hours: 1485, + nanoseconds: 2422_559_903_100, + }, + { + hours: 0, + nanoseconds: 1461_068_214_153, + }, + { + hours: 393, + nanoseconds: 1250_229_561_658, + }, + { + hours: 0, + nanoseconds: 91_035_820, + }, + { + hours: 0, + nanoseconds: 790_982_655, + }, + { + hours: 150, + nanoseconds: 608_531_524, + }, + { + hours: 5469, + nanoseconds: 889_204_952, + }, + { + hours: 7870, + nanoseconds: 680_042_770, + }, +]; + +const nsPerHour = 3600_000_000_000; + +const fractionDigits = Math.log10(nsPerHour) + Math.log10(100_000_000_000) - Math.log10(36); +assert.sameValue(fractionDigits, 22); + +for (let {hours, nanoseconds} of data) { + assert(nanoseconds < nsPerHour); + + // Compute enough fractional digits to approximate the exact result. Use BigInts + // to avoid floating point precision loss. Fill to the left with implicit zeros. + let fraction = ((BigInt(nanoseconds) * 100_000_000_000n) / 36n).toString().padStart(fractionDigits, "0"); + + // Get the Number approximation from the string representation. + let expected = Number(`${hours}.${fraction}`); + + let todayInstant = 0n; + let tomorrowInstant = BigInt(hours) * BigInt(nsPerHour) + BigInt(nanoseconds); + + let timeZone = new class extends Temporal.TimeZone { + #getPossibleInstantsFor = 0; + + getPossibleInstantsFor() { + if (++this.#getPossibleInstantsFor === 1) { + return [new Temporal.Instant(todayInstant)]; + } + assert.sameValue(this.#getPossibleInstantsFor, 2); + return [new Temporal.Instant(tomorrowInstant)]; + } + }("UTC"); + + let zdt = new Temporal.ZonedDateTime(0n, timeZone); + let actual = zdt.hoursInDay; + + assert.sameValue( + actual, + expected, + `hours=${hours}, nanoseconds=${nanoseconds}`, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..020324f35c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js @@ -0,0 +1,133 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: > + Hours in day is correctly rounded using precise mathematical values. +info: | + get Temporal.ZonedDateTime.prototype.hoursInDay + + ... + 15. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]]. + 16. Return 𝔽(diffNs / (3.6 × 10^12)). +features: [Temporal] +---*/ + +const maxInstant = 86_40000_00000_00000_00000n; + +function nextUp(num) { + if (!Number.isFinite(num)) { + return num; + } + if (num === 0) { + return Number.MIN_VALUE; + } + + var f64 = new Float64Array([num]); + var u64 = new BigUint64Array(f64.buffer); + u64[0] += (num < 0 ? -1n : 1n); + return f64[0]; +} + +function nextDown(num) { + if (!Number.isFinite(num)) { + return num; + } + if (num === 0) { + return -Number.MIN_VALUE; + } + + var f64 = new Float64Array([num]); + var u64 = new BigUint64Array(f64.buffer); + u64[0] += (num < 0 ? 1n : -1n); + return f64[0]; +} + +function BigIntAbs(n) { + return n >= 0 ? n : -n; +} + +function test(todayInstant, tomorrowInstant, expected) { + let timeZone = new class extends Temporal.TimeZone { + #getPossibleInstantsFor = 0; + + getPossibleInstantsFor() { + if (++this.#getPossibleInstantsFor === 1) { + return [new Temporal.Instant(todayInstant)]; + } + assert.sameValue(this.#getPossibleInstantsFor, 2); + return [new Temporal.Instant(tomorrowInstant)]; + } + }("UTC"); + + let zdt = new Temporal.ZonedDateTime(0n, timeZone); + let zdt_hoursInDay = zdt.hoursInDay; + + assert.sameValue(zdt_hoursInDay, expected, "hoursInDay return value"); + + // Ensure the |expected| value is actually correctly rounded. + + const nsPerSec = 1000n * 1000n * 1000n; + const secPerHour = 60n * 60n; + const nsPerHour = secPerHour * nsPerSec; + + function toNanoseconds(hours) { + let wholeHours = BigInt(Math.trunc(hours)) * nsPerHour; + let fractionalHours = BigInt(Math.trunc(hours % 1 * Number(nsPerHour))); + return wholeHours + fractionalHours; + } + + let diff = tomorrowInstant - todayInstant; + let nanosInDay = toNanoseconds(zdt_hoursInDay); + + // The next number gives a less precise result. + let next = toNanoseconds(nextUp(zdt_hoursInDay)); + assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - next)); + + // The previous number gives a less precise result. + let prev = toNanoseconds(nextDown(zdt_hoursInDay)); + assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - prev)); + + // This computation can be inaccurate. + let inaccurate = Number(diff) / Number(nsPerHour); + + // Doing it component-wise can produce more accurate results. + let hours = Number(diff / nsPerSec) / Number(secPerHour); + let fractionalHours = Number(diff % nsPerSec) / Number(nsPerHour); + assert.sameValue(hours + fractionalHours, expected); + + // Ensure the result is more precise than the inaccurate result. + let inaccurateNanosInDay = toNanoseconds(inaccurate); + assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - inaccurateNanosInDay)); +} + +test(-maxInstant, 0n, 2400000000); +test(-maxInstant, 1n, 2400000000); +test(-maxInstant, 10n, 2400000000); +test(-maxInstant, 100n, 2400000000); + +test(-maxInstant, 1_000n, 2400000000); +test(-maxInstant, 10_000n, 2400000000); +test(-maxInstant, 100_000n, 2400000000); + +test(-maxInstant, 1_000_000n, 2400000000.0000005); +test(-maxInstant, 10_000_000n, 2400000000.000003); +test(-maxInstant, 100_000_000n, 2400000000.0000277); + +test(-maxInstant, 1_000_000_000n, 2400000000.000278); +test(-maxInstant, 10_000_000_000n, 2400000000.0027776); +test(-maxInstant, 100_000_000_000n, 2400000000.0277777); + +test(-maxInstant, maxInstant, 4800000000); +test(-maxInstant, maxInstant - 10_000_000_000n, 4799999999.997222); +test(-maxInstant, maxInstant - 10_000_000_000n + 1n, 4799999999.997222); +test(-maxInstant, maxInstant - 10_000_000_000n - 1n, 4799999999.997222); + +test(maxInstant, -maxInstant, -4800000000); +test(maxInstant, -(maxInstant - 10_000_000_000n), -4799999999.997222); +test(maxInstant, -(maxInstant - 10_000_000_000n + 1n), -4799999999.997222); +test(maxInstant, -(maxInstant - 10_000_000_000n - 1n), -4799999999.997222); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js new file mode 100644 index 0000000000..ab35491c4f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: The "hoursInDay" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "hoursInDay"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..7dc8591bd9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.hoursInDay); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..f6af7e270b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.hoursInDay, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..4a88f1a582 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.hoursInDay); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..057bdca141 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.hoursInDay); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..bab068de3a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.hoursinday +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-get-temporal.zoneddatetime.prototype.hoursinday steps 13–14: + 13. Let _todayInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _today_, *"compatible"*). + 14. Let _tomorrowInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _tomorrow_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-builtintimezonegetinstantfor step 14: + 14. Assert: _disambiguation_ is *"compatible"* or *"later"*. + sec-temporal-builtintimezonegetinstantfor step 16: + 16. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _later_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected1 = [ + "2001-09-09T00:00:00", + "2001-09-10T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.hoursInDay; +}, expected1); + +// Same, but test the other path where the time doesn't exist and +// GetPossibleInstantsFor is called again on a later time + +const expected2 = [ + "2030-01-01T00:00:00", + "2030-01-01T01:00:00", + "2030-01-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_893_457_800_000_000_000n, timeZone); + datetime.hoursInDay; +}, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js new file mode 100644 index 0000000000..1b745bc8f4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/basic.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: Basic test for inLeapYear +features: [Temporal] +---*/ + +assert.sameValue((new Temporal.ZonedDateTime(217178610123456789n, "UTC")).inLeapYear, + true, "leap year"); +assert.sameValue((new Temporal.ZonedDateTime(248714610123456789n, "UTC")).inLeapYear, + false, "non-leap year"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js new file mode 100644 index 0000000000..628ce75143 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const inLeapYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "inLeapYear").get; + +assert.sameValue(typeof inLeapYear, "function"); + +assert.throws(TypeError, () => inLeapYear.call(undefined), "undefined"); +assert.throws(TypeError, () => inLeapYear.call(null), "null"); +assert.throws(TypeError, () => inLeapYear.call(true), "true"); +assert.throws(TypeError, () => inLeapYear.call(""), "empty string"); +assert.throws(TypeError, () => inLeapYear.call(Symbol()), "symbol"); +assert.throws(TypeError, () => inLeapYear.call(1), "1"); +assert.throws(TypeError, () => inLeapYear.call({}), "plain object"); +assert.throws(TypeError, () => inLeapYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => inLeapYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..0554a9eba1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.inleapyear +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const inLeapYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "inLeapYear"); +Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("inLeapYear should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.inLeapYear; + +Object.defineProperty(Temporal.Calendar.prototype, "inLeapYear", inLeapYearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..d3c41c92f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.inleapyear +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.inLeapYear; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js new file mode 100644 index 0000000000..b91fe2b043 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: Custom calendar tests for inLeapYear(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + inLeapYear(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "inLeapYear arguments"); + return true; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.inLeapYear; +assert.sameValue(result, true, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js new file mode 100644 index 0000000000..512c2bdf8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: The "inLeapYear" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "inLeapYear"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..e30672adae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.inLeapYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..7667eb1548 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.inLeapYear, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..8a73653452 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.inLeapYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..542b349b62 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.inLeapYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js new file mode 100644 index 0000000000..f60b463dc8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/inLeapYear/validate-calendar-value.js @@ -0,0 +1,56 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.inleapyear +description: Validate result returned from calendar inLeapYear() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [0, TypeError], + [-0, TypeError], + [42, TypeError], + [7.1, TypeError], + [NaN, TypeError], + [Infinity, TypeError], + [-Infinity, TypeError], + ["", TypeError], + ["a string", TypeError], + ["0", TypeError], + [Symbol("foo"), TypeError], + [0n, TypeError], + [42n, TypeError], + [{}, TypeError], + [{valueOf() { return false; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + inLeapYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.inLeapYear, `${typeof result} ${String(result)} not converted to boolean`); +}); + +const preservedResults = [ + true, + false, +]; + +preservedResults.forEach(result => { + const calendar = new class extends Temporal.Calendar { + inLeapYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.sameValue(instance.inLeapYear, result, `${typeof result} ${String(result)} preserved`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js new file mode 100644 index 0000000000..c3c65270f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/balance-negative-time-units.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.microsecond +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–4: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.microsecond step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +assert.sameValue(datetime.microsecond, 0); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js new file mode 100644 index 0000000000..598c583d72 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const microsecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "microsecond").get; + +assert.sameValue(typeof microsecond, "function"); + +assert.throws(TypeError, () => microsecond.call(undefined), "undefined"); +assert.throws(TypeError, () => microsecond.call(null), "null"); +assert.throws(TypeError, () => microsecond.call(true), "true"); +assert.throws(TypeError, () => microsecond.call(""), "empty string"); +assert.throws(TypeError, () => microsecond.call(Symbol()), "symbol"); +assert.throws(TypeError, () => microsecond.call(1), "1"); +assert.throws(TypeError, () => microsecond.call({}), "plain object"); +assert.throws(TypeError, () => microsecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => microsecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..35639e06cc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.microsecond +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.microsecond; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js new file mode 100644 index 0000000000..0948f3578c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/negative-epochnanoseconds.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.microsecond +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +assert.sameValue(datetime.microsecond, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js new file mode 100644 index 0000000000..f9d4cd67d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: The "microsecond" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "microsecond"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..027044911b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.microsecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..ab74ef0eb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.microsecond, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..38560f2714 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.microsecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..fd35a38fd8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/microsecond/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.microsecond +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.microsecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js new file mode 100644 index 0000000000..0c9a387d10 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/balance-negative-time-units.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.millisecond +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–6: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.millisecond step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1_000_001n, tz); + +assert.sameValue(datetime.millisecond, 0); +assert.sameValue(datetime.microsecond, 999); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js new file mode 100644 index 0000000000..22d91cf12c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const millisecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "millisecond").get; + +assert.sameValue(typeof millisecond, "function"); + +assert.throws(TypeError, () => millisecond.call(undefined), "undefined"); +assert.throws(TypeError, () => millisecond.call(null), "null"); +assert.throws(TypeError, () => millisecond.call(true), "true"); +assert.throws(TypeError, () => millisecond.call(""), "empty string"); +assert.throws(TypeError, () => millisecond.call(Symbol()), "symbol"); +assert.throws(TypeError, () => millisecond.call(1), "1"); +assert.throws(TypeError, () => millisecond.call({}), "plain object"); +assert.throws(TypeError, () => millisecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => millisecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..7e38b494bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.millisecond +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.millisecond; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js new file mode 100644 index 0000000000..b489d8d9a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/negative-epochnanoseconds.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.millisecond +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +assert.sameValue(datetime.millisecond, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js new file mode 100644 index 0000000000..7ea0edc462 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: The "millisecond" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "millisecond"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..d1aeed81d0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.millisecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..b357f6ac48 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.millisecond, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..b90f3a2799 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.millisecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..0f09e240aa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/millisecond/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.millisecond +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.millisecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js new file mode 100644 index 0000000000..90ca2b62f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/balance-negative-time-units.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.minute +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–10: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.minute step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(60_000_000_001n, tz); + +assert.sameValue(datetime.minute, 0); +assert.sameValue(datetime.second, 59); +assert.sameValue(datetime.millisecond, 999); +assert.sameValue(datetime.microsecond, 999); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js new file mode 100644 index 0000000000..6f3cd8715d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const minute = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "minute").get; + +assert.sameValue(typeof minute, "function"); + +assert.throws(TypeError, () => minute.call(undefined), "undefined"); +assert.throws(TypeError, () => minute.call(null), "null"); +assert.throws(TypeError, () => minute.call(true), "true"); +assert.throws(TypeError, () => minute.call(""), "empty string"); +assert.throws(TypeError, () => minute.call(Symbol()), "symbol"); +assert.throws(TypeError, () => minute.call(1), "1"); +assert.throws(TypeError, () => minute.call({}), "plain object"); +assert.throws(TypeError, () => minute.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => minute.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..9425752d0c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.minute +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.minute; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js new file mode 100644 index 0000000000..d67ef59b2a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: The "minute" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "minute"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..907f510ed9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.minute); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..16116a21a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.minute, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..37e77f338d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.minute); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..a043b13ad7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/minute/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.minute +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.minute); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js new file mode 100644 index 0000000000..d35fbe41dd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const month = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "month").get; + +assert.sameValue(typeof month, "function"); + +assert.throws(TypeError, () => month.call(undefined), "undefined"); +assert.throws(TypeError, () => month.call(null), "null"); +assert.throws(TypeError, () => month.call(true), "true"); +assert.throws(TypeError, () => month.call(""), "empty string"); +assert.throws(TypeError, () => month.call(Symbol()), "symbol"); +assert.throws(TypeError, () => month.call(1), "1"); +assert.throws(TypeError, () => month.call({}), "plain object"); +assert.throws(TypeError, () => month.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => month.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..f754556f08 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.month +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const monthOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "month"); +Object.defineProperty(Temporal.Calendar.prototype, "month", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("month should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.month; + +Object.defineProperty(Temporal.Calendar.prototype, "month", monthOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..fd396d9685 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.month +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.month; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js new file mode 100644 index 0000000000..16071b8041 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: Custom calendar tests for month(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + month(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "month arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.month; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js new file mode 100644 index 0000000000..f60d4b541f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: The "month" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "month"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..797b041649 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.month); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..3ae559d72f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.month, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..fcc5c7d50a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.month); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7ddf852475 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.month); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js new file mode 100644 index 0000000000..d43ee61f98 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/month/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.month +description: Validate result returned from calendar month() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + month() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.month, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js new file mode 100644 index 0000000000..a11ac57133 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const monthCode = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthCode").get; + +assert.sameValue(typeof monthCode, "function"); + +assert.throws(TypeError, () => monthCode.call(undefined), "undefined"); +assert.throws(TypeError, () => monthCode.call(null), "null"); +assert.throws(TypeError, () => monthCode.call(true), "true"); +assert.throws(TypeError, () => monthCode.call(""), "empty string"); +assert.throws(TypeError, () => monthCode.call(Symbol()), "symbol"); +assert.throws(TypeError, () => monthCode.call(1), "1"); +assert.throws(TypeError, () => monthCode.call({}), "plain object"); +assert.throws(TypeError, () => monthCode.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => monthCode.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..861ad18b66 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.monthcode +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const monthCodeOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthCode"); +Object.defineProperty(Temporal.Calendar.prototype, "monthCode", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("monthCode should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.monthCode; + +Object.defineProperty(Temporal.Calendar.prototype, "monthCode", monthCodeOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..9527493caa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.monthcode +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.monthCode; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js new file mode 100644 index 0000000000..60531203bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: Custom calendar tests for monthCode(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + monthCode(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "monthCode arguments"); + return "M01"; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.monthCode; +assert.sameValue(result, "M01", "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js new file mode 100644 index 0000000000..5dd6d071c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: The "monthCode" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthCode"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..f2b7a6477c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.monthCode); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..688409ca27 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.monthCode, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..9d82bef0fe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.monthCode); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..50e670522c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.monthCode); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js new file mode 100644 index 0000000000..901d158707 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthCode/validate-calendar-value.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthcode +description: Validate result returned from calendar monthCode() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [Symbol("foo"), TypeError], + [null, TypeError], + [true, TypeError], + [false, TypeError], + [7.1, TypeError], + [{toString() { return "M01"; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + monthCode() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.monthCode, `${typeof result} ${String(result)} not converted to string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js new file mode 100644 index 0000000000..4c46612d39 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const monthsInYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthsInYear").get; + +assert.sameValue(typeof monthsInYear, "function"); + +assert.throws(TypeError, () => monthsInYear.call(undefined), "undefined"); +assert.throws(TypeError, () => monthsInYear.call(null), "null"); +assert.throws(TypeError, () => monthsInYear.call(true), "true"); +assert.throws(TypeError, () => monthsInYear.call(""), "empty string"); +assert.throws(TypeError, () => monthsInYear.call(Symbol()), "symbol"); +assert.throws(TypeError, () => monthsInYear.call(1), "1"); +assert.throws(TypeError, () => monthsInYear.call({}), "plain object"); +assert.throws(TypeError, () => monthsInYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => monthsInYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..16dd9910c7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.monthsinyear +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const monthsInYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthsInYear"); +Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("monthsInYear should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.monthsInYear; + +Object.defineProperty(Temporal.Calendar.prototype, "monthsInYear", monthsInYearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..cf715f9643 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.monthsinyear +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.monthsInYear; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js new file mode 100644 index 0000000000..07df89ecd8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: Custom calendar tests for monthsInYear(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + monthsInYear(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "monthsInYear arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.monthsInYear; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js new file mode 100644 index 0000000000..bcad139c78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: The "monthsInYear" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "monthsInYear"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..f5e3794a47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.monthsInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..b6ade0c6e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.monthsInYear, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..19298d27bb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.monthsInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..c086a9c140 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.monthsInYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js new file mode 100644 index 0000000000..140e7ffebe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/monthsInYear/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.monthsinyear +description: Validate result returned from calendar monthsInYear() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + monthsInYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.monthsInYear, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js new file mode 100644 index 0000000000..bfc35fc9d5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const nanosecond = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "nanosecond").get; + +assert.sameValue(typeof nanosecond, "function"); + +assert.throws(TypeError, () => nanosecond.call(undefined), "undefined"); +assert.throws(TypeError, () => nanosecond.call(null), "null"); +assert.throws(TypeError, () => nanosecond.call(true), "true"); +assert.throws(TypeError, () => nanosecond.call(""), "empty string"); +assert.throws(TypeError, () => nanosecond.call(Symbol()), "symbol"); +assert.throws(TypeError, () => nanosecond.call(1), "1"); +assert.throws(TypeError, () => nanosecond.call({}), "plain object"); +assert.throws(TypeError, () => nanosecond.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => nanosecond.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..f91ac58107 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.nanosecond +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.nanosecond; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js new file mode 100644 index 0000000000..892b93bab0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/negative-epochnanoseconds.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.nanosecond +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +assert.sameValue(datetime.nanosecond, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js new file mode 100644 index 0000000000..fe1edf18b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: The "nanosecond" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "nanosecond"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..976416762b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.nanosecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..1d1626342f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.nanosecond, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..b481e7082b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.nanosecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..c1cfd89e9e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/nanosecond/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.nanosecond +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.nanosecond); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js new file mode 100644 index 0000000000..32c57e464b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/basic.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: Basic tests for Temporal.ZonedDateTime.prototype.offset +features: [BigInt, Temporal] +---*/ + +function test(timeZoneIdentifier, expectedOffsetString, description) { + const timeZone = new Temporal.TimeZone(timeZoneIdentifier); + const datetime = new Temporal.ZonedDateTime(0n, timeZone); + assert.sameValue(datetime.offset, expectedOffsetString, description); +} + +test("UTC", "+00:00", "offset of UTC is +00:00"); +test("+01:00", "+01:00", "positive offset"); +test("-05:00", "-05:00", "negative offset"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js new file mode 100644 index 0000000000..9cacefd093 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const offset = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offset").get; + +assert.sameValue(typeof offset, "function"); + +assert.throws(TypeError, () => offset.call(undefined), "undefined"); +assert.throws(TypeError, () => offset.call(null), "null"); +assert.throws(TypeError, () => offset.call(true), "true"); +assert.throws(TypeError, () => offset.call(""), "empty string"); +assert.throws(TypeError, () => offset.call(Symbol()), "symbol"); +assert.throws(TypeError, () => offset.call(1), "1"); +assert.throws(TypeError, () => offset.call({}), "plain object"); +assert.throws(TypeError, () => offset.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => offset.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..ee3c0c0504 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.offset +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.offset; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js new file mode 100644 index 0000000000..1764dd61fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: The "offset" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offset"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..0a46276d11 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.offset); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..1f60e1b765 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.offset, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..69db8612bd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.offset); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..0d0ea6a623 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offset/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offset +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.offset); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js new file mode 100644 index 0000000000..f0c591f896 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const offsetNanoseconds = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offsetNanoseconds").get; + +assert.sameValue(typeof offsetNanoseconds, "function"); + +assert.throws(TypeError, () => offsetNanoseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => offsetNanoseconds.call(null), "null"); +assert.throws(TypeError, () => offsetNanoseconds.call(true), "true"); +assert.throws(TypeError, () => offsetNanoseconds.call(""), "empty string"); +assert.throws(TypeError, () => offsetNanoseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => offsetNanoseconds.call(1), "1"); +assert.throws(TypeError, () => offsetNanoseconds.call({}), "plain object"); +assert.throws(TypeError, () => offsetNanoseconds.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => offsetNanoseconds.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..4e67a66806 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.offsetnanoseconds +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.offsetNanoseconds; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js new file mode 100644 index 0000000000..d1aa0c79c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: The "offsetNanoseconds" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "offsetNanoseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..e6da8ef60c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.offsetNanoseconds); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..1970edc4ff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.offsetNanoseconds, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..05a2465c80 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.offsetNanoseconds); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..70812b8800 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/offsetNanoseconds/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.offsetNanoseconds); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js new file mode 100644 index 0000000000..5af4afe5cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/prop-desc.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-zoneddatetime-prototype +description: The "prototype" property of Temporal.ZonedDateTime +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue(typeof Temporal.ZonedDateTime.prototype, "object"); +assert.notSameValue(Temporal.ZonedDateTime.prototype, null); + +verifyProperty(Temporal.ZonedDateTime, "prototype", { + writable: false, + enumerable: false, + configurable: false, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js new file mode 100644 index 0000000000..ccb7839122 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const round = Temporal.ZonedDateTime.prototype.round; + +assert.sameValue(typeof round, "function"); + +const args = ["hour"]; + +assert.throws(TypeError, () => round.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => round.apply(null, args), "null"); +assert.throws(TypeError, () => round.apply(true, args), "true"); +assert.throws(TypeError, () => round.apply("", args), "empty string"); +assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => round.apply(1, args), "1"); +assert.throws(TypeError, () => round.apply({}, args), "plain object"); +assert.throws(TypeError, () => round.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => round.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..fd909ce3de --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd"); +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateAdd should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.round("day"); + +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..74e7a5376e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.round("day"); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js new file mode 100644 index 0000000000..bca23b8eac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Tests that Temporal.ZonedDateTime.prototype.round + 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.ZonedDateTime.prototype.round), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.round), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.round), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.round.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js new file mode 100644 index 0000000000..776cb7454f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/div-zero.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown if the calculated day length is zero +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #calls = 0; + getPossibleInstantsFor(dateTime) { + if (++this.#calls === 2) { + return super.getPossibleInstantsFor(dateTime.withCalendar("iso8601").subtract({ days: 1 })); + } + return super.getPossibleInstantsFor(dateTime); + } +} + +const units = ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]; +for (const smallestUnit of units) { + const zdt = new Temporal.ZonedDateTime(0n, new TimeZone("UTC")); + assert.throws(RangeError, () => zdt.round({ smallestUnit, roundingIncrement: 2 }), `zero day-length with smallestUnit ${smallestUnit}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..0c622df167 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); + +const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); +instance.round({ smallestUnit: "hours" }); + +assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js new file mode 100644 index 0000000000..7e48739e9d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Temporal.ZonedDateTime.prototype.round.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.ZonedDateTime.prototype.round, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js new file mode 100644 index 0000000000..a91ac4b450 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Temporal.ZonedDateTime.prototype.round.name is "round". +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.ZonedDateTime.prototype.round, "name", { + value: "round", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js new file mode 100644 index 0000000000..827a5b440a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.round({ smallestUnit: "millisecond" }); +assert.sameValue(result.epochNanoseconds, -13849765_000_000_000n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js new file mode 100644 index 0000000000..75d14fe479 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Temporal.ZonedDateTime.prototype.round 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.ZonedDateTime.prototype.round(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.round), false, + "isConstructor(Temporal.ZonedDateTime.prototype.round)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js new file mode 100644 index 0000000000..c2bf08c0f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/options-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: TypeError thrown when options argument is missing or a non-string primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + undefined, + null, + true, + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.round(value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js new file mode 100644 index 0000000000..48ca638b39 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js @@ -0,0 +1,158 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Properties on objects passed to round() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor on receiver's instant + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "call this.timeZone.getPossibleInstantsFor", + // AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const options = TemporalHelpers.propertyBagObserver(actual, { + smallestUnit: "nanoseconds", + roundingMode: "halfExpand", + roundingIncrement: 2, +}, "options"); + +const nextHourOptions = TemporalHelpers.propertyBagObserver(actual, { + smallestUnit: "hour", + roundingMode: "ceil", + roundingIncrement: 1, +}, "options"); + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime( + 988786472_987_654_321n, /* 2001-05-02T06:54:32.987654321Z */ + TemporalHelpers.timeZoneObserver(actual, "this.timeZone"), + calendar, +); + +const fallBackTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(1800), -3600_000_000_000); +const fallBackTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: fallBackTimeZone.getOffsetNanosecondsFor.bind(fallBackTimeZone), + getPossibleInstantsFor: fallBackTimeZone.getPossibleInstantsFor.bind(fallBackTimeZone), +}); +const fallBackInstance = new Temporal.ZonedDateTime(0n, fallBackTimeZoneObserver, calendar); +const beforeFallBackInstance = new Temporal.ZonedDateTime(-3599_000_000_000n, fallBackTimeZoneObserver, calendar); + +const springForwardTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(-1800), 3600_000_000_000); +const springForwardTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: springForwardTimeZone.getOffsetNanosecondsFor.bind(springForwardTimeZone), + getPossibleInstantsFor: springForwardTimeZone.getPossibleInstantsFor.bind(springForwardTimeZone), +}); +const springForwardInstance = new Temporal.ZonedDateTime(0n, springForwardTimeZoneObserver, calendar); +const beforeSpringForwardInstance = new Temporal.ZonedDateTime(-3599_000_000_000n, springForwardTimeZoneObserver, calendar); +// clear any observable operations that happen due to time zone or calendar +// calls in the constructors +actual.splice(0); + +instance.round(options); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +fallBackInstance.round(options); +assert.compareArray(actual, expected, "order of operations with preceding midnight at repeated wall-clock time"); +actual.splice(0); // clear + +beforeFallBackInstance.round(nextHourOptions); +assert.compareArray(actual, expected, "order of operations with rounding result at repeated wall-clock time"); +actual.splice(0); // clear + +const expectedSkippedDateTime = [ + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor on receiver's instant + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", + // AddZonedDateTime + "call this.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getOffsetNanosecondsFor", +]; + +springForwardInstance.round(options); +assert.compareArray(actual, expectedSkippedDateTime, "order of operations with preceding midnight at skipped wall-clock time"); +actual.splice(0); // clear + +const expectedSkippedResult = [ + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor on receiver's instant + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "call this.timeZone.getPossibleInstantsFor", + // AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]; + +beforeSpringForwardInstance.round(nextHourOptions); +assert.compareArray( + actual, + expectedSkippedResult, + "order of operations with following midnight and rounding result at skipped wall-clock time" +); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js new file mode 100644 index 0000000000..aa8fbfef3d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: The "round" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.round, + "function", + "`typeof ZonedDateTime.prototype.round` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "round", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js new file mode 100644 index 0000000000..095bb6640f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-direction.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Rounding down is towards the Big Bang, not the epoch or 1 BCE +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(-65_261_246_399_500_000_000n, "UTC"); // -000099-12-15T12:00:00.5Z +assert.sameValue( + instance.round({ smallestUnit: "second", roundingMode: "floor" }).epochNanoseconds, + -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z + "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode floor)" +); +assert.sameValue( + instance.round({ smallestUnit: "second", roundingMode: "trunc" }).epochNanoseconds, + -65_261_246_400_000_000_000n, // -000099-12-15T12:00:00Z + "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)" +); +assert.sameValue( + instance.round({ smallestUnit: "second", roundingMode: "ceil" }).epochNanoseconds, + -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z + "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)" +); +assert.sameValue( + instance.round({ smallestUnit: "second", roundingMode: "halfExpand" }).epochNanoseconds, + -65_261_246_399_000_000_000n, // -000099-12-15T12:00:01Z + "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js new file mode 100644 index 0000000000..a52601fe31 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/rounding-is-noop.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + No calendar or time zone methods are called under circumstances where rounding + is a no-op +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarThrowEverything(); +const timeZone = TemporalHelpers.timeZoneThrowEverything(); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +const noopRoundingOperations = [ + [{ smallestUnit: "nanoseconds" }, "smallestUnit ns"], + [{ smallestUnit: "nanoseconds", roundingIncrement: 1 }, "round to 1 ns"], +]; +for (const [options, descr] of noopRoundingOperations) { + const result = instance.round(options); + assert.notSameValue(result, instance, "rounding result should be a new object"); + assert.sameValue(result.epochNanoseconds, instance.epochNanoseconds, "instant should be unchanged"); + assert.sameValue(result.getCalendar(), instance.getCalendar(), "calendar should be preserved"); + assert.sameValue(result.getTimeZone(), instance.getTimeZone(), "time zone should be preserved"); +} + +const notNoopRoundingOperations = [ + [{ smallestUnit: "microseconds" }, "round to 1 µs"], + [{ smallestUnit: "nanoseconds", roundingIncrement: 2 }, "round to 2 ns"], +]; +for (const [options, descr] of notNoopRoundingOperations) { + assert.throws(Test262Error, () => instance.round(options), `rounding should not be a no-op with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js new file mode 100644 index 0000000000..89bc7f34f8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-nan.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown when roundingIncrement option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal-totemporaldatetimeroundingincrement step 5: + 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*). + sec-temporal.zoneddatetime.prototype.round step 8: + 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => datetime.round({ smallestUnit: 'second', roundingIncrement: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js new file mode 100644 index 0000000000..bca19257d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-non-integer.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Rounding for roundingIncrement option +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +const result = datetime.round({ smallestUnit: "nanosecond", roundingIncrement: 2.5, roundingMode: "expand" }); +assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_006n, "roundingIncrement 2.5 truncates to 2"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js new file mode 100644 index 0000000000..c7b5daf9b2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-out-of-range.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown when roundingIncrement option out of range +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -Infinity })); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: -1 })); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0 })); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 0.9 })); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: 1e9 + 1 })); +assert.throws(RangeError, () => datetime.round({ smallestUnit: "nanoseconds", roundingIncrement: Infinity })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js new file mode 100644 index 0000000000..95e3b77b12 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Fallback value for roundingIncrement option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal-totemporaldatetimeroundingincrement step 5: + 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*). + sec-temporal.zoneddatetime.prototype.round step 8: + 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +const explicit = datetime.round({ smallestUnit: 'second', roundingIncrement: undefined }); +assert.sameValue(explicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1"); + +const implicit = datetime.round({ smallestUnit: 'second' }); +assert.sameValue(implicit.epochNanoseconds, 1_000_000_001_000_000_000n, "default roundingIncrement is 1"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js new file mode 100644 index 0000000000..4b21c1e712 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingincrement-wrong-type.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Type conversions for roundingIncrement option +info: | + sec-getoption step 8.a: + a. Set _value_ to ? ToNumber(value). + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal-totemporaldatetimeroundingincrement step 5: + 5. Return ? ToTemporalRoundingIncrement(_normalizedOptions_, _maximum_, *false*). + sec-temporal.zoneddatetime.prototype.round step 8: + 8. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +TemporalHelpers.checkRoundingIncrementOptionWrongType( + (roundingIncrement) => datetime.round({ smallestUnit: 'second', roundingIncrement }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_001_000_000_000n, descr), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_000_000_000n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js new file mode 100644 index 0000000000..6ca2d5363a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-ceil.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "ceil". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175011_000_000_000n /* 1976-11-18T15:23:31+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "ceil"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js new file mode 100644 index 0000000000..8ba1f7a9a3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-expand.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "expand". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175011_000_000_000n /* 1976-11-18T15:23:31+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "expand"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js new file mode 100644 index 0000000000..5ce8c322ac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-floor.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "floor". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217119600_000_000_000n /* 1976-11-18T00:00:00+01:00 */], + ["minute", 217174980_000_000_000n /* 1976-11-18T15:23:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_123_000_000n /* 1976-11-18T15:23:30.123+01:00 */], + ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "floor"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js new file mode 100644 index 0000000000..3c76d77adb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfCeil.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "halfCeil". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "halfCeil"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js new file mode 100644 index 0000000000..910937fcf6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfEven.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "halfEven". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "halfEven"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js new file mode 100644 index 0000000000..aa42588b57 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfExpand.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "halfExpand". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_988_000n /* 1976-11-18T15:23:30.123988+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "halfExpand"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js new file mode 100644 index 0000000000..f41ae34d5d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfFloor.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "halfFloor". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "halfFloor"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js new file mode 100644 index 0000000000..68c26b68b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-halfTrunc.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "halfTrunc". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217206000_000_000_000n /* 1976-11-19T00:00:00+01:00 */], + ["minute", 217175040_000_000_000n /* 1976-11-18T15:24:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_124_000_000n /* 1976-11-18T15:23:30.124+01:00 */], + ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "halfTrunc"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js new file mode 100644 index 0000000000..82051763ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-invalid-string.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => datetime.round({ smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js new file mode 100644 index 0000000000..05cd46c481 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-trunc.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Tests calculations with roundingMode "trunc". +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */, "+01:00"); + +const expected = [ + ["day", 217119600_000_000_000n /* 1976-11-18T00:00:00+01:00 */], + ["minute", 217174980_000_000_000n /* 1976-11-18T15:23:00+01:00 */], + ["second", 217175010_000_000_000n /* 1976-11-18T15:23:30+01:00 */], + ["millisecond", 217175010_123_000_000n /* 1976-11-18T15:23:30.123+01:00 */], + ["microsecond", 217175010_123_987_000n /* 1976-11-18T15:23:30.123987+01:00 */], + ["nanosecond", 217175010_123_987_500n /* 1976-11-18T15:23:30.1239875+01:00 */], +]; + +const roundingMode = "trunc"; + +expected.forEach(([smallestUnit, expected]) => { + assert.sameValue( + instance.round({ smallestUnit, roundingMode }).epochNanoseconds, + expected, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js new file mode 100644 index 0000000000..0add782f70 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Fallback value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const explicit1 = datetime.round({ smallestUnit: "microsecond", roundingMode: undefined }); +assert.sameValue(explicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand"); +const implicit1 = datetime.round({ smallestUnit: "microsecond" }); +assert.sameValue(implicit1.epochNanoseconds, 1_000_000_000_123_988_000n, "default roundingMode is halfExpand"); + +const explicit2 = datetime.round({ smallestUnit: "millisecond", roundingMode: undefined }); +assert.sameValue(explicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand"); +const implicit2 = datetime.round({ smallestUnit: "millisecond" }); +assert.sameValue(implicit2.epochNanoseconds, 1_000_000_000_124_000_000n, "default roundingMode is halfExpand"); + +const explicit3 = datetime.round({ smallestUnit: "second", roundingMode: undefined }); +assert.sameValue(explicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand"); +const implicit3 = datetime.round({ smallestUnit: "second" }); +assert.sameValue(implicit3.epochNanoseconds, 1_000_000_000_000_000_000n, "default roundingMode is halfExpand"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js new file mode 100644 index 0000000000..921a1ef5c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundingmode-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand", + (roundingMode) => datetime.round({ smallestUnit: "microsecond", roundingMode }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js new file mode 100644 index 0000000000..47fba41c19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/roundto-invalid-string.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +const badValues = [ + "era", + "eraYear", + "year", + "month", + "week", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "years", + "months", + "weeks", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => datetime.round(smallestUnit), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js new file mode 100644 index 0000000000..0a1e6a1d4d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-too-large.js @@ -0,0 +1,87 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Round smallestUnit "day" with very large or very small divisor (dayLengthNs). +info: | + Temporal.ZonedDateTime.prototype.round ( roundTo ) + ... + 18. Let dayLengthNs be ℝ(endNs - startNs). + 19. If dayLengthNs ≤ 0, then + a. Throw a RangeError exception. + 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], + temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], + temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], + temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], + temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, + dayLengthNs). + ... + + RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode [ , dayLength ] ) + ... + 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode, dayLength). + ... + + RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, + roundingMode [ , dayLengthNs ] ) + ... + 4. If unit is "day", then + ... + b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + + microsecond) × 1000 + nanosecond) / dayLengthNs. + ... +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #count = 0; + #nanoseconds; + + constructor(nanoseconds) { + super("UTC"); + this.#nanoseconds = nanoseconds; + } + getPossibleInstantsFor(dateTime) { + if (++this.#count === 2) { + return [new Temporal.Instant(this.#nanoseconds)]; + } + return super.getPossibleInstantsFor(dateTime); + } +} + +const maxInstant = 86_40000_00000_00000_00000n; +const minInstant = -86_40000_00000_00000_00000n; +const oneDay = 24n * 60n * 60n * 1000n * 1000n * 1000n + +// Divisor too large. +{ + let tz = new TimeZone(maxInstant); + let zoned = new Temporal.ZonedDateTime(0n, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} +{ + let tz = new TimeZone(maxInstant); + let zoned = new Temporal.ZonedDateTime(minInstant, tz); + let result = zoned.round({ smallestUnit: "days" }); + assert(zoned.equals(result)); +} + +// Divisor too small. +{ + let tz = new TimeZone(minInstant); + let zoned = new Temporal.ZonedDateTime(0n, tz); + assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" })); +} +{ + let tz = new TimeZone(minInstant); + let zoned = new Temporal.ZonedDateTime(maxInstant - oneDay, tz); + assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js new file mode 100644 index 0000000000..c7904c2e93 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-daylength-zero-or-negative.js @@ -0,0 +1,69 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Round smallestUnit "day" with zero or negative day length. +info: | + Temporal.ZonedDateTime.prototype.round ( roundTo ) + ... + 18. Let dayLengthNs be ℝ(endNs - startNs). + 19. If dayLengthNs ≤ 0, then + a. Throw a RangeError exception. + 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], + temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], + temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], + temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], + temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, + dayLengthNs). + ... + + RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode [ , dayLength ] ) + ... + 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode, dayLength). + ... + + RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, + roundingMode [ , dayLengthNs ] ) + ... + 4. If unit is "day", then + ... + b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + + microsecond) × 1000 + nanosecond) / dayLengthNs. + ... +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #count = 0; + #nanoseconds; + + constructor(nanoseconds) { + super("UTC"); + this.#nanoseconds = nanoseconds; + } + getPossibleInstantsFor(dateTime) { + if (++this.#count === 2) { + return [new Temporal.Instant(this.#nanoseconds)]; + } + return super.getPossibleInstantsFor(dateTime); + } +} + +{ + let tz = new TimeZone(0n); + let zoned = new Temporal.ZonedDateTime(0n, tz); + assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" })); +} + +{ + let tz = new TimeZone(-1n); + let zoned = new Temporal.ZonedDateTime(0n, tz); + assert.throws(RangeError, () => zoned.round({ smallestUnit: "days" })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js new file mode 100644 index 0000000000..f694712457 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallest-unit-day-rounding-modes.js @@ -0,0 +1,97 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: > + Round smallestUnit "day" with various rounding modes. +info: | + Temporal.ZonedDateTime.prototype.round ( roundTo ) + ... + 18. Let dayLengthNs be ℝ(endNs - startNs). + ... + 20. Let roundResult be ! RoundISODateTime(temporalDateTime.[[ISOYear]], + temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], + temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], + temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], + temporalDateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode, + dayLengthNs). + ... + + RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode [ , dayLength ] ) + ... + 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, + increment, unit, roundingMode, dayLength). + ... + + RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, + roundingMode [ , dayLengthNs ] ) + ... + 4. If unit is "day", then + ... + b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + + microsecond) × 1000 + nanosecond) / dayLengthNs. + ... +features: [Temporal] +---*/ + +class TimeZone extends Temporal.TimeZone { + #count = 0; + #nanoseconds; + + constructor(nanoseconds) { + super("UTC"); + this.#nanoseconds = nanoseconds; + } + getPossibleInstantsFor(dateTime) { + if (++this.#count === 2) { + return [new Temporal.Instant(this.#nanoseconds)]; + } + return super.getPossibleInstantsFor(dateTime); + } +} + +function test(epochNanoseconds, tomorrowEpochNanoseconds, testCases) { + for (let [roundingMode, expected] of Object.entries(testCases)) { + let timeZone = new TimeZone(tomorrowEpochNanoseconds); + let zoned = new Temporal.ZonedDateTime(epochNanoseconds, timeZone); + let result = zoned.round({ smallestUnit: "days", roundingMode }); + assert.sameValue(result.epochNanoseconds, expected); + } +} + +const oneDay = 24n * 60n * 60n * 1000n * 1000n * 1000n; + +// Test positive divisor (dayLengthNs). +test(3n, 10n, { + ceil: oneDay, + floor: 0n, + trunc: 0n, + halfExpand: 0n, +}); + +test(-3n, 10n, { + ceil: 0n, + floor: -oneDay, + trunc: -oneDay, + halfExpand: 0n, +}); + +test(-3n, -10n, { + ceil: oneDay, + floor: 0n, + trunc: 0n, + halfExpand: 0n, +}); + +// Test values at int64 boundaries. +test(3n, /*INT64_MAX=*/ 9223372036854775807n, { + ceil: oneDay, + floor: 0n, + trunc: 0n, + halfExpand: 0n, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js new file mode 100644 index 0000000000..40c987eb81 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-invalid-string.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +const badValues = [ + "era", + "eraYear", + "year", + "month", + "week", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "years", + "months", + "weeks", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => datetime.round({ smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..d131690241 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round({ smallestUnit }), validUnits); +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.round(smallestUnit), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js new file mode 100644 index 0000000000..0a1f554087 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-string-shorthand.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: String as first argument is equivalent to options bag with smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +validUnits.forEach((smallestUnit) => { + const full = instance.round({ smallestUnit }); + const shorthand = instance.round(smallestUnit); + TemporalHelpers.assertZonedDateTimesEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js new file mode 100644 index 0000000000..f1f43da8d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/smallestunit-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => datetime.round({ smallestUnit }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_000_000_123_988_000n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js new file mode 100644 index 0000000000..f329bf25ce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatime.prototype.round +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "round", + [{ smallestUnit: 'second', roundingMode: 'ceil' }], + (result) => { + assert.sameValue(result.epochNanoseconds, 1_000_000_000n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 1, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 0, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..e2f42ae0c2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..7f56dc553d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.round({ smallestUnit: "second" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..feabc87f2c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.round({ smallestUnit: "second" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..f3f3a626e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.round({ smallestUnit: "second" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..922b114e0b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/round/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.round +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.round steps 14, 16, and 20: + 14. Let _instantStart_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dtStart_, *"compatible"*). + 16. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _zonedDateTime_.[[Calendar]], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0). + 20. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_roundResult_.[[Year]], [...], _roundResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, *"compatible"*, *"prefer"*). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2001-09-09T00:00:00", // called once on midnight of the input datetime + "2001-09-10T00:00:00", // called once on the previous value plus one calendar day + "2001-09-09T02:00:00", // called once on the rounding result +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.round({ smallestUnit: 'hour' }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js new file mode 100644 index 0000000000..f18e5737db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/balance-negative-time-units.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.second +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–8: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.second step 6: + 6. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1_000_000_001n, tz); + +assert.sameValue(datetime.second, 0); +assert.sameValue(datetime.millisecond, 999); +assert.sameValue(datetime.microsecond, 999); +assert.sameValue(datetime.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js new file mode 100644 index 0000000000..3abae4d6b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const second = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "second").get; + +assert.sameValue(typeof second, "function"); + +assert.throws(TypeError, () => second.call(undefined), "undefined"); +assert.throws(TypeError, () => second.call(null), "null"); +assert.throws(TypeError, () => second.call(true), "true"); +assert.throws(TypeError, () => second.call(""), "empty string"); +assert.throws(TypeError, () => second.call(Symbol()), "symbol"); +assert.throws(TypeError, () => second.call(1), "1"); +assert.throws(TypeError, () => second.call({}), "plain object"); +assert.throws(TypeError, () => second.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => second.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..7a4d35aaad --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.second +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.second; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js new file mode 100644 index 0000000000..c27117caf4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: The "second" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "second"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..360c2f8036 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.second); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..39c961233c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.second, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..b75712751a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.second); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..04885bc85d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/second/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.second +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.second); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..7c211ed7b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" }; +instance.since(arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..37a807a693 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Correct time zone calls are made when converting a ZonedDateTime-like property + bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const calendar = TemporalHelpers.calendarObserver(actual, "calendar"); + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.since(arg); + +const expected = [ + // GetTemporalCalendarSlotValueWithISODefault + "has calendar.dateAdd", + "has calendar.dateFromFields", + "has calendar.dateUntil", + "has calendar.day", + "has calendar.dayOfWeek", + "has calendar.dayOfYear", + "has calendar.daysInMonth", + "has calendar.daysInWeek", + "has calendar.daysInYear", + "has calendar.fields", + "has calendar.id", + "has calendar.inLeapYear", + "has calendar.mergeFields", + "has calendar.month", + "has calendar.monthCode", + "has calendar.monthDayFromFields", + "has calendar.monthsInYear", + "has calendar.weekOfYear", + "has calendar.year", + "has calendar.yearMonthFromFields", + "has calendar.yearOfWeek", + // lookup in ToTemporalZonedDateTime + "get calendar.dateFromFields", + "get calendar.fields", + // CalendarFields + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "call calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", +]; + +const expectedSpringForward = expected.concat([ + // DisambiguatePossibleInstants + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.since(arg); + +assert.compareArray( + actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime + expected, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..53d0d01258 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The calendar name is case-insensitive +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "IsO8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.since(arg); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..4722e5630e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Leap second is a valid ISO string for a calendar in a property bag +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.since(arg); +TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..c4f7c29c36 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-number.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; + assert.throws( + TypeError, + () => instance.since(arg), + "Numbers cannot be used as a calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..7fec62dde5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-string.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: A calendar ID is valid input for Calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "iso8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.since(arg); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..76eabbc22c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === 'string' ? RangeError : TypeError, + () => instance.since(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..52a3aee0f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.since(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..5aea2202fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar }; + +const instance = new Temporal.ZonedDateTime(0n, timeZone); +instance.since(arg); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..34f3ee71fc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-invalid-offset-string.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +badOffsets.forEach((offset) => { + const arg = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => instance.since(arg), + `"${offset} is not a valid offset string` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js new file mode 100644 index 0000000000..3a66dec910 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-offset-not-agreeing-with-timezone.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Property bag with offset property is rejected if offset does not agree with time zone +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone }; +assert.throws(RangeError, () => instance.since(properties), "offset property not matching time zone is rejected"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..b212528041 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.since(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..d44a948d58 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.since(properties, { largestUnit: "days" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..7404bcb2cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.since(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..e9c6d6a1b1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(TypeError, () => datetime.since(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js new file mode 100644 index 0000000000..2aca2d9cd6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-id-wrong-type.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + assert.throws(TypeError, () => datetime.since(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..bfa51c52a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js @@ -0,0 +1,69 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let expectedTimeZone = "UTC"; +const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance1.since({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => instance1.since({ year: 2020, month: 5, day: 2, timeZone }), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone` + ); +}); + +// The following are all valid strings so should not throw. They should produce +// expectedTimeZone, so additionally the operation will not throw due to the +// time zones being different on the receiver and the argument. + +timeZone = "2021-08-19T17:30Z"; +instance1.since({ year: 2020, month: 5, day: 2, timeZone }); + +expectedTimeZone = "-07:00"; +const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-08-19T17:30-07:00"; +instance2.since({ year: 2020, month: 5, day: 2, timeZone }); + +expectedTimeZone = "UTC"; +const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-08-19T17:30[UTC]"; +instance3.since({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30Z[UTC]"; +instance3.since({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30-07:00[UTC]"; +instance3.since({ year: 2020, month: 5, day: 2, timeZone }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..fe5907ef88 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const expectedTimeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// This operation should produce expectedTimeZone, so the following operation +// should not throw due to the time zones being different on the receiver and +// the argument. + +instance.since({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.since({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..048d907230 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-multiple-offsets.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const expectedTimeZone = "+01:46"; +const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone); +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +// This operation should produce expectedTimeZone, so the following operation +// should not throw due to the time zones being different on the receiver and +// the argument. + +instance.since({ year: 2020, month: 5, day: 2, timeZone }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..a6ab714edb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-year-zero.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.since({ year: 2020, month: 5, day: 2, timeZone }), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js new file mode 100644 index 0000000000..6e2b02f543 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +assert(instance1.since({ year: 1970, month: 1, day: 1, timeZone: "UTC" }).blank, "Time zone created from string 'UTC'"); + +const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30")); +assert(instance2.since({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }).blank, "Time zone created from string '-01:30'"); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..e7ee5b19db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => instance.since({ year: 2020, month: 5, day: 2, 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, () => instance.since({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..49ef648896 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-calendar-annotation.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"], + ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.since(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..f18c475a84 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-critical-unknown-annotation.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.since(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..7830684d15 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-date-with-utc-offset.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: UTC offset not valid with format that does not include a time +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const validStrings = [ + "1970-01-01T00Z[UTC]", + "1970-01-01T00Z[!UTC]", + "1970-01-01T00+00:00[UTC]", + "1970-01-01T00+00:00[!UTC]", +]; + +for (const arg of validStrings) { + const result = instance.since(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `"${arg}" is a valid UTC offset with time for ZonedDateTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.since(arg), + `"${arg}" UTC offset without time is not valid for ZonedDateTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..915733189c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-calendar.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.since(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..06ea7a0ed9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-multiple-time-zone.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.since(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js new file mode 100644 index 0000000000..c2d3f6cc44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-separators.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Time separator in string argument can vary +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00+00:00[UTC]", "uppercase T"], + ["1970-01-01t00:00+00:00[UTC]", "lowercase T"], + ["1970-01-01 00:00+00:00[UTC]", "space between date and time"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.since(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..8dd7c50c9a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-time-zone-annotation.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC]", "named, with no offset"], + ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"], + ["1970-01-01T00:00[+00:00]", "numeric, with no offset"], + ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"], + ["1970-01-01T00:00Z[UTC]", "named, with Z"], + ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"], + ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"], + ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"], + ["1970-01-01T00:00+00:00[UTC]", "named, with offset"], + ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"], + ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"], + ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.since(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..c1efd988fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-unknown-annotation.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Various forms of unknown annotation +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"], + ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"], + ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.since(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js new file mode 100644 index 0000000000..1d3b610010 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-wrong-type.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for ZonedDateTime +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [undefined, "undefined"], + [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"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.since(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js new file mode 100644 index 0000000000..802cdbf92d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/balance-negative-time-units.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-differencetime step 8: + 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-differenceisodatetime step 2: + 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + sec-temporal-differencezoneddatetime step 7: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + sec-temporal.zoneddatetime.prototype.since step 16: + 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone); +const options = { largestUnit: "days" }; + +const result1 = datetime.since(new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone), options); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance"); + +const result2 = datetime.since(new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone), options); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance"); + +const result3 = datetime.since(new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone), options); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance"); + +const result4 = datetime.since(new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone), options); +TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance"); + +const result5 = datetime.since(new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone), options); +TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance"); + +// This one is different because hours are later balanced again in BalanceDuration +const result6 = datetime.since(new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone), options); +TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/branding.js new file mode 100644 index 0000000000..129c2be910 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const since = Temporal.ZonedDateTime.prototype.since; + +assert.sameValue(typeof since, "function"); + +const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("UTC"))]; + +assert.throws(TypeError, () => since.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => since.apply(null, args), "null"); +assert.throws(TypeError, () => since.apply(true, args), "true"); +assert.throws(TypeError, () => since.apply("", args), "empty string"); +assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => since.apply(1, args), "1"); +assert.throws(TypeError, () => since.apply({}, args), "plain object"); +assert.throws(TypeError, () => since.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => since.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..60a4505527 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil"); +Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateUntil should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.since(new Temporal.ZonedDateTime(0n, "UTC")); + +Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..f7e23e64c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.since(new Temporal.ZonedDateTime(0n, "UTC")); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js new file mode 100644 index 0000000000..a3958bbb48 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Tests that Temporal.ZonedDateTime.prototype.since + 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.ZonedDateTime.prototype.since), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.since), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.since), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.since.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..7a5367bf29 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +// Basic difference with largestUnit larger than days. +// The call comes from this path: +// ZonedDateTime.since() -> DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() + +const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar); +later1.since(earlier, { largestUnit: "weeks" }); +assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days"); + +// Difference with rounding, with smallestUnit a calendar unit. +// The calls come from these paths: +// ZonedDateTime.since() -> +// DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() +// MoveRelativeDate -> calendar.dateAdd() +// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd() + +calendar.dateAddCallCount = 0; + +later1.since(earlier, { smallestUnit: "weeks" }); +assert.sameValue(calendar.dateAddCallCount, 4, "rounding difference with calendar smallestUnit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..6508201c05 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar }; +instance.since(arg); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js new file mode 100644 index 0000000000..91db3a3298 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-copy-of-options.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The dateUntil() method on the calendar is called with a copy of the options bag +features: [Temporal] +---*/ + +const originalOptions = { + largestUnit: "year", + shouldBeCopied: {}, +}; +let called = false; + +class Calendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(d1, d2, options) { + called = true; + assert.notSameValue(options, originalOptions, "options bag should be a copy"); + assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy"); + return new Temporal.Duration(-1); + } +} +const calendar = new Calendar(); +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +// exactly one year later; avoids NanosecondsToDays path +const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar); +later.since(earlier, originalOptions); +assert(called, "calendar.dateUntil must be called"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js new file mode 100644 index 0000000000..0bcc949ed2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-null-prototype-options.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Calendar.dateUntil method is called with a null-prototype object as the + options value when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const argument = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +instance.since(argument, { largestUnit: "year" }); +assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..6474c9aa96 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,117 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.zoneddatetime.prototype.since steps 14–18: + 14. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + c. Return ... + 15. ... + 16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). + 17. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + 18. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + later.since(earlier, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Additionally check the path that goes through AdjustRoundedDurationDays + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(-31536000_000_000_000n /* = -365 days */, "UTC", calendar); + const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar); + later.since(earlier, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' }); + }, + { + years: ["year", "year", "year"], + months: ["month", "month", "month"], + weeks: ["week", "week", "week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Also check the path that goes through RoundDuration when smallestUnit is +// given + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, smallestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + later.since(earlier, { smallestUnit }); + }, + { + years: ["year", "year", "year"], + months: ["month", "month", "month"], + weeks: ["week", "week", "week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js new file mode 100644 index 0000000000..b3fe7c118c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-fields-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.since step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar1 = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1); +const calendar2 = TemporalHelpers.calendarFieldsIterable(); +datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 }); + +assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called"); +assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js new file mode 100644 index 0000000000..600a7c4560 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.prototype.since step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject); + datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..38fbbcdbae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/constructor-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.since(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js new file mode 100644 index 0000000000..63378ff0d0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/date-and-time-durations-opposite-signs.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Rounding calculation in difference method can result in duration date and + time components with opposite signs +info: | + DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ) + 17. If _roundingGranularityIsNoop_ is *false*, then + ... + e. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], + _roundResult_.[[Weeks]], _days_, _daysResult_.[[NormalizedTime]], _settings_.[[RoundingIncrement]], + _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_, _calendarRec_, _timeZoneRec_, + _precalculatedPlainDateTime_). + f. Let _balanceResult_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], + _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _settings_.[[SmallestUnit]], + _plainRelativeTo_, _calendarRec_). + g. Set _result_ to ? CombineDateAndNormalizedTimeDuration(_balanceResult_, _adjustResult_.[[NormalizedTime]]). +features: [Temporal] +---*/ + +// Based on a test case by André Bargull + +const calendar = new class extends Temporal.Calendar { + #dateUntil = 0; + + dateUntil(one, two, options) { + let result = super.dateUntil(one, two, options); + if (++this.#dateUntil === 2) { + result = result.negated(); + } + return result; + } +}("iso8601"); + +const oneDay = 86400_000_000_000; +const start = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const end = new Temporal.ZonedDateTime(BigInt(500.5 * oneDay), "UTC", calendar); + +assert.throws(RangeError, () => end.since(start, { + largestUnit: "years", + smallestUnit: "hours", +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js new file mode 100644 index 0000000000..9c308ecd5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/duplicate-calendar-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const timeZone = 'Europe/Paris' + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + const instance = new Temporal.ZonedDateTime(0n, timeZone); + + assert.throws(RangeError, () => instance.since(arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..2fd5bc22e4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.prototype.since +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.since({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.since({ ...base, [prop]: obj })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js new file mode 100644 index 0000000000..c4634b3162 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when largestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string" +]; +for (const largestUnit of badValues) { + assert.throws(RangeError, () => later.since(earlier, { largestUnit }), + `"${largestUnit}" is not a valid value for largestUnit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..31b4b7dea9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js new file mode 100644 index 0000000000..5898a41959 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-smallestunit-mismatch.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when smallestUnit is larger than largestUnit +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; +for (let largestIdx = 1; largestIdx < units.length; largestIdx++) { + for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) { + const largestUnit = units[largestIdx]; + const smallestUnit = units[smallestIdx]; + assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit })); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js new file mode 100644 index 0000000000..bc79f17917 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fallback value for largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); + +const explicit = later.since(earlier, { largestUnit: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour"); +const implicit = later.since(earlier, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js new file mode 100644 index 0000000000..bca7496654 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Type conversions for largestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("largestUnit", "year", + (largestUnit) => later.since(earlier, { largestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js new file mode 100644 index 0000000000..58736d1b1e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/largestunit.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Specify behavior of ZonedDateTime.since when largest specified unit is specified +includes: [temporalHelpers.js] +features: [Temporal, BigInt] +---*/ +const thePast = new Temporal.ZonedDateTime(1234567890123456789n, '-08:00'); +const theFuture = new Temporal.ZonedDateTime(2345678901234567890n, '-08:00'); +TemporalHelpers.assertDuration(theFuture.since(thePast), 0, 0, 0, 0, 308641, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit unspecified)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'years' }), 35, 2, 0, 15, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is years)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'months' }), 0, 422, 0, 15, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is months)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'weeks' }), 0, 0, 1837, 1, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is weeks)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'days' }), 0, 0, 0, 12860, 1, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is days)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'hours' }), 0, 0, 0, 0, 308641, 56, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is hours)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'minutes' }), 0, 0, 0, 0, 0, 18518516, 51, 111, 111, 101, 'does not include higher units than necessary (largest unit is minutes)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'seconds' }), 0, 0, 0, 0, 0, 0, 1111111011, 111, 111, 101, 'does not include higher units than necessary (largest unit is seconds)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'milliseconds' }), 0, 0, 0, 0, 0, 0, 0, 1111111011111, 111, 101, 'does not include higher units than necessary (largest unit is milliseconds)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'microseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111, 101, 'does not include higher units than necessary (largest unit is microseconds)'); +TemporalHelpers.assertDuration(theFuture.since(thePast, { largestUnit: 'nanoseconds' }), 0, 0, 0, 0, 0, 0, 0, 0, 0, 1111111011111111000, 'does not include higher units than necessary (largest unit is nanoseconds)'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js new file mode 100644 index 0000000000..a9b6619fb8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/leap-second.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Leap second is a valid ISO string for ZonedDateTime +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone); + +let arg = "2016-12-31T23:59:60+00:00[UTC]"; +const result = instance.since(arg); +TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for ZonedDateTime" +); + +arg = "2000-05-02T12:34:56+23:59[+23:59:60]"; +assert.throws( + RangeError, + () => instance.since(arg), + "leap second in time zone name not valid" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js new file mode 100644 index 0000000000..7e7f1a1dff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Temporal.ZonedDateTime.prototype.since.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.ZonedDateTime.prototype.since, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js new file mode 100644 index 0000000000..b742e2620c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Temporal.ZonedDateTime.prototype.since.name is "since". +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.ZonedDateTime.prototype.since, "name", { + value: "since", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js new file mode 100644 index 0000000000..501f4ae30c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/negative-epochnanoseconds.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.since(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" }); +TemporalHelpers.assertDuration(result, 0, -5, 0, -7, -7, -9, -24, -999, -999, -999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js new file mode 100644 index 0000000000..23f993f25f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-loop-arbitrarily.js @@ -0,0 +1,78 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer +info: | + NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] ) + ... + 21. Repeat, while done is false, + a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]], + relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign). + b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]], + relativeResult.[[EpochNanoseconds]]). + c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs). + c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then + i. Set norm to oneDayLess. + ii. Set relativeResult to oneDayFarther. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const dayLengthNs = 86400000000000n; +const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601"); + +function createRelativeTo(count) { + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(50); +calls.splice(0); // Reset calls list after ZonedDateTime construction +zdt.since(other, { + largestUnit: "day", +}); +assert.sameValue( + calls.length, + 50 + 1, + "Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(100); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +zdt.since(other, { + largestUnit: "day", +}); +assert.sameValue( + calls.length, + 100 + 1, + "Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(105); +assert.throws(RangeError, () => zdt.since(other, { largestUnit: "day" }), "105 days > 2⁵³ ns"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js new file mode 100644 index 0000000000..0c0b07f5dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/normalized-time-duration-to-days-range-errors.js @@ -0,0 +1,126 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Abstract operation NormalizedTimeDurationToDays can throw four different + RangeErrors. +info: | + NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] ) + 22. If days < 0 and sign = 1, throw a RangeError exception. + 23. If days > 0 and sign = -1, throw a RangeError exception. + ... + 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception. + ... + 28. If dayLength ≥ 2⁵³, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +const dayNs = 86_400_000_000_000; +const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC"); +const oneZDT = new Temporal.ZonedDateTime(1n, "UTC"); +const epochInstant = new Temporal.Instant(0n); +const options = { largestUnit: "days" }; + +// Step 22: days < 0 and sign = 1 +let start = new Temporal.ZonedDateTime( + 0n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[epochInstant]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned in step 8, setting _startDateTime_ + -dayNs + 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.since( + oneZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 23: days > 0 and sign = -1 +start = new Temporal.ZonedDateTime( + 1n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[epochInstant]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + -dayNs + 1, // Returned in step 8, setting _startDateTime_ + dayNs - 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.since( + zeroZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 25: nanoseconds > 0 and sign = -1 +start = new Temporal.ZonedDateTime( + 1n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned in step 8, setting _startDateTime_ + -dayNs + 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.since( + zeroZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 28: day length is an unsafe integer +start = new Temporal.ZonedDateTime( + 0n, + timeZoneSubstituteValues( + // Not called in step 16 because _days_ = 0 + // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_ + [[new Temporal.Instant(2n ** 53n)]], + [] + ) +); +assert.throws(RangeError, () => + start.since( + oneZDT, + options + ), + "Should throw RangeError when time zone calculates an outrageous day length" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js new file mode 100644 index 0000000000..3f1b838344 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + Temporal.ZonedDateTime.prototype.since 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.ZonedDateTime.prototype.since(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.since), false, + "isConstructor(Temporal.ZonedDateTime.prototype.since)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js new file mode 100644 index 0000000000..26a66315d0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-object.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Empty or a function object may be used as options +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), {}); +TemporalHelpers.assertDuration( + result1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + "options may be an empty plain object" +); + +const result2 = instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), () => {}); +TemporalHelpers.assertDuration( + result2, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js new file mode 100644 index 0000000000..925553fc93 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC"); + +const explicit = later.since(earlier, undefined); +assert.sameValue(explicit.years, 0, "default largest unit is hours"); +assert.sameValue(explicit.months, 0, "default largest unit is hours"); +assert.sameValue(explicit.weeks, 0, "default largest unit is hours"); +assert.sameValue(explicit.days, 0, "default largest unit is hours"); +assert.sameValue(explicit.hours, 744, "default largest unit is hours"); +assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding"); + +const implicit = later.since(earlier); +assert.sameValue(implicit.years, 0, "default largest unit is hours"); +assert.sameValue(implicit.months, 0, "default largest unit is hours"); +assert.sameValue(implicit.weeks, 0, "default largest unit is hours"); +assert.sameValue(implicit.days, 0, "default largest unit is hours"); +assert.sameValue(implicit.hours, 744, "default largest unit is hours"); +assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-wrong-type.js new file mode 100644 index 0000000000..3789038a5b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.since(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js new file mode 100644 index 0000000000..566a1ce192 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js @@ -0,0 +1,408 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Properties on objects passed to since() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalZonedDateTime + "get other.calendar", + "has other.calendar.dateAdd", + "has other.calendar.dateFromFields", + "has other.calendar.dateUntil", + "has other.calendar.day", + "has other.calendar.dayOfWeek", + "has other.calendar.dayOfYear", + "has other.calendar.daysInMonth", + "has other.calendar.daysInWeek", + "has other.calendar.daysInYear", + "has other.calendar.fields", + "has other.calendar.id", + "has other.calendar.inLeapYear", + "has other.calendar.mergeFields", + "has other.calendar.month", + "has other.calendar.monthCode", + "has other.calendar.monthDayFromFields", + "has other.calendar.monthsInYear", + "has other.calendar.weekOfYear", + "has other.calendar.year", + "has other.calendar.yearMonthFromFields", + "has other.calendar.yearOfWeek", + "get other.calendar.dateFromFields", + "get other.calendar.fields", + "call other.calendar.fields", + "get other.day", + "get other.day.valueOf", + "call other.day.valueOf", + "get other.hour", + "get other.hour.valueOf", + "call other.hour.valueOf", + "get other.microsecond", + "get other.microsecond.valueOf", + "call other.microsecond.valueOf", + "get other.millisecond", + "get other.millisecond.valueOf", + "call other.millisecond.valueOf", + "get other.minute", + "get other.minute.valueOf", + "call other.minute.valueOf", + "get other.month", + "get other.month.valueOf", + "call other.month.valueOf", + "get other.monthCode", + "get other.monthCode.toString", + "call other.monthCode.toString", + "get other.nanosecond", + "get other.nanosecond.valueOf", + "call other.nanosecond.valueOf", + "get other.offset", + "get other.offset.toString", + "call other.offset.toString", + "get other.second", + "get other.second.valueOf", + "call other.second.valueOf", + "get other.timeZone", + "get other.year", + "get other.year.valueOf", + "call other.year.valueOf", + "has other.timeZone.getOffsetNanosecondsFor", + "has other.timeZone.getPossibleInstantsFor", + "has other.timeZone.id", + "call other.calendar.dateFromFields", + "get other.timeZone.getOffsetNanosecondsFor", + "get other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getOffsetNanosecondsFor", + // CalendarEquals + "get this.calendar.id", + "get other.calendar.id", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.roundingIncrement", + "get options.roundingIncrement", + "getOwnPropertyDescriptor options.roundingMode", + "get options.roundingMode", + "getOwnPropertyDescriptor options.largestUnit", + "get options.largestUnit", + "getOwnPropertyDescriptor options.smallestUnit", + "get options.smallestUnit", + "getOwnPropertyDescriptor options.additional", + "get options.additional", + // GetDifferenceSettings + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", +]; +const actual = []; + +const ownTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar); + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const ownDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); +const otherDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "other.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); +/* 2000-10-29T01:30-07:00, in the middle of the first repeated hour: */ +const fallBackInstance = new Temporal.ZonedDateTime(972808200_000_000_000n, ownDstTimeZone, ownCalendar); + +const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2004, + month: 5, + monthCode: "M05", + day: 12, + hour: 1, + minute: 46, + second: 40, + millisecond: 250, + microsecond: 500, + nanosecond: 750, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"), +}, "other"); + +function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) { + return TemporalHelpers.propertyBagObserver(actual, { + // order is significant, due to iterating through properties in order to + // copy them to an internal null-prototype object: + roundingIncrement, + roundingMode, + largestUnit, + smallestUnit, + additional: "property", + }, "options"); +} + +// clear any observable things that happened while constructing the objects +actual.splice(0); + +// basic order of observable operations, without rounding: +instance.since(otherDateTimePropertyBag, createOptionsObserver()); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +// short-circuit for identical objects will still test TimeZoneEquals if +// largestUnit is a calendar unit: +const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 9, + monthCode: "M09", + day: 9, + hour: 1, + minute: 46, + second: 40, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"), +}, "other"); + +instance.since(identicalPropertyBag, createOptionsObserver({ largestUnit: "years" })); +assert.compareArray(actual, expected.concat([ + "get this.timeZone.id", + "get other.timeZone.id", +]), "order of operations with identical dates and largestUnit a calendar unit"); +actual.splice(0); // clear + +// two ZonedDateTimes that denote the same wall-clock time in the time zone can +// avoid calling some calendar methods: +const fallBackPropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "-08:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: otherDstTimeZone, +}, "other"); +fallBackInstance.since(fallBackPropertyBag, createOptionsObserver({ largestUnit: "days" })); +assert.compareArray(actual, [ + // ToTemporalZonedDateTime + "get other.calendar", + "has other.calendar.dateAdd", + "has other.calendar.dateFromFields", + "has other.calendar.dateUntil", + "has other.calendar.day", + "has other.calendar.dayOfWeek", + "has other.calendar.dayOfYear", + "has other.calendar.daysInMonth", + "has other.calendar.daysInWeek", + "has other.calendar.daysInYear", + "has other.calendar.fields", + "has other.calendar.id", + "has other.calendar.inLeapYear", + "has other.calendar.mergeFields", + "has other.calendar.month", + "has other.calendar.monthCode", + "has other.calendar.monthDayFromFields", + "has other.calendar.monthsInYear", + "has other.calendar.weekOfYear", + "has other.calendar.year", + "has other.calendar.yearMonthFromFields", + "has other.calendar.yearOfWeek", + "get other.calendar.dateFromFields", + "get other.calendar.fields", + "call other.calendar.fields", + "get other.day", + "get other.day.valueOf", + "call other.day.valueOf", + "get other.hour", + "get other.hour.valueOf", + "call other.hour.valueOf", + "get other.microsecond", + "get other.microsecond.valueOf", + "call other.microsecond.valueOf", + "get other.millisecond", + "get other.millisecond.valueOf", + "call other.millisecond.valueOf", + "get other.minute", + "get other.minute.valueOf", + "call other.minute.valueOf", + "get other.month", + "get other.month.valueOf", + "call other.month.valueOf", + "get other.monthCode", + "get other.monthCode.toString", + "call other.monthCode.toString", + "get other.nanosecond", + "get other.nanosecond.valueOf", + "call other.nanosecond.valueOf", + "get other.offset", + "get other.offset.toString", + "call other.offset.toString", + "get other.second", + "get other.second.valueOf", + "call other.second.valueOf", + "get other.timeZone", + "get other.year", + "get other.year.valueOf", + "call other.year.valueOf", + "has other.timeZone.getOffsetNanosecondsFor", + "has other.timeZone.getPossibleInstantsFor", + "has other.timeZone.id", + "call other.calendar.dateFromFields", + "get other.timeZone.getOffsetNanosecondsFor", + "get other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getOffsetNanosecondsFor", + // NOTE: extra because of wall-clock time ambiguity: + "call other.timeZone.getOffsetNanosecondsFor", + // CalendarEquals + "get this.calendar.id", + "get other.calendar.id", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.roundingIncrement", + "get options.roundingIncrement", + "getOwnPropertyDescriptor options.roundingMode", + "get options.roundingMode", + "getOwnPropertyDescriptor options.largestUnit", + "get options.largestUnit", + "getOwnPropertyDescriptor options.smallestUnit", + "get options.smallestUnit", + "getOwnPropertyDescriptor options.additional", + "get options.additional", + // GetDifferenceSettings + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // TimeZoneEquals + "get this.timeZone.id", + "get other.timeZone.id", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + "get this.calendar.dateUntil", + // DifferenceZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", +], "order of operations with identical wall-clock times and largestUnit a calendar unit"); +actual.splice(0); // clear + +// Making largestUnit a calendar unit adds the following observable operations: +const expectedOpsForCalendarDifference = [ + // TimeZoneEquals + "get this.timeZone.id", + "get other.timeZone.id", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + "get this.calendar.dateUntil", + // precalculate PlainDateTime + "call this.timeZone.getOffsetNanosecondsFor", + // DifferenceZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + // DifferenceISODateTime + "call this.calendar.dateUntil", + // AddZonedDateTime + "call this.calendar.dateAdd", + "call this.timeZone.getPossibleInstantsFor", + // NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]; + +const expectedOpsForCalendarRounding = [ + // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime + "call this.calendar.dateAdd", + "call this.timeZone.getPossibleInstantsFor", + // RoundDuration → NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", +]; + +// code path that skips RoundDuration: +instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 })); +assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest year: +const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateAdd", // 12.d + "call this.calendar.dateAdd", // 12.f + "call this.calendar.dateUntil", // 12.n + "call this.calendar.dateAdd", // 12.x MoveRelativeDate + // (12.r not called because other units can't add up to >1 year at this point) + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 9.c + "call this.calendar.dateUntil" // 9.d +]); +instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" })); +assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest month: +const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateAdd", // 13.c + "call this.calendar.dateAdd", // 13.e + "call this.calendar.dateUntil", // 13.m + "call this.calendar.dateAdd", // 13.w MoveRelativeDate + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 10.d + "call this.calendar.dateUntil", // 10.e +]); +instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" })); +assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest week: +const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateUntil", // 14.f + "call this.calendar.dateAdd", // 14.p MoveRelativeDate + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 16 + "call this.calendar.dateUntil", // 17 +]); +instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" })); +assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks"); +actual.splice(0); // clear + +instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" })); +assert.compareArray(actual, expected, "order of operations with largestUnit being a time unit"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js new file mode 100644 index 0000000000..32c5015ca3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The "since" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.since, + "function", + "`typeof ZonedDateTime.prototype.since` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "since", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js new file mode 100644 index 0000000000..2347de2c3f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/proto-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.since(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..9b8bf8f410 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/read-time-fields-before-datefromfields.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.prototype.since step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.j: + j. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const duration = datetime.since({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar }); + +TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 1, 46, 40, 987, 654, 321); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js new file mode 100644 index 0000000000..6c185f0bea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/round-cross-unit-boundary.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Rounding can cross unit boundaries up to largestUnit +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Date units +{ + const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC"); + const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC"); + const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "-1 year -11 months balances to -2 years"); +} + +// Time units +{ + const earlier = new Temporal.ZonedDateTime(0n, "UTC"); + const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC"); + const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:59 balances to -2 hours"); +} + +// Both +{ + const earlier = new Temporal.ZonedDateTime(0n, "UTC"); + const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC"); + const duration = earlier.since(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding down 1 ns balances to -2 years"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js new file mode 100644 index 0000000000..8efd41b7f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/rounding-zero-year-month-week-length.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: > + A malicious calendar resulting in a year, month, or week length of zero is + handled correctly +info: | + RoundDuration + 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception. + ... + 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception. + ... + 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception. +features: [Temporal] +---*/ + +const cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Called several times, last call sets oneYear/Month/WeekDays to 0 + return new Temporal.PlainDate(1970, 1, 1); + } +}("iso8601"); + +const dt1 = new Temporal.ZonedDateTime(0n, "UTC", cal); +const dt2 = new Temporal.ZonedDateTime(365n * 86400_000_000_000n + 1n, "UTC", cal); + +assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "years" }), "zero year length handled correctly"); +assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "months" }), "zero month length handled correctly"); +assert.throws(RangeError, () => dt1.since(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js new file mode 100644 index 0000000000..274a5eb9fc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-nan.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when roundingIncrement option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.since step 13: + 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js new file mode 100644 index 0000000000..d2b4198b43 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-non-integer.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Rounding for roundingIncrement option +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" }); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2"); +const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" }); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js new file mode 100644 index 0000000000..055d9a8eae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-out-of-range.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when roundingIncrement option out of range +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity })); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 })); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 })); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 })); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 })); +assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js new file mode 100644 index 0000000000..c0e260c6f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fallback value for roundingIncrement option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.since step 13: + 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); + +const explicit = later.since(earlier, { roundingIncrement: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1"); + +const implicit = later.since(earlier, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js new file mode 100644 index 0000000000..52e13eb6cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingincrement-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Type conversions for roundingIncrement option +info: | + sec-getoption step 8.a: + a. Set _value_ to ? ToNumber(value). + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.since step 13: + 13. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); + +TemporalHelpers.checkRoundingIncrementOptionWrongType( + (roundingIncrement) => later.since(earlier, { roundingIncrement }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js new file mode 100644 index 0000000000..ef202aead8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-ceil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "ceil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-2]], + ["months", [0, 32], [0, -31]], + ["weeks", [0, 0, 140], [0, 0, -139]], + ["days", [0, 0, 0, 974], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -4]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "ceil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js new file mode 100644 index 0000000000..81759f95bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-expand.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "expand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 140], [0, 0, -140]], + ["days", [0, 0, 0, 974], [0, 0, 0, -974]], + ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23357]], + ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -18]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -865]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "expand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js new file mode 100644 index 0000000000..f5ec3ecb6e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-floor.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "floor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [2], [-3]], + ["months", [0, 31], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -140]], + ["days", [0, 0, 0, 973], [0, 0, 0, -974]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23357]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -18]], + ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -865]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "floor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js new file mode 100644 index 0000000000..fa6428bd02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfCeil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "halfCeil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfCeil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js new file mode 100644 index 0000000000..9058ecd380 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfEven.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "halfEven". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfEven"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js new file mode 100644 index 0000000000..7c35b8f8d1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfExpand.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "halfExpand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfExpand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js new file mode 100644 index 0000000000..f781610ec5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfFloor.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "halfFloor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfFloor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js new file mode 100644 index 0000000000..3277cae514 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-halfTrunc.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "halfTrunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfTrunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js new file mode 100644 index 0000000000..4f2b40168b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-invalid-string.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js new file mode 100644 index 0000000000..10bfe6c805 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-trunc.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Tests calculations with roundingMode "trunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [2], [-2]], + ["months", [0, 31], [0, -31]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -4]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "trunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + later.since(earlier, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + earlier.since(later, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js new file mode 100644 index 0000000000..a086ded204 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fallback value for roundingMode option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); + +const explicit1 = later.since(earlier, { smallestUnit: "microsecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc"); +const implicit1 = later.since(earlier, { smallestUnit: "microsecond" }); +TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc"); + +const explicit2 = later.since(earlier, { smallestUnit: "millisecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc"); +const implicit2 = later.since(earlier, { smallestUnit: "millisecond" }); +TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc"); + +const explicit3 = later.since(earlier, { smallestUnit: "second", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc"); +const implicit3 = later.since(earlier, { smallestUnit: "second" }); +TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js new file mode 100644 index 0000000000..00c84cfdba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/roundingmode-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc", + (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js new file mode 100644 index 0000000000..b27a214e85 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => later.since(earlier, { smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..f6985998f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js new file mode 100644 index 0000000000..5b7863939a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Fallback value for smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); + +const explicit = later.since(earlier, { smallestUnit: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond"); +const implicit = later.since(earlier, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js new file mode 100644 index 0000000000..15876eff64 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/smallestunit-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => later.since(earlier, { smallestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..6f44690729 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..6fd71013ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.since(other, { largestUnit: "days" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..23b6e8bb60 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(RangeError, () => datetime.since(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..94158e7004 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(TypeError, () => datetime.since(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..17d5b5f84a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.since step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Not called on the instance's time zone + +const expected1 = []; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.since({ year: 2005, month: 6, day: 2, timeZone: "UTC" }); +}, expected1); + +// Called on the argument's time zone + +const expected2 = [ + "2005-06-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + datetime.since({ year: 2005, month: 6, day: 2, timeZone }); +}, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js new file mode 100644 index 0000000000..e1063a41b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/year-zero.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-0000000-01-01T00:02Z[UTC]", + "-0000000-01-01T00:02+00:00[UTC]", + "-0000000-01-01T00:02:00.000000000+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.since(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js new file mode 100644 index 0000000000..779537326e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string-multiple-offsets.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:35"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]"; + +const result = instance.since(str); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, -30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes"); + +str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]"; +assert.throws( + RangeError, + () => instance.since(str), + "Trailing zeroes not allowed for sub-minute time zone identifiers" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js new file mode 100644 index 0000000000..a11ad11aa0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/since/zoneddatetime-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.since +description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let str = "1970-01-01T00:00"; +assert.throws(RangeError, () => instance.since(str), "bare date-time string is not a ZonedDateTime"); +str = "1970-01-01T00:00Z"; +assert.throws(RangeError, () => instance.since(str), "date-time + Z is not a ZonedDateTime"); +str = "1970-01-01T00:00+01:00"; +assert.throws(RangeError, () => instance.since(str), "date-time + offset is not a ZonedDateTime"); + +str = "1970-01-01T00:00[+01:00]"; +const result1 = instance.since(str); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone"); + +str = "1970-01-01T00:00Z[+01:00]"; +const result2 = instance.since(str); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone"); + +str = "1970-01-01T00:00+01:00[+01:00]"; +const result3 = instance.since(str); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match"); + +str = "1970-01-01T00:00-04:15[+01:00]"; +assert.throws(RangeError, () => instance.since(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js new file mode 100644 index 0000000000..4fe873fadc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const startOfDay = Temporal.ZonedDateTime.prototype.startOfDay; + +assert.sameValue(typeof startOfDay, "function"); + +assert.throws(TypeError, () => startOfDay.call(undefined), "undefined"); +assert.throws(TypeError, () => startOfDay.call(null), "null"); +assert.throws(TypeError, () => startOfDay.call(true), "true"); +assert.throws(TypeError, () => startOfDay.call(""), "empty string"); +assert.throws(TypeError, () => startOfDay.call(Symbol()), "symbol"); +assert.throws(TypeError, () => startOfDay.call(1), "1"); +assert.throws(TypeError, () => startOfDay.call({}), "plain object"); +assert.throws(TypeError, () => startOfDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => startOfDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..b2ccc52635 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.startOfDay(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js new file mode 100644 index 0000000000..e67c761fbb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: > + Tests that Temporal.ZonedDateTime.prototype.startOfDay + 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.ZonedDateTime.prototype.startOfDay), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.startOfDay), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.startOfDay), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.startOfDay.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..247f94a19d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); + +const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); +instance.startOfDay(); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js new file mode 100644 index 0000000000..9a93ac0f2e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: Temporal.ZonedDateTime.prototype.startOfDay.length is 0 +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.ZonedDateTime.prototype.startOfDay, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js new file mode 100644 index 0000000000..72dd5b4b55 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: Temporal.ZonedDateTime.prototype.startOfDay.name is "startOfDay". +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.ZonedDateTime.prototype.startOfDay, "name", { + value: "startOfDay", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js new file mode 100644 index 0000000000..ca1bd66664 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: > + Temporal.ZonedDateTime.prototype.startOfDay 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.ZonedDateTime.prototype.startOfDay(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.startOfDay), false, + "isConstructor(Temporal.ZonedDateTime.prototype.startOfDay)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js new file mode 100644 index 0000000000..d41ddce253 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js @@ -0,0 +1,69 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "call this.timeZone.getPossibleInstantsFor", +]; +const actual = []; + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime( + 1_000_000_000_000_000_000n, + TemporalHelpers.timeZoneObserver(actual, "this.timeZone"), + calendar, +); + +const fallBackTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(1800), -3600_000_000_000); +const fallBackInstance = new Temporal.ZonedDateTime( + 0n, + TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: fallBackTimeZone.getOffsetNanosecondsFor.bind(fallBackTimeZone), + getPossibleInstantsFor: fallBackTimeZone.getPossibleInstantsFor.bind(fallBackTimeZone), + }), + calendar, +); +const springForwardTimeZone = TemporalHelpers.oneShiftTimeZone(Temporal.Instant.fromEpochSeconds(-1800), 3600_000_000_000); +const springForwardInstance = new Temporal.ZonedDateTime( + 0n, + TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: springForwardTimeZone.getOffsetNanosecondsFor.bind(springForwardTimeZone), + getPossibleInstantsFor: springForwardTimeZone.getPossibleInstantsFor.bind(springForwardTimeZone), + }), + calendar, +); +// clear any observable operations that happen due to time zone or calendar +// calls in the constructors +actual.splice(0); + +instance.startOfDay(); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +fallBackInstance.startOfDay(); +assert.compareArray(actual, expected, "order of operations with preceding midnight at repeated wall-clock time"); +actual.splice(0); // clear + +springForwardInstance.startOfDay(); +assert.compareArray(actual, expected.concat([ + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations with preceding midnight at skipped wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js new file mode 100644 index 0000000000..4a674d1619 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: The "startOfDay" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.startOfDay, + "function", + "`typeof ZonedDateTime.prototype.startOfDay` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "startOfDay", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js new file mode 100644 index 0000000000..b4e0ebccfc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [88000_123_456_789n, "UTC"], + "startOfDay", + [], + (result) => { + assert.sameValue(result.epochNanoseconds, 86400_000_000_000n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 2, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 0, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..6dcfa91513 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.startOfDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..cce770ce1d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.startOfDay(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..6f9d7d472c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.startOfDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..494b7a1b70 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.startofday +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.startOfDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js new file mode 100644 index 0000000000..c8590dc610 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-max.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Maximum allowed duration +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const maxCases = [ + ["P273790Y8M11D", "string with max years"], + [{ years: 273790, months: 8, days: 11 }, "property bag with max years"], + ["P3285488M11D", "string with max months"], + [{ months: 3285488, days: 11 }, "property bag with max months"], + ["P14285714W2D", "string with max weeks"], + [{ weeks: 14285714, days: 2 }, "property bag with max weeks"], + ["P100000000D", "string with max days"], + [{ days: 100000000 }, "property bag with max days"], + ["PT2400000000H", "string with max hours"], + [{ hours: 2400000000 }, "property bag with max hours"], + ["PT144000000000M", "string with max minutes"], + [{ minutes: 144000000000 }, "property bag with max minutes"], + ["PT8640000000000S", "string with max seconds"], + [{ seconds: 8640000000000 }, "property bag with max seconds"], +]; + +for (const [arg, descr] of maxCases) { + const result = instance.subtract(arg); + assert.sameValue(result.epochNanoseconds, -8640000000000000000000n, `operation succeeds with ${descr}`); +} + +const minCases = [ + ["-P273790Y8M12D", "string with min years"], + [{ years: -273790, months: -8, days: -12 }, "property bag with min years"], + ["-P3285488M12D", "string with min months"], + [{ months: -3285488, days: -12 }, "property bag with min months"], + ["-P14285714W2D", "string with min weeks"], + [{ weeks: -14285714, days: -2 }, "property bag with min weeks"], + ["-P100000000D", "string with min days"], + [{ days: -100000000 }, "property bag with min days"], + ["-PT2400000000H", "string with min hours"], + [{ hours: -2400000000 }, "property bag with min hours"], + ["-PT144000000000M", "string with min minutes"], + [{ minutes: -144000000000 }, "property bag with min minutes"], + ["-PT8640000000000S", "string with min seconds"], + [{ seconds: -8640000000000 }, "property bag with min seconds"], +]; + +for (const [arg, descr] of minCases) { + const result = instance.subtract(arg); + assert.sameValue(result.epochNanoseconds, 8640000000000000000000n, `operation succeeds with ${descr}`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js new file mode 100644 index 0000000000..969ed8b5b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-duration-out-of-range.js @@ -0,0 +1,75 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Duration-like argument that is out of range +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const cases = [ + // 2^32 = 4294967296 + ["P4294967296Y", "string with years > max"], + [{ years: 4294967296 }, "property bag with years > max"], + ["-P4294967296Y", "string with years < min"], + [{ years: -4294967296 }, "property bag with years < min"], + ["P4294967296M", "string with months > max"], + [{ months: 4294967296 }, "property bag with months > max"], + ["-P4294967296M", "string with months < min"], + [{ months: -4294967296 }, "property bag with months < min"], + ["P4294967296W", "string with weeks > max"], + [{ weeks: 4294967296 }, "property bag with weeks > max"], + ["-P4294967296W", "string with weeks < min"], + [{ weeks: -4294967296 }, "property bag with weeks < min"], + + // ceil(max safe integer / 86400) = 104249991375 + ["P104249991375D", "string with days > max"], + [{ days: 104249991375 }, "property bag with days > max"], + ["P104249991374DT24H", "string where hours balance into days > max"], + [{ days: 104249991374, hours: 24 }, "property bag where hours balance into days > max"], + ["-P104249991375D", "string with days < min"], + [{ days: -104249991375 }, "property bag with days < min"], + ["-P104249991374DT24H", "string where hours balance into days < min"], + [{ days: -104249991374, hours: -24 }, "property bag where hours balance into days < min"], + + // ceil(max safe integer / 3600) = 2501999792984 + ["PT2501999792984H", "string with hours > max"], + [{ hours: 2501999792984 }, "property bag with hours > max"], + ["PT2501999792983H60M", "string where minutes balance into hours > max"], + [{ hours: 2501999792983, minutes: 60 }, "property bag where minutes balance into hours > max"], + ["-PT2501999792984H", "string with hours < min"], + [{ hours: -2501999792984 }, "property bag with hours < min"], + ["-PT2501999792983H60M", "string where minutes balance into hours < min"], + [{ hours: -2501999792983, minutes: -60 }, "property bag where minutes balance into hours < min"], + + // ceil(max safe integer / 60) = 150119987579017 + ["PT150119987579017M", "string with minutes > max"], + [{ minutes: 150119987579017 }, "property bag with minutes > max"], + ["PT150119987579016M60S", "string where seconds balance into minutes > max"], + [{ minutes: 150119987579016, seconds: 60 }, "property bag where seconds balance into minutes > max"], + ["-PT150119987579017M", "string with minutes < min"], + [{ minutes: -150119987579017 }, "property bag with minutes < min"], + ["-PT150119987579016M60S", "string where seconds balance into minutes < min"], + [{ minutes: -150119987579016, seconds: -60 }, "property bag where seconds balance into minutes < min"], + + // 2^53 = 9007199254740992 + ["PT9007199254740992S", "string with seconds > max"], + [{ seconds: 9007199254740992 }, "property bag with seconds > max"], + [{ seconds: 9007199254740991, milliseconds: 1000 }, "property bag where milliseconds balance into seconds > max"], + [{ seconds: 9007199254740991, microseconds: 1000000 }, "property bag where microseconds balance into seconds > max"], + [{ seconds: 9007199254740991, nanoseconds: 1000000000 }, "property bag where nanoseconds balance into seconds > max"], + ["-PT9007199254740992S", "string with seconds < min"], + [{ seconds: -9007199254740992 }, "property bag with seconds < min"], + [{ seconds: -9007199254740991, milliseconds: -1000 }, "property bag where milliseconds balance into seconds < min"], + [{ seconds: -9007199254740991, microseconds: -1000000 }, "property bag where microseconds balance into seconds < min"], + [{ seconds: -9007199254740991, nanoseconds: -1000000000 }, "property bag where nanoseconds balance into seconds < min"], +]; + +for (const [arg, descr] of cases) { + assert.throws(RangeError, () => instance.subtract(arg), `${descr} is out of range`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js new file mode 100644 index 0000000000..f09c025bef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +assert.throws( + TypeError, + () => instance.subtract({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js new file mode 100644 index 0000000000..87dc248624 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-mixed-sign.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +["constrain", "reject"].forEach((overflow) => { + assert.throws( + RangeError, + () => instance.subtract({ hours: 1, minutes: -30 }, { overflow }), + `mixed positive and negative values always throw (overflow = "${overflow}")` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js new file mode 100644 index 0000000000..0c8dc80e23 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-not-object.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Passing a primitive other than string to subtract() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +assert.throws(TypeError, () => instance.subtract(undefined), "undefined"); +assert.throws(TypeError, () => instance.subtract(null), "null"); +assert.throws(TypeError, () => instance.subtract(true), "boolean"); +assert.throws(RangeError, () => instance.subtract(""), "empty string"); +assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol"); +assert.throws(TypeError, () => instance.subtract(7), "number"); +assert.throws(TypeError, () => instance.subtract(7n), "bigint"); +assert.throws(TypeError, () => instance.subtract([]), "array"); +assert.throws(TypeError, () => instance.subtract(() => {}), "function"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js new file mode 100644 index 0000000000..416f794333 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-singular-properties.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.subtract(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..88af40381c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Strings with fractional duration units are rounded with the correct rounding mode +features: [Temporal] +---*/ + +const epoch = new Temporal.ZonedDateTime(0n, "UTC"); + +assert.sameValue(epoch.subtract("PT1.03125H").epochNanoseconds, -3712_500_000_000n, + "positive fractional units rounded with correct rounding mode"); +assert.sameValue(epoch.subtract("-PT1.03125H").epochNanoseconds, 3712_500_000_000n, + "negative fractional units rounded with correct rounding mode"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..966cf660a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/argument-string-negative-fractional-units.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Strings with fractional duration units are treated with the correct sign +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const resultHours = instance.subtract("-PT24.567890123H"); +assert.sameValue(resultHours.epochNanoseconds, 1_000_088_444_404_442_800n, "negative fractional hours"); + +const resultMinutes = instance.subtract("-PT1440.567890123M"); +assert.sameValue(resultMinutes.epochNanoseconds, 1_000_086_434_073_407_380n, "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js new file mode 100644 index 0000000000..e7b0cb2cdd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const subtract = Temporal.ZonedDateTime.prototype.subtract; + +assert.sameValue(typeof subtract, "function"); + +const args = [new Temporal.Duration(5)]; + +assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => subtract.apply(null, args), "null"); +assert.throws(TypeError, () => subtract.apply(true, args), "true"); +assert.throws(TypeError, () => subtract.apply("", args), "empty string"); +assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => subtract.apply(1, args), "1"); +assert.throws(TypeError, () => subtract.apply({}, args), "plain object"); +assert.throws(TypeError, () => subtract.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => subtract.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..5938e6eb78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateAddOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateAdd"); +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateAdd should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.subtract(new Temporal.Duration(1)); + +Object.defineProperty(Temporal.Calendar.prototype, "dateAdd", dateAddOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..fcca75fbfd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.subtract(new Temporal.Duration(1)); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js new file mode 100644 index 0000000000..f3c13cbee2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: > + Tests that Temporal.ZonedDateTime.prototype.subtract + 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.ZonedDateTime.prototype.subtract), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.subtract), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.subtract), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.subtract.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js new file mode 100644 index 0000000000..5a97b28c19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/calendar-dateadd.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: ZonedDateTime.prototype.subtract should call dateAdd with the appropriate values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(plainDate, duration, options) { + ++calls; + TemporalHelpers.assertPlainDate(plainDate, 2020, 3, "M03", 14, "plainDate argument"); + TemporalHelpers.assertDuration(duration, 0, -10, 0, 0, 0, 0, 0, 0, 0, 0, "duration argument"); + assert.sameValue(typeof options, "object", "options argument: type"); + assert.sameValue(Object.getPrototypeOf(options), null, "options argument: prototype"); + return super.dateAdd(plainDate, duration, options); + } +} + +const zonedDateTime = new Temporal.ZonedDateTime(1584189296_987654321n, + new Temporal.TimeZone("UTC"), new CustomCalendar()); +const result = zonedDateTime.subtract({ months: 10, hours: 14 }); +assert.sameValue(result.epochNanoseconds, 1557786896_987654321n); +assert.sameValue(calls, 1, "should have called dateAdd"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js new file mode 100644 index 0000000000..53d8cf9f8a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Temporal.ZonedDateTime.prototype.subtract.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.ZonedDateTime.prototype.subtract, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js new file mode 100644 index 0000000000..999ed448fc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Temporal.ZonedDateTime.prototype.subtract.name is "subtract". +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.ZonedDateTime.prototype.subtract, "name", { + value: "subtract", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js new file mode 100644 index 0000000000..28f3f2bbf9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.subtract(new Temporal.Duration(0, 0, 0, 1)); +assert.sameValue(result.epochNanoseconds, -13936164_999_999_999n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..50f508601b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js new file mode 100644 index 0000000000..37bee46093 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: > + Temporal.ZonedDateTime.prototype.subtract 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.ZonedDateTime.prototype.subtract(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.subtract), false, + "isConstructor(Temporal.ZonedDateTime.prototype.subtract)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js new file mode 100644 index 0000000000..432b9879d2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-object.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.subtract({ years: 1 }, {}); +assert.sameValue( + result1.epochNanoseconds, -31536000000000000n, "UTC", + "options may be an empty plain object" +); + +const result2 = instance.subtract({ years: 1 }, () => {}); +assert.sameValue( + result2.epochNanoseconds, -31536000000000000n, "UTC", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js new file mode 100644 index 0000000000..7fd18df8fe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-undefined.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(954506096_987_654_321n, "UTC"); +const duration = { months: 1 }; + +const explicit = datetime.subtract(duration, undefined); +assert.sameValue(explicit.month, 2, "default overflow is constrain"); +assert.sameValue(explicit.day, 29, "default overflow is constrain"); + +const implicit = datetime.subtract(duration); +assert.sameValue(implicit.month, 2, "default overflow is constrain"); +assert.sameValue(implicit.day, 29, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js new file mode 100644 index 0000000000..42e2ebe02d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.subtract({ years: 1 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js new file mode 100644 index 0000000000..84d7fb7a24 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js @@ -0,0 +1,84 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Properties on objects passed to subtract() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDurationRecord + "get duration.days", + "get duration.days.valueOf", + "call duration.days.valueOf", + "get duration.hours", + "get duration.hours.valueOf", + "call duration.hours.valueOf", + "get duration.microseconds", + "get duration.microseconds.valueOf", + "call duration.microseconds.valueOf", + "get duration.milliseconds", + "get duration.milliseconds.valueOf", + "call duration.milliseconds.valueOf", + "get duration.minutes", + "get duration.minutes.valueOf", + "call duration.minutes.valueOf", + "get duration.months", + "get duration.months.valueOf", + "call duration.months.valueOf", + "get duration.nanoseconds", + "get duration.nanoseconds.valueOf", + "call duration.nanoseconds.valueOf", + "get duration.seconds", + "get duration.seconds.valueOf", + "call duration.seconds.valueOf", + "get duration.weeks", + "get duration.weeks.valueOf", + "call duration.weeks.valueOf", + "get duration.years", + "get duration.years.valueOf", + "call duration.years.valueOf", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + // AddZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + "call this.calendar.dateAdd", + // ... inside Calendar.p.dateAdd + "get options.overflow", + "get options.overflow.toString", + "call options.overflow.toString", + // AddZonedDateTime again + "call this.timeZone.getPossibleInstantsFor", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +const duration = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "duration"); + +const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constrain" }, "options"); + +instance.subtract(duration, options); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js new file mode 100644 index 0000000000..e1ffb534cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-invalid-string.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: RangeError thrown when overflow option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.subtract step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => datetime.subtract(duration, { overflow }), + `invalid overflow ("${overflow}")` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js new file mode 100644 index 0000000000..faf4baa15f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Fallback value for overflow option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.subtract step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-1n, "UTC"); +const duration = new Temporal.Duration(0, 1); + +const explicit = datetime.subtract(duration, { overflow: undefined }); +assert.sameValue(explicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain"); +const implicit = datetime.subtract(duration, {}); +assert.sameValue(implicit.epochNanoseconds, -2678400_000_000_001n, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js new file mode 100644 index 0000000000..93e0c376f9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/overflow-wrong-type.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Type conversions for overflow option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal.calendar.prototype.dateadd step 7: + 7. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal-addzoneddatetime step 6: + 6. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). + sec-temporal.zoneddatetime.prototype.subtract step 7: + 7. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, −_duration_.[[Years]], −_duration_.[[Months]], −_duration_.[[Weeks]], −_duration_.[[Days]], −_duration_.[[Hours]], −_duration_.[[Minutes]], −_duration_.[[Seconds]], −_duration_.[[Milliseconds]], −_duration_.[[Microseconds]], −_duration_.[[Nanoseconds]], _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); +TemporalHelpers.checkStringOptionWrongType("overflow", "constrain", + (overflow) => datetime.subtract(duration, { overflow }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 999_913_600_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js new file mode 100644 index 0000000000..b73eca66e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: The "subtract" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.subtract, + "function", + "`typeof ZonedDateTime.prototype.subtract` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "subtract", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js new file mode 100644 index 0000000000..5607f98d09 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "subtract", + [{ nanoseconds: 5 }], + (result) => { + assert.sameValue(result.epochNanoseconds, 5n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 5, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..2c20b22fd6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.subtract(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..f6db8e31fe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.subtract(duration), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..ebefefcd5e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.subtract(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..784eeddde0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/subtract/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.subtract +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.subtract(duration)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js new file mode 100644 index 0000000000..141652a2b6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.timezoneid +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const timeZoneId = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "timeZoneId").get; + +assert.sameValue(typeof timeZoneId, "function"); + +assert.throws(TypeError, () => timeZoneId.call(undefined), "undefined"); +assert.throws(TypeError, () => timeZoneId.call(null), "null"); +assert.throws(TypeError, () => timeZoneId.call(true), "true"); +assert.throws(TypeError, () => timeZoneId.call(""), "empty string"); +assert.throws(TypeError, () => timeZoneId.call(Symbol()), "symbol"); +assert.throws(TypeError, () => timeZoneId.call(1), "1"); +assert.throws(TypeError, () => timeZoneId.call({}), "plain object"); +assert.throws(TypeError, () => timeZoneId.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => timeZoneId.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..0dc8bd72b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.timezoneid +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.timeZoneId; + +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js new file mode 100644 index 0000000000..b21f020fd2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.timezoneid +description: The "timeZoneId" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "timeZoneId"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js new file mode 100644 index 0000000000..8604204bae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/timeZoneId/timezone-id-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone }); + assert.throws(TypeError, () => zdt.timeZoneId); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js new file mode 100644 index 0000000000..8840d75cda --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toinstant +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toInstant = Temporal.ZonedDateTime.prototype.toInstant; + +assert.sameValue(typeof toInstant, "function"); + +assert.throws(TypeError, () => toInstant.call(undefined), "undefined"); +assert.throws(TypeError, () => toInstant.call(null), "null"); +assert.throws(TypeError, () => toInstant.call(true), "true"); +assert.throws(TypeError, () => toInstant.call(""), "empty string"); +assert.throws(TypeError, () => toInstant.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toInstant.call(1), "1"); +assert.throws(TypeError, () => toInstant.call({}), "plain object"); +assert.throws(TypeError, () => toInstant.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toInstant.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js new file mode 100644 index 0000000000..ec7c1bcdbf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toinstant +description: > + Tests that Temporal.ZonedDateTime.prototype.toInstant + 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.ZonedDateTime.prototype.toInstant), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toInstant), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toInstant), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toInstant.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js new file mode 100644 index 0000000000..af0f57deaf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.zoneddatetime.prototype.toinstant +description: Temporal.ZonedDateTime.prototype.toInstant.length is 0 +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.ZonedDateTime.prototype.toInstant, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js new file mode 100644 index 0000000000..9de6d78822 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toinstant +description: Temporal.ZonedDateTime.prototype.toInstant.name is "toInstant". +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.ZonedDateTime.prototype.toInstant, "name", { + value: "toInstant", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js new file mode 100644 index 0000000000..cb8cabbef8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toinstant +description: > + Temporal.ZonedDateTime.prototype.toInstant 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.ZonedDateTime.prototype.toInstant(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toInstant), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toInstant)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js new file mode 100644 index 0000000000..795a6fea6b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toinstant +description: The "toInstant" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toInstant, + "function", + "`typeof ZonedDateTime.prototype.toInstant` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toInstant", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toInstant/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js new file mode 100644 index 0000000000..8496ba4b0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/balance-negative-time-units.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-temporal-temporalzoneddatetimetostring step 9: + 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_). + sec-get-temporal.zoneddatetime.prototype.tojson step 3: + 3. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, *"auto"*, *"auto"*, *"auto"*, *"auto"*). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// ZonedDateTime +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +const jsonString = datetime.toJSON(); + +assert.sameValue(jsonString, "1970-01-01T00:00:00.000000999+00:00[-00:00:00.000000002]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js new file mode 100644 index 0000000000..a82fcfa23f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/basic.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Basic behavior for toJSON +features: [BigInt, Temporal] +---*/ + +const tests = [ + [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"], + [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"], +]; + +const options = new Proxy({}, { + get() { throw new Test262Error("should not get properties off argument") } +}); +for (const [datetime, expected] of tests) { + assert.sameValue(datetime.toJSON(), expected, "toJSON without argument"); + assert.sameValue(datetime.toJSON(options), expected, "toJSON with argument"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js new file mode 100644 index 0000000000..6be0b98e47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toJSON = Temporal.ZonedDateTime.prototype.toJSON; + +assert.sameValue(typeof toJSON, "function"); + +assert.throws(TypeError, () => toJSON.call(undefined), "undefined"); +assert.throws(TypeError, () => toJSON.call(null), "null"); +assert.throws(TypeError, () => toJSON.call(true), "true"); +assert.throws(TypeError, () => toJSON.call(""), "empty string"); +assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toJSON.call(1), "1"); +assert.throws(TypeError, () => toJSON.call({}), "plain object"); +assert.throws(TypeError, () => toJSON.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toJSON.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..0ce3ae9c34 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toJSON(); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..bbab91de0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toJSON(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js new file mode 100644 index 0000000000..1cc3e874df --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: > + Tests that Temporal.ZonedDateTime.prototype.toJSON + 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.ZonedDateTime.prototype.toJSON), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toJSON), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toJSON), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toJSON.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js new file mode 100644 index 0000000000..9d6f0cedee --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Temporal.ZonedDateTime.prototype.toJSON.length is 0 +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.ZonedDateTime.prototype.toJSON, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js new file mode 100644 index 0000000000..0f9a9dad74 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Temporal.ZonedDateTime.prototype.toJSON.name is "toJSON". +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.ZonedDateTime.prototype.toJSON, "name", { + value: "toJSON", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js new file mode 100644 index 0000000000..57f7d0b3a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.toJSON(); +assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js new file mode 100644 index 0000000000..b57ee43d71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: > + Temporal.ZonedDateTime.prototype.toJSON 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.ZonedDateTime.prototype.toJSON(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toJSON), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toJSON)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js new file mode 100644 index 0000000000..abe92bea4c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/offset.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: The time zone offset part of the string serialization +features: [BigInt, Temporal] +---*/ + +function test(timeZoneIdentifier, expected, description) { + const timeZone = new Temporal.TimeZone(timeZoneIdentifier); + const datetime = new Temporal.ZonedDateTime(0n, timeZone); + assert.sameValue(datetime.toJSON(), expected, description); +} + +test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00"); +test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset"); +test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js new file mode 100644 index 0000000000..516bf5103d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/order-of-operations.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Properties on objects passed to toJSON() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.id", + "get this.calendar.id", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +instance.toJSON(); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js new file mode 100644 index 0000000000..0f060aa215 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: The "toJSON" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toJSON, + "function", + "`typeof ZonedDateTime.prototype.toJSON` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toJSON", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ab9d955c51 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toJSON()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..e4ee22087d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toJSON(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..011224be4c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toJSON()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..62c5cf286a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toJSON()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js new file mode 100644 index 0000000000..8cbcf29f0d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/timezone-id-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone }); + assert.throws(TypeError, () => zdt.toJSON()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js new file mode 100644 index 0000000000..571a9bc74d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toJSON/year-format.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tojson +description: Verify that the year is appropriately formatted as 4 or 6 digits +features: [Temporal] +---*/ + +function epochNsInYear(year) { + // Return an epoch nanoseconds value near the middle of the given year + const avgNsPerYear = 31_556_952_000_000_000n; + return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n); +} + +const utc = new Temporal.TimeZone("UTC"); + +let instance = new Temporal.ZonedDateTime(epochNsInYear(-100000n), utc); +assert.sameValue(instance.toJSON(), "-100000-07-01T21:30:36+00:00[UTC]", "large negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-10000n), utc); +assert.sameValue(instance.toJSON(), "-010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-9999n), utc); +assert.sameValue(instance.toJSON(), "-009999-07-02T03:19:48+00:00[UTC]", "largest 4-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-1000n), utc); +assert.sameValue(instance.toJSON(), "-001000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-999n), utc); +assert.sameValue(instance.toJSON(), "-000999-07-02T15:19:48+00:00[UTC]", "largest 3-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-1n), utc); +assert.sameValue(instance.toJSON(), "-000001-07-02T15:41:24+00:00[UTC]", "year -1 formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(0n), utc); +assert.sameValue(instance.toJSON(), "0000-07-01T21:30:36+00:00[UTC]", "year 0 formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(1n), utc); +assert.sameValue(instance.toJSON(), "0001-07-02T03:19:48+00:00[UTC]", "year 1 formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(999n), utc); +assert.sameValue(instance.toJSON(), "0999-07-02T03:41:24+00:00[UTC]", "largest 3-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(1000n), utc); +assert.sameValue(instance.toJSON(), "1000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(9999n), utc); +assert.sameValue(instance.toJSON(), "9999-07-02T15:41:24+00:00[UTC]", "largest 4-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(10000n), utc); +assert.sameValue(instance.toJSON(), "+010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit positive year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(100000n), utc); +assert.sameValue(instance.toJSON(), "+100000-07-01T21:30:36+00:00[UTC]", "large positive year formatted as 6-digit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js new file mode 100644 index 0000000000..a59ba2cebb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toLocaleString = Temporal.ZonedDateTime.prototype.toLocaleString; + +assert.sameValue(typeof toLocaleString, "function"); + +assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined"); +assert.throws(TypeError, () => toLocaleString.call(null), "null"); +assert.throws(TypeError, () => toLocaleString.call(true), "true"); +assert.throws(TypeError, () => toLocaleString.call(""), "empty string"); +assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toLocaleString.call(1), "1"); +assert.throws(TypeError, () => toLocaleString.call({}), "plain object"); +assert.throws(TypeError, () => toLocaleString.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toLocaleString.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..cbb699c021 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toLocaleString(); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..181c111869 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toLocaleString(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js new file mode 100644 index 0000000000..2b73b77098 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: > + Tests that Temporal.ZonedDateTime.prototype.toLocaleString + 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.ZonedDateTime.prototype.toLocaleString), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toLocaleString), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toLocaleString), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toLocaleString.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js new file mode 100644 index 0000000000..24b9b3b9e1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Temporal.ZonedDateTime.prototype.toLocaleString.length is 0 +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.ZonedDateTime.prototype.toLocaleString, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js new file mode 100644 index 0000000000..677c3c6c87 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: Temporal.ZonedDateTime.prototype.toLocaleString.name is "toLocaleString". +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.ZonedDateTime.prototype.toLocaleString, "name", { + value: "toLocaleString", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js new file mode 100644 index 0000000000..f22770d115 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: > + Temporal.ZonedDateTime.prototype.toLocaleString 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.ZonedDateTime.prototype.toLocaleString(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toLocaleString)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js new file mode 100644 index 0000000000..b35f9f7fb9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: The "toLocaleString" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toLocaleString, + "function", + "`typeof ZonedDateTime.prototype.toLocaleString` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toLocaleString", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js new file mode 100644 index 0000000000..b22c79caea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/return-string.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Kate Miháliková. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tolocalestring +description: > + toLocaleString return a string. +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC"); + +assert.sameValue(typeof datetime.toLocaleString("en", { dateStyle: "short" }), "string"); +assert.sameValue(typeof datetime.toLocaleString("en", { timeStyle: "short" }), "string"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js new file mode 100644 index 0000000000..002de3c45f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toLocaleString/timezone-id-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone }); + assert.throws(TypeError, () => zdt.toLocaleString()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js new file mode 100644 index 0000000000..fa9699b616 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toPlainDate = Temporal.ZonedDateTime.prototype.toPlainDate; + +assert.sameValue(typeof toPlainDate, "function"); + +assert.throws(TypeError, () => toPlainDate.call(undefined), "undefined"); +assert.throws(TypeError, () => toPlainDate.call(null), "null"); +assert.throws(TypeError, () => toPlainDate.call(true), "true"); +assert.throws(TypeError, () => toPlainDate.call(""), "empty string"); +assert.throws(TypeError, () => toPlainDate.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toPlainDate.call(1), "1"); +assert.throws(TypeError, () => toPlainDate.call({}), "plain object"); +assert.throws(TypeError, () => toPlainDate.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toPlainDate.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..ab2f148bcb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainDate(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js new file mode 100644 index 0000000000..f4b3991392 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: > + Tests that Temporal.ZonedDateTime.prototype.toPlainDate + 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.ZonedDateTime.prototype.toPlainDate), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDate), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDate), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDate.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js new file mode 100644 index 0000000000..8a2ebcdb12 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: Temporal.ZonedDateTime.prototype.toPlainDate.length is 0 +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.ZonedDateTime.prototype.toPlainDate, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js new file mode 100644 index 0000000000..1aac1481e8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: Temporal.ZonedDateTime.prototype.toPlainDate.name is "toPlainDate". +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.ZonedDateTime.prototype.toPlainDate, "name", { + value: "toPlainDate", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js new file mode 100644 index 0000000000..4a53da5fba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: > + Temporal.ZonedDateTime.prototype.toPlainDate 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.ZonedDateTime.prototype.toPlainDate(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDate)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js new file mode 100644 index 0000000000..5c55b55f75 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: The "toPlainDate" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toPlainDate, + "function", + "`typeof ZonedDateTime.prototype.toPlainDate` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDate", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..c4fc81e5a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainDate()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..94afa2426c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toPlainDate(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..fbf216ad68 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainDate()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..6e9b2f1490 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindate +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toPlainDate()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js new file mode 100644 index 0000000000..7c495c14f6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/balance-negative-time-units.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.toplaindatetime step 5: + 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +const pdt = datetime.toPlainDateTime(); + +TemporalHelpers.assertPlainDateTime(pdt, 1970, 1, "M01", 1, 0, 0, 0, 0, 0, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js new file mode 100644 index 0000000000..cb5656f538 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.zoneddatetime.prototype.toplaindatetime +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toPlainDateTime = Temporal.ZonedDateTime.prototype.toPlainDateTime; + +assert.sameValue(typeof toPlainDateTime, "function"); + +assert.throws(TypeError, () => toPlainDateTime.call(undefined), "undefined"); +assert.throws(TypeError, () => toPlainDateTime.call(null), "null"); +assert.throws(TypeError, () => toPlainDateTime.call(true), "true"); +assert.throws(TypeError, () => toPlainDateTime.call(""), "empty string"); +assert.throws(TypeError, () => toPlainDateTime.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toPlainDateTime.call(1), "1"); +assert.throws(TypeError, () => toPlainDateTime.call({}), "plain object"); +assert.throws(TypeError, () => toPlainDateTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toPlainDateTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..207864ee22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainDateTime(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js new file mode 100644 index 0000000000..aaf8d295ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: > + Tests that Temporal.ZonedDateTime.prototype.toPlainDateTime + 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.ZonedDateTime.prototype.toPlainDateTime), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainDateTime), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainDateTime), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainDateTime.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js new file mode 100644 index 0000000000..0bff20504a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.zoneddatetime.prototype.toplaindatetime +description: Temporal.ZonedDateTime.prototype.toPlainDateTime.length is 0 +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.ZonedDateTime.prototype.toPlainDateTime, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js new file mode 100644 index 0000000000..697d404dcd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: Temporal.ZonedDateTime.prototype.toPlainDateTime.name is "toPlainDateTime". +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.ZonedDateTime.prototype.toPlainDateTime, "name", { + value: "toPlainDateTime", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js new file mode 100644 index 0000000000..8f62f95941 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/negative-epochnanoseconds.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.toPlainDateTime(); +TemporalHelpers.assertPlainDateTime(result, 1969, 7, "M07", 24, 16, 50, 35, 0, 0, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js new file mode 100644 index 0000000000..7a3d9fd77a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: > + Temporal.ZonedDateTime.prototype.toPlainDateTime 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.ZonedDateTime.prototype.toPlainDateTime(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toPlainDateTime)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js new file mode 100644 index 0000000000..7bd3d23b3d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/plain-custom-timezone.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.zoneddatetime.prototype.toplaindatetime +description: timeZone.getOffsetNanosecondsFor() called +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", +]; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: -8735135802468, +}); + +const zdt = new Temporal.ZonedDateTime(160583136123456789n, timeZone); +const dateTime = Temporal.PlainDateTime.from("1975-02-02T12:00:00.987654321"); +const result = zdt.toPlainDateTime(); +for (const property of ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"]) { + assert.sameValue(result[property], dateTime[property], property); +} + +assert.compareArray(actual, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js new file mode 100644 index 0000000000..6d97b7e478 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// 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.zoneddatetime.prototype.toplaindatetime +description: The "toPlainDateTime" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toPlainDateTime, + "function", + "`typeof ZonedDateTime.prototype.toPlainDateTime` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainDateTime", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..8d3578959b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainDateTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..78a93f2c87 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toPlainDateTime(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..cd6d086688 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainDateTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..4f65674b9d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainDateTime/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaindatetime +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toPlainDateTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js new file mode 100644 index 0000000000..d1b8111099 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toPlainMonthDay = Temporal.ZonedDateTime.prototype.toPlainMonthDay; + +assert.sameValue(typeof toPlainMonthDay, "function"); + +assert.throws(TypeError, () => toPlainMonthDay.call(undefined), "undefined"); +assert.throws(TypeError, () => toPlainMonthDay.call(null), "null"); +assert.throws(TypeError, () => toPlainMonthDay.call(true), "true"); +assert.throws(TypeError, () => toPlainMonthDay.call(""), "empty string"); +assert.throws(TypeError, () => toPlainMonthDay.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toPlainMonthDay.call(1), "1"); +assert.throws(TypeError, () => toPlainMonthDay.call({}), "plain object"); +assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toPlainMonthDay.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..dff21b9190 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainMonthDay(); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..aa31f54010 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-calendar-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields"); +Object.defineProperty(Temporal.Calendar.prototype, "fields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("fields should not be looked up"); + }, +}); +const monthDayFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "monthDayFromFields"); +Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("monthDayFromFields should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainMonthDay(); + +Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal); +Object.defineProperty(Temporal.Calendar.prototype, "monthDayFromFields", monthDayFromFieldsOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..5c2f01374b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainMonthDay(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js new file mode 100644 index 0000000000..de85dc4996 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Tests that Temporal.ZonedDateTime.prototype.toPlainMonthDay + 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.ZonedDateTime.prototype.toPlainMonthDay), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainMonthDay), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainMonthDay), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainMonthDay.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js new file mode 100644 index 0000000000..4b088c3520 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-arguments.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: Correct options value is passed to calendar method +info: | + MonthDayFromFields ( calendar, fields [ , options ] ) + + 3. If options is not present, then + a. Set options to undefined. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + monthDayFromFields(...args) { + assert.sameValue(args.length, 2, "args.length"); + assert.sameValue(typeof args[0], "object", "args[0]"); + assert.sameValue(args[1], undefined, "args[1]"); + return super.monthDayFromFields(...args); + } +} +const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar()); +const result = zonedDateTime.toPlainMonthDay(); +TemporalHelpers.assertPlainMonthDay(result, "M05", 2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js new file mode 100644 index 0000000000..c23e255c14 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fields-iterable.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.toplainmonthday step 7: + 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"monthCode"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "monthCode", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.toPlainMonthDay(); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..3a5a0d57a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-fromfields-called-with-null-prototype-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Calendar.monthDayFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +instance.toPlainMonthDay(); +assert.sameValue(calendar.monthDayFromFieldsCallCount, 1, "monthDayFromFields should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js new file mode 100644 index 0000000000..d124b0e2f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-monthdayfromfields-called-with-options-undefined.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Calendar.monthDayFromFields method is called with undefined as the options + value when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +instance.toPlainMonthDay(); +assert.sameValue(calendar.monthDayFromFieldsCallCount, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js new file mode 100644 index 0000000000..45fdb31d48 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/calendar-result.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: TypeError thrown when calendar method returns an object with the wrong brand +info: | + MonthDayFromFields ( calendar, fields, options ) + + 4. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]). +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + monthDayFromFields() { + return {}; + } +} +const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar()); +assert.throws(TypeError, () => zonedDateTime.toPlainMonthDay()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..eb95558c12 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/constructor-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js new file mode 100644 index 0000000000..10110794a3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/duplicate-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + + assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay()); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js new file mode 100644 index 0000000000..93fdd16c7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.length is 0 +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.ZonedDateTime.prototype.toPlainMonthDay, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js new file mode 100644 index 0000000000..dd52a2400d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: Temporal.ZonedDateTime.prototype.toPlainMonthDay.name is "toPlainMonthDay". +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.ZonedDateTime.prototype.toPlainMonthDay, "name", { + value: "toPlainMonthDay", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js new file mode 100644 index 0000000000..3ce42e7be1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + Temporal.ZonedDateTime.prototype.toPlainMonthDay 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.ZonedDateTime.prototype.toPlainMonthDay(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toPlainMonthDay)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js new file mode 100644 index 0000000000..b09f37d7a6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/order-of-operations.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: > + User-observable time zone and calendar accesses and calls in + toPlainMonthDay() happen the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get this.calendar.fields", + "get this.calendar.monthDayFromFields", + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.calendar.fields", + "get this.calendar.day", + "call this.calendar.day", + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "call this.calendar.monthDayFromFields", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +Object.defineProperties(instance, { + day: { + get() { + actual.push("get this.day"); + return TemporalHelpers.toPrimitiveObserver(actual, 1, "this.day"); + } + }, + monthCode: { + get() { + actual.push("get this.monthCode"); + return TemporalHelpers.toPrimitiveObserver(actual, "M01", "this.monthCode"); + } + }, +}); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +instance.toPlainMonthDay(); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js new file mode 100644 index 0000000000..eae409c8b4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: The "toPlainMonthDay" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toPlainMonthDay, + "function", + "`typeof ZonedDateTime.prototype.toPlainMonthDay` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainMonthDay", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js new file mode 100644 index 0000000000..37da40afa8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/proto-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.toPlainMonthDay()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..047e4fee38 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainMonthDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..85b9809e84 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toPlainMonthDay(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..ee65dbb110 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainMonthDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..a958952db6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainMonthDay/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainmonthday +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toPlainMonthDay()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js new file mode 100644 index 0000000000..3cff09ad52 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/balance-negative-time-units.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-get-temporal.zoneddatetime.prototype.toplaintime step 5: + 5. Let _temporalDateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _zonedDateTime_.[[Calendar]]). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +const time = datetime.toPlainTime(); + +TemporalHelpers.assertPlainTime(time, 0, 0, 0, 0, 0, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js new file mode 100644 index 0000000000..6f719cd2ad --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toPlainTime = Temporal.ZonedDateTime.prototype.toPlainTime; + +assert.sameValue(typeof toPlainTime, "function"); + +assert.throws(TypeError, () => toPlainTime.call(undefined), "undefined"); +assert.throws(TypeError, () => toPlainTime.call(null), "null"); +assert.throws(TypeError, () => toPlainTime.call(true), "true"); +assert.throws(TypeError, () => toPlainTime.call(""), "empty string"); +assert.throws(TypeError, () => toPlainTime.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toPlainTime.call(1), "1"); +assert.throws(TypeError, () => toPlainTime.call({}), "plain object"); +assert.throws(TypeError, () => toPlainTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toPlainTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..e34b4197f7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainTime(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js new file mode 100644 index 0000000000..6899a40700 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: > + Tests that Temporal.ZonedDateTime.prototype.toPlainTime + 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.ZonedDateTime.prototype.toPlainTime), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainTime), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainTime), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainTime.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js new file mode 100644 index 0000000000..e74970d6af --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: Temporal.ZonedDateTime.prototype.toPlainTime.length is 0 +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.ZonedDateTime.prototype.toPlainTime, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js new file mode 100644 index 0000000000..64b8e9422d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: Temporal.ZonedDateTime.prototype.toPlainTime.name is "toPlainTime". +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.ZonedDateTime.prototype.toPlainTime, "name", { + value: "toPlainTime", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js new file mode 100644 index 0000000000..726ec7c3d1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/negative-epochnanoseconds.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.toPlainTime(); +TemporalHelpers.assertPlainTime(result, 16, 50, 35, 0, 0, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js new file mode 100644 index 0000000000..9d4cddde8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: > + Temporal.ZonedDateTime.prototype.toPlainTime 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.ZonedDateTime.prototype.toPlainTime(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toPlainTime)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js new file mode 100644 index 0000000000..970a345c05 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: The "toPlainTime" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toPlainTime, + "function", + "`typeof ZonedDateTime.prototype.toPlainTime` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainTime", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..f16bb9deb7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..d4270af22d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toPlainTime(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..e4313799c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..6f8a84f651 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplaintime +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toPlainTime()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js new file mode 100644 index 0000000000..0e03bffc9c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toPlainYearMonth = Temporal.ZonedDateTime.prototype.toPlainYearMonth; + +assert.sameValue(typeof toPlainYearMonth, "function"); + +assert.throws(TypeError, () => toPlainYearMonth.call(undefined), "undefined"); +assert.throws(TypeError, () => toPlainYearMonth.call(null), "null"); +assert.throws(TypeError, () => toPlainYearMonth.call(true), "true"); +assert.throws(TypeError, () => toPlainYearMonth.call(""), "empty string"); +assert.throws(TypeError, () => toPlainYearMonth.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toPlainYearMonth.call(1), "1"); +assert.throws(TypeError, () => toPlainYearMonth.call({}), "plain object"); +assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toPlainYearMonth.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..7ed8f1b128 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainYearMonth(); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..50b48cca02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-calendar-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields"); +Object.defineProperty(Temporal.Calendar.prototype, "fields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("fields should not be looked up"); + }, +}); +const yearMonthFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearMonthFromFields"); +Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("yearMonthFromFields should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainYearMonth(); + +Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal); +Object.defineProperty(Temporal.Calendar.prototype, "yearMonthFromFields", yearMonthFromFieldsOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..e2e1f07886 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toPlainYearMonth(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js new file mode 100644 index 0000000000..0e3f0b1e41 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Tests that Temporal.ZonedDateTime.prototype.toPlainYearMonth + 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.ZonedDateTime.prototype.toPlainYearMonth), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toPlainYearMonth), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toPlainYearMonth), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toPlainYearMonth.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js new file mode 100644 index 0000000000..9a492cf44b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-arguments.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: Correct options value is passed to calendar method +info: | + YearMonthFromFields ( calendar, fields [ , options ] ) + + 3. If options is not present, then + a. Set options to undefined. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + yearMonthFromFields(...args) { + assert.sameValue(args.length, 2, "args.length"); + assert.sameValue(typeof args[0], "object", "args[0]"); + assert.sameValue(args[1], undefined, "args[1]"); + return super.yearMonthFromFields(...args); + } +} +const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar()); +const result = zonedDateTime.toPlainYearMonth(); +TemporalHelpers.assertPlainYearMonth(result, 2000, 5, "M05"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js new file mode 100644 index 0000000000..a1acc85bf5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fields-iterable.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.toplainyearmonth step 7: + 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "monthCode", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.toPlainYearMonth(); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..19fd8176cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-fromfields-called-with-null-prototype-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Calendar.yearMonthFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +instance.toPlainYearMonth(); +assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1, "yearMonthFromFields should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js new file mode 100644 index 0000000000..eae729859d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-result.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: TypeError thrown when calendar method returns an object with the wrong brand +info: | + YearMonthFromFields ( calendar, fields [ , options ] ) + + 4. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]). +features: [Temporal] +---*/ + +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + yearMonthFromFields() { + return {}; + } +} +const zonedDateTime = new Temporal.ZonedDateTime(957270896123456789n, "UTC", new CustomCalendar()); +assert.throws(TypeError, () => zonedDateTime.toPlainYearMonth()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js new file mode 100644 index 0000000000..4b0aba2901 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/calendar-yearmonthfromfields-called-with-options-undefined.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Calendar.yearMonthFromFields method is called with undefined as the options + value when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +instance.toPlainYearMonth(); +assert.sameValue(calendar.yearMonthFromFieldsCallCount, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..5d6e709f1e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/constructor-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js new file mode 100644 index 0000000000..0314561a11 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/duplicate-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['year']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + + assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth()); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js new file mode 100644 index 0000000000..29809a71ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.length is 0 +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.ZonedDateTime.prototype.toPlainYearMonth, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js new file mode 100644 index 0000000000..a40c2d3a18 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: Temporal.ZonedDateTime.prototype.toPlainYearMonth.name is "toPlainYearMonth". +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.ZonedDateTime.prototype.toPlainYearMonth, "name", { + value: "toPlainYearMonth", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js new file mode 100644 index 0000000000..fa4bb26929 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + Temporal.ZonedDateTime.prototype.toPlainYearMonth 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.ZonedDateTime.prototype.toPlainYearMonth(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toPlainYearMonth)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js new file mode 100644 index 0000000000..7ff72c5f8a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/order-of-operations.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: > + User-observable time zone and calendar accesses and calls in + toPlainYearMonth() happen the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get this.calendar.fields", + "get this.calendar.yearMonthFromFields", + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.calendar.fields", + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + "call this.calendar.yearMonthFromFields", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +Object.defineProperties(instance, { + monthCode: { + get() { + actual.push("get this.monthCode"); + return TemporalHelpers.toPrimitiveObserver(actual, "M01", "this.monthCode"); + } + }, + year: { + get() { + actual.push("get this.year"); + return TemporalHelpers.toPrimitiveObserver(actual, 1970, "this.year"); + } + }, +}); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +instance.toPlainYearMonth(); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js new file mode 100644 index 0000000000..cba0686500 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: The "toPlainYearMonth" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toPlainYearMonth, + "function", + "`typeof ZonedDateTime.prototype.toPlainYearMonth` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toPlainYearMonth", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js new file mode 100644 index 0000000000..55c00cc95f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/proto-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.toPlainYearMonth()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..9d207815b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainYearMonth()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..0cdd95f305 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toPlainYearMonth(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..98681b1c71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toPlainYearMonth()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..45ab56c39d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toPlainYearMonth/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.toplainyearmonth +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toPlainYearMonth()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js new file mode 100644 index 0000000000..dd736305e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/balance-negative-time-units.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-temporal-temporalzoneddatetimetostring step 9: + 9. Let _dateTime_ be ? BuiltinTimeZoneGetPlainDateTimeFor(_timeZone_, _instant_, _isoCalendar_). + sec-get-temporal.zoneddatetime.prototype.tostring step 9: + 9. Return ? TemporalZonedDateTimeToString(_zonedDateTime_, _precision_.[[Precision]], _showCalendar_, _showTimeZone_, _showOffset_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// ZonedDateTime +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(1001n, tz); + +const isoString = datetime.toString(); + +assert.sameValue(isoString, "1970-01-01T00:00:00.000000999+00:00[-00:00:00.000000002]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js new file mode 100644 index 0000000000..79580e4591 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toString = Temporal.ZonedDateTime.prototype.toString; + +assert.sameValue(typeof toString, "function"); + +assert.throws(TypeError, () => toString.call(undefined), "undefined"); +assert.throws(TypeError, () => toString.call(null), "null"); +assert.throws(TypeError, () => toString.call(true), "true"); +assert.throws(TypeError, () => toString.call(""), "empty string"); +assert.throws(TypeError, () => toString.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toString.call(1), "1"); +assert.throws(TypeError, () => toString.call({}), "plain object"); +assert.throws(TypeError, () => toString.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => toString.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..f04b7be82c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toString(); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..eab024a413 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.toString(); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js new file mode 100644 index 0000000000..a67286e337 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + Tests that Temporal.ZonedDateTime.prototype.toString + 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.ZonedDateTime.prototype.toString), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.toString), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.toString), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.toString.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js new file mode 100644 index 0000000000..da39782390 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendar-tostring.js @@ -0,0 +1,56 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Number of observable 'toString' calls on the calendar for each value of calendarName +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls; +const customCalendar = { + get id() { + ++calls; + return "custom"; + }, + toString() { + TemporalHelpers.assertUnreachable('toString should not be called'); + }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +const date = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", customCalendar); +[ + ["always", "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1], + ["auto", "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1], + ["critical", "2001-09-09T01:46:40.987654321+00:00[UTC][!u-ca=custom]", 1], + ["never", "2001-09-09T01:46:40.987654321+00:00[UTC]", 0], + [undefined, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", 1], +].forEach(([calendarName, expectedResult, expectedCalls]) => { + calls = 0; + const result = date.toString({ calendarName }); + assert.sameValue(result, expectedResult, `id for calendarName = ${calendarName}`); + assert.sameValue(calls, expectedCalls, `calls to id getter for calendarName = ${calendarName}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js new file mode 100644 index 0000000000..64da01fe8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-always.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: If calendarName is "always", the calendar ID should be included. +features: [Temporal] +---*/ + +const calendarMethods = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +const tests = [ + [[], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=iso8601]", "built-in ISO"], + [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"], + [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=iso8601]", "custom with iso8601 id"], + [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"], + [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"], +]; + +for (const [args, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args); + const result = date.toString({ calendarName: "always" }); + assert.sameValue(result, expected, `${description} calendar for calendarName = always`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js new file mode 100644 index 0000000000..116a22818b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-auto.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: If calendarName is "auto", "iso8601" should be omitted. +features: [Temporal] +---*/ + +const calendarMethods = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +const tests = [ + [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"], + [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"], + [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"], + [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"], + [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"], +]; + +for (const [args, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args); + const result = date.toString({ calendarName: "auto" }); + assert.sameValue(result, expected, `${description} calendar for calendarName = auto`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js new file mode 100644 index 0000000000..7939d2152e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-critical.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + If calendarName is "calendar", the calendar ID should be included and prefixed + with "!". +features: [Temporal] +---*/ + +const calendarMethods = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +const tests = [ + [[], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=iso8601]", "built-in ISO"], + [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=custom]", "custom"], + [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=iso8601]", "custom with iso8601 id"], + [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=ISO8601]", "custom with caps id"], + [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][!u-ca=\u0131so8601]", "custom with dotless i id"], +]; + +for (const [args, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args); + const result = date.toString({ calendarName: "critical" }); + assert.sameValue(result, expected, `${description} calendar for calendarName = critical`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js new file mode 100644 index 0000000000..c352fb52d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-invalid-string.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: RangeError thrown when calendarName option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-toshowcalendaroption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 6: + 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const invalidValues = ["ALWAYS", "sometimes", "other string", "auto\0"]; + +for (const calendarName of invalidValues) { + assert.throws( + RangeError, + () => datetime.toString({ calendarName }), + `${calendarName} is an invalid value for calendarName option` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js new file mode 100644 index 0000000000..3180df0569 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-never.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: If calendarName is "never", the calendar ID should be omitted. +features: [Temporal] +---*/ + +const calendarMethods = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +const tests = [ + [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"], + [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom"], + [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"], + [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with caps id"], + [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with dotless i id"], +]; + +for (const [args, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args); + const result = date.toString({ calendarName: "never" }); + assert.sameValue(result, expected, `${description} calendar for calendarName = never`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js new file mode 100644 index 0000000000..d1ad1f6ac9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-undefined.js @@ -0,0 +1,56 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Fallback value for calendarName option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-toshowcalendaroption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 6: + 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_). +features: [Temporal] +---*/ + +const calendarMethods = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +const tests = [ + [[], "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in ISO"], + [[{ id: "custom", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=custom]", "custom"], + [[{ id: "iso8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC]", "custom with iso8601 id"], + [[{ id: "ISO8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=ISO8601]", "custom with caps id"], + [[{ id: "\u0131so8601", ...calendarMethods }], "1970-01-01T01:01:01.987654321+00:00[UTC][u-ca=\u0131so8601]", "custom with dotless i id"], +]; + +for (const [args, expected, description] of tests) { + const datetime = new Temporal.ZonedDateTime(3661_987_654_321n, "UTC", ...args); + const result = datetime.toString({ calendarName: undefined }); + assert.sameValue(result, expected, `default calendarName option is auto with ${description} calendar`); + // See options-object.js for {} and options-undefined.js for absent options arg +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js new file mode 100644 index 0000000000..5e4f67edf5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/calendarname-wrong-type.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Type conversions for calendarName option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-toshowcalendaroption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"calendarName"*, « *"string"* », « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 6: + 6. Let _showCalendar_ be ? ToShowCalendarOption(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = { + id: "custom", + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + +TemporalHelpers.checkStringOptionWrongType("calendarName", "auto", + (calendarName) => datetime.toString({ calendarName }), + (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC][u-ca=custom]", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js new file mode 100644 index 0000000000..303faec688 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-auto.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: auto value for fractionalSecondDigits option +features: [BigInt, Temporal] +---*/ + +const tests = [ + [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"], + [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"], +]; + +for (const [datetime, expected] of tests) { + assert.sameValue(datetime.toString(), expected, "default is to emit seconds and drop trailing zeroes"); + assert.sameValue(datetime.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js new file mode 100644 index 0000000000..4562775585 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-invalid-string.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values +info: | + sec-getstringornumberoption step 4: + 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC"); + +for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) { + assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits }), + `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js new file mode 100644 index 0000000000..66d886ee90 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-nan.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js new file mode 100644 index 0000000000..19064a373e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-non-integer.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Rounding for fractionalSecondDigits option +info: | + sec-getstringornumberoption step 3.b: + b. Return floor(ℝ(_value_)). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC"); + +let string = datetime.toString({ fractionalSecondDigits: 2.5 }); +assert.sameValue(string, "2001-09-09T01:46:40.98+00:00[UTC]", "fractionalSecondDigits 2.5 floors to 2"); + +string = datetime.toString({ fractionalSecondDigits: 9.7 }); +assert.sameValue(string, "2001-09-09T01:46:40.987650000+00:00[UTC]", "fractionalSecondDigits 9.7 floors to 9 and is not out of range"); + +assert.throws( + RangeError, + () => datetime.toString({ fractionalSecondDigits: -0.6 }), + "fractionalSecondDigits -0.6 floors to -1 and is out of range" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js new file mode 100644 index 0000000000..445b498ed2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-number.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Number for fractionalSecondDigits option +features: [BigInt, Temporal] +---*/ + +const fewSeconds = new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"); +const zeroSeconds = new Temporal.ZonedDateTime(0n, "UTC"); +const wholeSeconds = new Temporal.ZonedDateTime(30_000_000_000n, "UTC"); +const subSeconds = new Temporal.ZonedDateTime(30_123_400_000n, "UTC"); + +assert.sameValue(fewSeconds.toString({ fractionalSecondDigits: 0 }), "1976-02-04T05:03:01+00:00[UTC]", + "pads parts with 0"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "1970-01-01T00:00:30+00:00[UTC]", + "truncates 4 decimal places to 0"); +assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:00.00+00:00[UTC]", + "pads zero seconds to 2 decimal places"); +assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.00+00:00[UTC]", + "pads whole seconds to 2 decimal places"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "1970-01-01T00:00:30.12+00:00[UTC]", + "truncates 4 decimal places to 2"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "1970-01-01T00:00:30.123+00:00[UTC]", + "truncates 4 decimal places to 3"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "1970-01-01T00:00:30.123400+00:00[UTC]", + "pads 4 decimal places to 6"); +assert.sameValue(zeroSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:00.0000000+00:00[UTC]", + "pads zero seconds to 7 decimal places"); +assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.0000000+00:00[UTC]", + "pads whole seconds to 7 decimal places"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "1970-01-01T00:00:30.1234000+00:00[UTC]", + "pads 4 decimal places to 7"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "1970-01-01T00:00:30.123400000+00:00[UTC]", + "pads 4 decimal places to 9"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js new file mode 100644 index 0000000000..8b134d7b87 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-out-of-range.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option out of range +info: | + sec-getstringornumberoption step 3.a: + a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception. + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC"); + +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -Infinity }), + "−∞ is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: -1 }), + "−1 is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 10 }), + "10 is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: Infinity }), + "∞ is out of range for fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js new file mode 100644 index 0000000000..0f89571cab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-undefined.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Fallback value for fractionalSecondDigits option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const tests = [ + [new Temporal.ZonedDateTime(192_258_181_000_000_000n, "UTC"), "1976-02-04T05:03:01+00:00[UTC]"], + [new Temporal.ZonedDateTime(0n, "UTC"), "1970-01-01T00:00:00+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_000_000_000n, "UTC"), "1970-01-01T00:00:30+00:00[UTC]"], + [new Temporal.ZonedDateTime(30_123_400_000n, "UTC"), "1970-01-01T00:00:30.1234+00:00[UTC]"], +]; + +for (const [datetime, expected] of tests) { + const explicit = datetime.toString({ fractionalSecondDigits: undefined }); + assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)"); + + const implicit = datetime.toString({}); + assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)"); + + const lambda = datetime.toString(() => {}); + assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js new file mode 100644 index 0000000000..bedea182ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/fractionalseconddigits-wrong-type.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Type conversions for fractionalSecondDigits option +info: | + sec-getoption steps 8–9: + 8. Else if _type_ is Number, then + a. Set _value_ to ? ToNumber(value). + b. ... + 9. Else, + a. Set _value_ to ? ToString(value). + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.zoneddatetime.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_650_000n, "UTC"); + +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: null }), + "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: true }), + "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: false }), + "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits"); +assert.throws(TypeError, () => datetime.toString({ fractionalSecondDigits: Symbol() }), + "symbols are not numbers and cannot convert to strings"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: 2n }), + "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => datetime.toString({ fractionalSecondDigits: {} }), + "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits"); + +const expected = [ + "get fractionalSecondDigits.toString", + "call fractionalSecondDigits.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits"); +const result = datetime.toString({ fractionalSecondDigits: observer }); +assert.sameValue(result, "2001-09-09T01:46:40.98765+00:00[UTC]", "object with toString uses toString return value"); +assert.compareArray(actual, expected, "object with toString calls toString and not valueOf"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js new file mode 100644 index 0000000000..dffb76116e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Temporal.ZonedDateTime.prototype.toString.length is 0 +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.ZonedDateTime.prototype.toString, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js new file mode 100644 index 0000000000..63fe389903 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Temporal.ZonedDateTime.prototype.toString.name is "toString". +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.ZonedDateTime.prototype.toString, "name", { + value: "toString", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js new file mode 100644 index 0000000000..255a46d256 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.toString(); +assert.sameValue(result, "1969-07-24T16:50:35.000000001+00:00[UTC]"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js new file mode 100644 index 0000000000..753e7f306e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + Temporal.ZonedDateTime.prototype.toString 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.ZonedDateTime.prototype.toString(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.toString), false, + "isConstructor(Temporal.ZonedDateTime.prototype.toString)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js new file mode 100644 index 0000000000..44f8d760ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-invalid-string.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: RangeError thrown when offset option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-toshowoffsetoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 8: + 8. Let _showOffset_ be ? ToShowOffsetOption(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC"); +assert.throws(RangeError, () => datetime.toString({ offset: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js new file mode 100644 index 0000000000..030f5181cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-undefined.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Fallback value for offset option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-toshowoffsetoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 8: + 8. Let _showOffset_ be ? ToShowOffsetOption(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +const explicit = datetime.toString({ offset: undefined }); +assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default offset option is auto"); + +// See options-undefined.js for {} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js new file mode 100644 index 0000000000..8896eb0e57 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Type conversions for offset option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-toshowoffsetoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"auto"*, *"never"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 8: + 8. Let _showOffset_ be ? ToShowOffsetOption(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +TemporalHelpers.checkStringOptionWrongType("offset", "auto", + (offset) => datetime.toString({ offset }), + (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js new file mode 100644 index 0000000000..39896f5760 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/offset.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: The time zone offset part of the string serialization +features: [BigInt, Temporal] +---*/ + +function test(timeZoneIdentifier, expected, description) { + const timeZone = new Temporal.TimeZone(timeZoneIdentifier); + const datetime = new Temporal.ZonedDateTime(0n, timeZone); + assert.sameValue(datetime.toString(), expected, description); +} + +test("UTC", "1970-01-01T00:00:00+00:00[UTC]", "offset of UTC is +00:00"); +test("+01:00", "1970-01-01T01:00:00+01:00[+01:00]", "positive offset"); +test("-05:00", "1969-12-31T19:00:00-05:00[-05:00]", "negative offset"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js new file mode 100644 index 0000000000..d1ee3ac976 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-object.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.toString({}); +assert.sameValue( + result1, "1970-01-01T00:00:00+00:00[UTC]", + "options may be an empty plain object" +); + +const result2 = instance.toString(() => {}); +assert.sameValue( + result2, "1970-01-01T00:00:00+00:00[UTC]", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js new file mode 100644 index 0000000000..5a74aefd16 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-undefined.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Verify that undefined options are handled correctly. +features: [Temporal] +---*/ + +const calendar = { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "custom", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; +const datetime1 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC"); +const datetime2 = new Temporal.ZonedDateTime(957270896_987_650_000n, "UTC", calendar); + +[ + [datetime1, "2000-05-02T12:34:56.98765+00:00[UTC]"], + [datetime2, "2000-05-02T12:34:56.98765+00:00[UTC][u-ca=custom]"], +].forEach(([datetime, expected]) => { + const explicit = datetime.toString(undefined); + assert.sameValue(explicit, expected, "default show options are auto, precision is auto, and no rounding"); + + const propertyImplicit = datetime.toString({}); + assert.sameValue(propertyImplicit, expected, "default show options are auto, precision is auto, and no rounding"); + + const implicit = datetime.toString(); + assert.sameValue(implicit, expected, "default show options are auto, precision is auto, and no rounding"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js new file mode 100644 index 0000000000..6ecfd96475 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.toString(value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js new file mode 100644 index 0000000000..ebe60ab561 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/order-of-operations.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Properties on objects passed to toString() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get options.calendarName", + "get options.calendarName.toString", + "call options.calendarName.toString", + "get options.fractionalSecondDigits", + "get options.fractionalSecondDigits.toString", + "call options.fractionalSecondDigits.toString", + "get options.offset", + "get options.offset.toString", + "call options.offset.toString", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + "get options.timeZoneName", + "get options.timeZoneName.toString", + "call options.timeZoneName.toString", + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.id", + "get this.calendar.id", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +instance.toString( + TemporalHelpers.propertyBagObserver(actual, { + fractionalSecondDigits: "auto", + roundingMode: "halfExpand", + smallestUnit: "millisecond", + offset: "auto", + timeZoneName: "auto", + calendarName: "auto", + }, "options"), +); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +// Same as above but without accessing options.smallestUnit.toString +const expectedForFractionalSecondDigits = [ + "get options.calendarName", + "get options.calendarName.toString", + "call options.calendarName.toString", + "get options.fractionalSecondDigits", + "get options.fractionalSecondDigits.toString", + "call options.fractionalSecondDigits.toString", + "get options.offset", + "get options.offset.toString", + "call options.offset.toString", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.timeZoneName", + "get options.timeZoneName.toString", + "call options.timeZoneName.toString", + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.id", + "get this.calendar.id", +]; + +instance.toString( + TemporalHelpers.propertyBagObserver(actual, { + fractionalSecondDigits: "auto", + roundingMode: "halfExpand", + smallestUnit: undefined, + offset: "auto", + timeZoneName: "auto", + calendarName: "auto", + }, "options"), +); +assert.compareArray(actual, expectedForFractionalSecondDigits, "order of operations with smallestUnit undefined"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js new file mode 100644 index 0000000000..6e52f25f2a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: The "toString" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.toString, + "function", + "`typeof ZonedDateTime.prototype.toString` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "toString", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js new file mode 100644 index 0000000000..63856a0be9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-cross-midnight.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Rounding can cross midnight +features: [Temporal] +---*/ + +const zonedDateTime = new Temporal.ZonedDateTime(946_684_799_999_999_999n, "UTC"); // one nanosecond before 2000-01-01T00:00:00 +for (const roundingMode of ["ceil", "halfExpand"]) { + assert.sameValue(zonedDateTime.toString({ fractionalSecondDigits: 8, roundingMode }), "2000-01-01T00:00:00.00000000+00:00[UTC]"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js new file mode 100644 index 0000000000..80f3f01f07 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Rounding down is towards the Big Bang, not the epoch or 1 BCE +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(-65_261_246_399_500_000_000n, "UTC"); // -000099-12-15T12:00:00.5Z +assert.sameValue( + instance.toString({ smallestUnit: "second", roundingMode: "floor" }), + "-000099-12-15T12:00:00+00:00[UTC]", + "Rounding down is towards the Big Bang, not the epoch or 1 BCE" +); +assert.sameValue( + instance.toString({ smallestUnit: "second", roundingMode: "trunc" }), + "-000099-12-15T12:00:00+00:00[UTC]", + "Rounding down is towards the Big Bang, not the epoch or 1 BCE (roundingMode trunc)" +); +assert.sameValue( + instance.toString({ smallestUnit: "second", roundingMode: "ceil" }), + "-000099-12-15T12:00:01+00:00[UTC]", + "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode ceil)" +); +assert.sameValue( + instance.toString({ smallestUnit: "second", roundingMode: "halfExpand" }), + "-000099-12-15T12:00:01+00:00[UTC]", + "Rounding up is away from the Big Bang, not the epoch or 1 BCE (roundingMode halfExpand)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js new file mode 100644 index 0000000000..fe8a3e9544 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-ceil.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: ceil value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "ceil" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is ceil (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is ceil (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "ceil" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is ceil (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is ceil (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "ceil" }); +assert.sameValue(result5, "2001-09-09T01:46:41+00:00[UTC]", + "roundingMode is ceil (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" }); +assert.sameValue(result6, "2001-09-09T01:46:41+00:00[UTC]", + "roundingMode is ceil (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "ceil" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is ceil (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js new file mode 100644 index 0000000000..7cb024373e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-expand.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: expand value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "expand" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is expand (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "expand" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is expand (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "expand" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is expand (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "expand" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is expand (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "expand" }); +assert.sameValue(result5, "2001-09-09T01:46:41+00:00[UTC]", + "roundingMode is expand (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "expand" }); +assert.sameValue(result6, "2001-09-09T01:46:41+00:00[UTC]", + "roundingMode is expand (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "expand" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is expand (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js new file mode 100644 index 0000000000..6b8ef9ae7a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-floor.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: floor value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "floor" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is floor (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "floor" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is floor (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "floor" }); +assert.sameValue(result3, "2001-09-09T01:46:40.123+00:00[UTC]", + "roundingMode is floor (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "floor" }); +assert.sameValue(result4, "2001-09-09T01:46:40.123+00:00[UTC]", + "roundingMode is floor (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "floor" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is floor (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "floor" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is floor (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "floor" }); +assert.sameValue(result7, "2001-09-09T01:46+00:00[UTC]", "roundingMode is floor (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js new file mode 100644 index 0000000000..be443375bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfCeil.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: halfCeil value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfCeil" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfCeil (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfCeil" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfCeil (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfCeil" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfCeil (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfCeil" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfCeil (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfCeil" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfCeil (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfCeil" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfCeil (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfCeil" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfCeil (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js new file mode 100644 index 0000000000..086ce192d4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfEven.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: halfEven value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfEven" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfEven (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfEven" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfEven (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfEven" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfEven (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfEven" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfEven (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfEven" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfEven (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfEven" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfEven (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfEven" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfEven (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js new file mode 100644 index 0000000000..66bd63e1d2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfExpand.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: halfExpand value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfExpand (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123988+00:00[UTC]", + "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfExpand (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfExpand" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfExpand (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfExpand" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfExpand (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js new file mode 100644 index 0000000000..0a901fd446 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfFloor.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: halfFloor value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfFloor" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is halfFloor (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfFloor" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is halfFloor (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfFloor" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfFloor (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfFloor" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfFloor (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfFloor" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfFloor (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfFloor" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfFloor (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfFloor" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfFloor (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js new file mode 100644 index 0000000000..0ceac7ccce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-halfTrunc.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: halfTrunc value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "halfTrunc" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is halfTrunc (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "halfTrunc" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is halfTrunc (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "halfTrunc" }); +assert.sameValue(result3, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfTrunc (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "halfTrunc" }); +assert.sameValue(result4, "2001-09-09T01:46:40.124+00:00[UTC]", + "roundingMode is halfTrunc (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "halfTrunc" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfTrunc (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "halfTrunc" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is halfTrunc (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "halfTrunc" }); +assert.sameValue(result7, "2001-09-09T01:47+00:00[UTC]", "roundingMode is halfTrunc (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js new file mode 100644 index 0000000000..566c1f9f70 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-invalid-string.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => datetime.toString({ smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js new file mode 100644 index 0000000000..8e125466fc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-trunc.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: trunc value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const result1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: "trunc" }); +assert.sameValue(result1, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is trunc (with 6 digits from smallestUnit)"); + +const result2 = datetime.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" }); +assert.sameValue(result2, "2001-09-09T01:46:40.123987+00:00[UTC]", + "roundingMode is trunc (with 6 digits from fractionalSecondDigits)"); + +const result3 = datetime.toString({ smallestUnit: "millisecond", roundingMode: "trunc" }); +assert.sameValue(result3, "2001-09-09T01:46:40.123+00:00[UTC]", + "roundingMode is trunc (with 3 digits from smallestUnit)"); + +const result4 = datetime.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" }); +assert.sameValue(result4, "2001-09-09T01:46:40.123+00:00[UTC]", + "roundingMode is trunc (with 3 digits from fractionalSecondDigits)"); + +const result5 = datetime.toString({ smallestUnit: "second", roundingMode: "trunc" }); +assert.sameValue(result5, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is trunc (with 0 digits from smallestUnit)"); + +const result6 = datetime.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" }); +assert.sameValue(result6, "2001-09-09T01:46:40+00:00[UTC]", + "roundingMode is trunc (with 0 digits from fractionalSecondDigits)"); + +const result7 = datetime.toString({ smallestUnit: "minute", roundingMode: "trunc" }); +assert.sameValue(result7, "2001-09-09T01:46+00:00[UTC]", "roundingMode is trunc (round to minute)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js new file mode 100644 index 0000000000..550ab18630 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Fallback value for roundingMode option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const explicit1 = datetime.toString({ smallestUnit: "microsecond", roundingMode: undefined }); +assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc"); +const implicit1 = datetime.toString({ smallestUnit: "microsecond" }); +assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default roundingMode is trunc"); + +const explicit2 = datetime.toString({ smallestUnit: "millisecond", roundingMode: undefined }); +assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc"); +const implicit2 = datetime.toString({ smallestUnit: "millisecond" }); +assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default roundingMode is trunc"); + +const explicit3 = datetime.toString({ smallestUnit: "second", roundingMode: undefined }); +assert.sameValue(explicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc"); +const implicit3 = datetime.toString({ smallestUnit: "second" }); +assert.sameValue(implicit3, "2001-09-09T01:46:40+00:00[UTC]", "default roundingMode is trunc"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js new file mode 100644 index 0000000000..2f79532055 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/roundingmode-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc", + (roundingMode) => datetime.toString({ smallestUnit: "microsecond", roundingMode }), + (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js new file mode 100644 index 0000000000..6e8e3f2fef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-fractionalseconddigits.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: fractionalSecondDigits option is not used with smallestUnit present +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(56_789_999_999n, "UTC"); +const tests = [ + ["minute", "1970-01-01T00:00+00:00[UTC]"], + ["second", "1970-01-01T00:00:56+00:00[UTC]"], + ["millisecond", "1970-01-01T00:00:56.789+00:00[UTC]"], + ["microsecond", "1970-01-01T00:00:56.789999+00:00[UTC]"], + ["nanosecond", "1970-01-01T00:00:56.789999999+00:00[UTC]"], +]; + +for (const [smallestUnit, expected] of tests) { + const string = datetime.toString({ + smallestUnit, + fractionalSecondDigits: 5, + }); + assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`); +} + +assert.throws(RangeError, () => datetime.toString({ + smallestUnit: "hour", + fractionalSecondDigits: 5, +}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js new file mode 100644 index 0000000000..8a45ad4214 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-invalid-string.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +const badValues = [ + "era", + "eraYear", + "year", + "month", + "week", + "day", + "hour", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "years", + "months", + "weeks", + "days", + "hours", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => datetime.toString({ smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..b67d82bd0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC"); +const validUnits = [ + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => datetime.toString({ smallestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js new file mode 100644 index 0000000000..2a4ee1855e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-undefined.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Fallback value for smallestUnit option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); + +const explicit1 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 }); +assert.sameValue(explicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits"); +const implicit1 = datetime.toString({ fractionalSecondDigits: 6 }); +assert.sameValue(implicit1, "2001-09-09T01:46:40.123987+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits"); + +const explicit2 = datetime.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 }); +assert.sameValue(explicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits"); +const implicit2 = datetime.toString({ fractionalSecondDigits: 3 }); +assert.sameValue(implicit2, "2001-09-09T01:46:40.123+00:00[UTC]", "default smallestUnit defers to fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js new file mode 100644 index 0000000000..5058c1467e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-valid-units.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Valid units for the smallestUnit option +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC"); + +function test(instance, expectations, description) { + for (const [smallestUnit, expectedResult] of expectations) { + assert.sameValue(instance.toString({ smallestUnit }), expectedResult, + `${description} with smallestUnit "${smallestUnit}"`); + } +} + +test( + datetime, + [ + ["minute", "2001-09-09T01:46+00:00[UTC]"], + ["second", "2001-09-09T01:46:40+00:00[UTC]"], + ["millisecond", "2001-09-09T01:46:40.123+00:00[UTC]"], + ["microsecond", "2001-09-09T01:46:40.123456+00:00[UTC]"], + ["nanosecond", "2001-09-09T01:46:40.123456789+00:00[UTC]"], + ], + "subseconds toString" +); + +test( + new Temporal.ZonedDateTime(999_999_960_000_000_000n, "UTC"), + [ + ["minute", "2001-09-09T01:46+00:00[UTC]"], + ["second", "2001-09-09T01:46:00+00:00[UTC]"], + ["millisecond", "2001-09-09T01:46:00.000+00:00[UTC]"], + ["microsecond", "2001-09-09T01:46:00.000000+00:00[UTC]"], + ["nanosecond", "2001-09-09T01:46:00.000000000+00:00[UTC]"], + ], + "whole minutes toString" +); + +const notValid = [ + "era", + "year", + "month", + "week", + "day", + "hour", +]; + +notValid.forEach((smallestUnit) => { + assert.throws(RangeError, () => datetime.toString({ smallestUnit }), + `"${smallestUnit}" is not a valid unit for the smallestUnit option`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js new file mode 100644 index 0000000000..cc085c40cc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/smallestunit-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => datetime.toString({ smallestUnit }), + (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.123987+00:00[UTC]", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..91c0ea1627 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toString()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..54bff8833d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.toString(), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..7ae4e736d1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.toString()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..dac816905c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.toString()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js new file mode 100644 index 0000000000..68370146ff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezone-id-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.equals +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const zdt = Temporal.ZonedDateTime.from({ year: 1970, month: 1, day: 1, timeZone }); + assert.throws(TypeError, () => zdt.toString()); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js new file mode 100644 index 0000000000..a743f25031 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-auto.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: If timeZoneName is "auto", the time zone ID should be included. +features: [Temporal] +---*/ + +const tests = [ + ["UTC", "1970-01-01T01:01:01.987654321+00:00[UTC]", "built-in UTC"], + ["+01:00", "1970-01-01T02:01:01.987654321+01:00[+01:00]", "built-in offset"], + [{ + getOffsetNanosecondsFor() { return 0; }, + getPossibleInstantsFor() { return []; }, + id: "Etc/Custom", + }, "1970-01-01T01:01:01.987654321+00:00[Etc/Custom]", "custom"], +]; + +for (const [timeZone, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone); + const result = date.toString({ timeZoneName: "auto" }); + assert.sameValue(result, expected, `${description} time zone for timeZoneName = auto`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js new file mode 100644 index 0000000000..c4209394e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-critical.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: > + If timeZoneName is "auto", the time zone ID should be included and prefixed + with "!" +features: [Temporal] +---*/ + +const tests = [ + ["UTC", "1970-01-01T01:01:01.987654321+00:00[!UTC]", "built-in UTC"], + ["+01:00", "1970-01-01T02:01:01.987654321+01:00[!+01:00]", "built-in offset"], + [{ + getOffsetNanosecondsFor() { return 0; }, + getPossibleInstantsFor() { return []; }, + id: "Etc/Custom", + }, "1970-01-01T01:01:01.987654321+00:00[!Etc/Custom]", "custom"], +]; + +for (const [timeZone, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone); + const result = date.toString({ timeZoneName: "critical" }); + assert.sameValue(result, expected, `${description} time zone for timeZoneName = critical`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js new file mode 100644 index 0000000000..4e6f47c7c7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-invalid-string.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: RangeError thrown when timeZoneName option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-toshowtimezonenameoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 7: + 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_64_321n, "UTC"); +const invalidValues = ["NEVER", "sometimes", "other string", "auto\0"]; + +for (const timeZoneName of invalidValues) { + assert.throws( + RangeError, + () => datetime.toString({ timeZoneName }), + `${timeZoneName} is an invalid value for timeZoneName option` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js new file mode 100644 index 0000000000..9fcb10d071 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-never.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: If timeZoneName is "never", the time zone ID should be omitted. +features: [Temporal] +---*/ + +const tests = [ + ["UTC", "1970-01-01T01:01:01.987654321+00:00", "built-in UTC"], + ["+01:00", "1970-01-01T02:01:01.987654321+01:00", "built-in offset"], + [{ + getOffsetNanosecondsFor() { return 0; }, + getPossibleInstantsFor() { return []; }, + id: "Etc/Custom", + }, "1970-01-01T01:01:01.987654321+00:00", "custom"], +]; + +for (const [timeZone, expected, description] of tests) { + const date = new Temporal.ZonedDateTime(3661_987_654_321n, timeZone); + const result = date.toString({ timeZoneName: "never" }); + assert.sameValue(result, expected, `${description} time zone for timeZoneName = never`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js new file mode 100644 index 0000000000..12a60c3d1b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-undefined.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Fallback value for timeZoneName option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-toshowtimezonenameoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 7: + 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +const explicit = datetime.toString({ timeZoneName: undefined }); +assert.sameValue(explicit, "2001-09-09T01:46:40.987654321+00:00[UTC]", "default timeZoneName option is auto"); + +// See options-object.js for {} and options-undefined.js for absent + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js new file mode 100644 index 0000000000..672dc77901 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/timezonename-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.tostring +description: Type conversions for timeZoneName option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-toshowtimezonenameoption step 1: + 1. Return ? GetOption(_normalizedOptions_, *"timeZoneName"*, « *"string"* », « *"auto"*, *"never"*, *"critical"* », *"auto"*). + sec-temporal.zoneddatetime.protoype.tostring step 7: + 7. Let _showTimeZone_ be ? ToShowTimeZoneNameOption(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +TemporalHelpers.checkStringOptionWrongType("timeZoneName", "auto", + (timeZoneName) => datetime.toString({ timeZoneName }), + (result, descr) => assert.sameValue(result, "2001-09-09T01:46:40.987654321+00:00[UTC]", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js new file mode 100644 index 0000000000..f90960cf9f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toString/year-format.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.tostring +description: Verify that the year is appropriately formatted as 4 or 6 digits +features: [Temporal] +---*/ + +function epochNsInYear(year) { + // Return an epoch nanoseconds value near the middle of the given year + const avgNsPerYear = 31_556_952_000_000_000n; + return (year - 1970n) * avgNsPerYear + (avgNsPerYear / 2n); +} + +const utc = new Temporal.TimeZone("UTC"); + +let instance = new Temporal.ZonedDateTime(epochNsInYear(-100000n), utc); +assert.sameValue(instance.toString(), "-100000-07-01T21:30:36+00:00[UTC]", "large negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-10000n), utc); +assert.sameValue(instance.toString(), "-010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-9999n), utc); +assert.sameValue(instance.toString(), "-009999-07-02T03:19:48+00:00[UTC]", "largest 4-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-1000n), utc); +assert.sameValue(instance.toString(), "-001000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-999n), utc); +assert.sameValue(instance.toString(), "-000999-07-02T15:19:48+00:00[UTC]", "largest 3-digit negative year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(-1n), utc); +assert.sameValue(instance.toString(), "-000001-07-02T15:41:24+00:00[UTC]", "year -1 formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(0n), utc); +assert.sameValue(instance.toString(), "0000-07-01T21:30:36+00:00[UTC]", "year 0 formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(1n), utc); +assert.sameValue(instance.toString(), "0001-07-02T03:19:48+00:00[UTC]", "year 1 formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(999n), utc); +assert.sameValue(instance.toString(), "0999-07-02T03:41:24+00:00[UTC]", "largest 3-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(1000n), utc); +assert.sameValue(instance.toString(), "1000-07-02T09:30:36+00:00[UTC]", "smallest 4-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(9999n), utc); +assert.sameValue(instance.toString(), "9999-07-02T15:41:24+00:00[UTC]", "largest 4-digit positive year formatted as 4-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(10000n), utc); +assert.sameValue(instance.toString(), "+010000-07-01T21:30:36+00:00[UTC]", "smallest 5-digit positive year formatted as 6-digit"); + +instance = new Temporal.ZonedDateTime(epochNsInYear(100000n), utc); +assert.sameValue(instance.toString(), "+100000-07-01T21:30:36+00:00[UTC]", "large positive year formatted as 6-digit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js new file mode 100644 index 0000000000..44b8978e72 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/prop-desc.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype-@@tostringtag +description: The @@toStringTag property of Temporal.ZonedDateTime +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.ZonedDateTime.prototype, Symbol.toStringTag, { + value: "Temporal.ZonedDateTime", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/toStringTag/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..2c1b07d2f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" }; +instance.until(arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..43819a54b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Correct time zone calls are made when converting a ZonedDateTime-like property + bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone), + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone), +}); +const calendar = TemporalHelpers.calendarObserver(actual, "calendar"); + +const timeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let arg = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.until(arg); + +const expected = [ + // GetTemporalCalendarSlotValueWithISODefault + "has calendar.dateAdd", + "has calendar.dateFromFields", + "has calendar.dateUntil", + "has calendar.day", + "has calendar.dayOfWeek", + "has calendar.dayOfYear", + "has calendar.daysInMonth", + "has calendar.daysInWeek", + "has calendar.daysInYear", + "has calendar.fields", + "has calendar.id", + "has calendar.inLeapYear", + "has calendar.mergeFields", + "has calendar.month", + "has calendar.monthCode", + "has calendar.monthDayFromFields", + "has calendar.monthsInYear", + "has calendar.weekOfYear", + "has calendar.year", + "has calendar.yearMonthFromFields", + "has calendar.yearOfWeek", + // lookup in ToTemporalZonedDateTime + "get calendar.dateFromFields", + "get calendar.fields", + // CalendarFields + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "call calendar.dateFromFields", + // lookup in ToTemporalZonedDateTime + "get timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "call timeZone.getPossibleInstantsFor", +]; + +const expectedSpringForward = expected.concat([ + // DisambiguatePossibleInstants + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToTemporalZonedDateTime + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +arg = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.until(arg); + +assert.compareArray( + actual.slice(0, expected.length), // ignore operations after ToTemporalZonedDateTime + expected, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..961a1ee8db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The calendar name is case-insensitive +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "IsO8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.until(arg); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..347b6c0c73 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Leap second is a valid ISO string for a calendar in a property bag +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.until(arg); +TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..e8fa71f417 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-number.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; + assert.throws( + TypeError, + () => instance.until(arg), + "Numbers cannot be used as a calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..379559e1ee --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-string.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: A calendar ID is valid input for Calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const calendar = "iso8601"; + +const arg = { year: 1970, monthCode: "M01", day: 1, timeZone, calendar }; +const result = instance.until(arg); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, `Calendar created from string "${calendar}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..7a69647935 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === 'string' ? RangeError : TypeError, + () => instance.until(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..f0c915198e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.until(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..02570557e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar }; + +const instance = new Temporal.ZonedDateTime(0n, timeZone); +instance.until(arg); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..ef4bb27e4e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-invalid-offset-string.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +badOffsets.forEach((offset) => { + const arg = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => instance.until(arg), + `"${offset} is not a valid offset string` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js new file mode 100644 index 0000000000..09ff638ad8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-offset-not-agreeing-with-timezone.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Property bag with offset property is rejected if offset does not agree with time zone +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:00"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const properties = { year: 2021, month: 10, day: 28, offset: "-07:00", timeZone }; +assert.throws(RangeError, () => instance.until(properties), "offset property not matching time zone is rejected"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..562b925709 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.until(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..b52d5f81e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.until(properties, { largestUnit: "days" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..6fb40a0e8e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(RangeError, () => datetime.until(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..d243987018 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + timeZone.getPossibleInstantsFor = function () { + return []; + }; + assert.throws(TypeError, () => datetime.until(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js new file mode 100644 index 0000000000..c11efec771 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-id-wrong-type.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Justin Grant. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown if time zone reports an id that is not a String +features: [Temporal] +---*/ + +class CustomTimeZone extends Temporal.TimeZone { + constructor(id) { + super("UTC"); + this._id = id; + } + get id() { + return this._id; + } +} + +[ + undefined, + null, + true, + -1000, + Symbol(), + 3600_000_000_000n, + {}, + { + valueOf() { + return 3600_000_000_000; + } + } +].forEach((wrongId) => { + const timeZone = new CustomTimeZone(wrongId); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const properties = { year: 2004, month: 11, day: 9, hour: 11, minute: 33, second: 20, timeZone }; + assert.throws(TypeError, () => datetime.until(properties, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..94f4001ee4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js @@ -0,0 +1,69 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let expectedTimeZone = "UTC"; +const instance1 = new Temporal.ZonedDateTime(0n, expectedTimeZone); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance1.until({ year: 2020, month: 5, day: 2, timeZone }), "bare date-time string is not a time zone"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => instance1.until({ year: 2020, month: 5, day: 2, timeZone }), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone` + ); +}); + +// The following are all valid strings so should not throw. They should produce +// expectedTimeZone, so additionally the operation will not throw due to the +// time zones being different on the receiver and the argument. + +timeZone = "2021-08-19T17:30Z"; +instance1.until({ year: 2020, month: 5, day: 2, timeZone }); + +expectedTimeZone = "-07:00"; +const instance2 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-08-19T17:30-07:00"; +instance2.until({ year: 2020, month: 5, day: 2, timeZone }); + +expectedTimeZone = "UTC"; +const instance3 = new Temporal.ZonedDateTime(0n, expectedTimeZone); +timeZone = "2021-08-19T17:30[UTC]"; +instance3.until({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30Z[UTC]"; +instance3.until({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30-07:00[UTC]"; +instance3.until({ year: 2020, month: 5, day: 2, timeZone }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..9ba561195b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const expectedTimeZone = "UTC"; +const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// This operation should produce expectedTimeZone, so the following operation +// should not throw due to the time zones being different on the receiver and +// the argument. + +instance.until({ year: 2020, month: 5, day: 2, timeZone }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.until({ year: 2020, month: 5, day: 2, timeZone }), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..2be403f13e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-multiple-offsets.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const expectedTimeZone = "+01:46"; +const instance = new Temporal.ZonedDateTime(0n, expectedTimeZone); +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +// This operation should produce expectedTimeZone, so the following operation +// should not throw due to the time zones being different on the receiver and +// the argument. + +instance.until({ year: 2020, month: 5, day: 2, timeZone }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..0882be9c80 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-year-zero.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.until({ year: 2020, month: 5, day: 2, timeZone }), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js new file mode 100644 index 0000000000..d2c99517b2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance1 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +assert(instance1.until({ year: 1970, month: 1, day: 1, timeZone: "UTC" }).blank, "Time zone created from string 'UTC'"); + +const instance2 = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("-01:30")); +assert(instance2.until({ year: 1969, month: 12, day: 31, hour: 22, minute: 30, timeZone: "-01:30" }).blank, "Time zone created from string '-01:30'"); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..cc9d5d6e7b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => instance.until({ year: 2020, month: 5, day: 2, 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, () => instance.until({ year: 2020, month: 5, day: 2, timeZone }), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..3f50c229cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-calendar-annotation.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][u-ca=iso8601]", "without !"], + ["1970-01-01T00:00[UTC][!u-ca=iso8601]", "with !"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.until(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..79896e6656 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-critical-unknown-annotation.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.until(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..1949680722 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-date-with-utc-offset.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: UTC offset not valid with format that does not include a time +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const validStrings = [ + "1970-01-01T00Z[UTC]", + "1970-01-01T00Z[!UTC]", + "1970-01-01T00+00:00[UTC]", + "1970-01-01T00+00:00[!UTC]", +]; + +for (const arg of validStrings) { + const result = instance.until(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `"${arg}" is a valid UTC offset with time for ZonedDateTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.until(arg), + `"${arg}" UTC offset without time is not valid for ZonedDateTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..ee56d8c8d5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-calendar.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[UTC][!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.until(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..5204d2b29a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-multiple-time-zone.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.until(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js new file mode 100644 index 0000000000..c494617d6c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-separators.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Time separator in string argument can vary +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00+00:00[UTC]", "uppercase T"], + ["1970-01-01t00:00+00:00[UTC]", "lowercase T"], + ["1970-01-01 00:00+00:00[UTC]", "space between date and time"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.until(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..ac9f4d59ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-time-zone-annotation.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC]", "named, with no offset"], + ["1970-01-01T00:00[!UTC]", "named, with ! and no offset"], + ["1970-01-01T00:00[+00:00]", "numeric, with no offset"], + ["1970-01-01T00:00[!+00:00]", "numeric, with ! and no offset"], + ["1970-01-01T00:00Z[UTC]", "named, with Z"], + ["1970-01-01T00:00Z[!UTC]", "named, with Z and !"], + ["1970-01-01T00:00Z[+00:00]", "numeric, with Z"], + ["1970-01-01T00:00Z[!+00:00]", "numeric, with Z and !"], + ["1970-01-01T00:00+00:00[UTC]", "named, with offset"], + ["1970-01-01T00:00+00:00[!UTC]", "named, with offset and !"], + ["1970-01-01T00:00+00:00[+00:00]", "numeric, with offset"], + ["1970-01-01T00:00+00:00[!+00:00]", "numeric, with offset and !"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.until(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..c39e2f6531 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-unknown-annotation.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Various forms of unknown annotation +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tests = [ + ["1970-01-01T00:00[UTC][foo=bar]", "with time zone"], + ["1970-01-01T00:00[UTC][foo=bar][u-ca=iso8601]", "before calendar"], + ["1970-01-01T00:00[UTC][u-ca=iso8601][foo=bar]", "after calendar"], + ["1970-01-01T00:00[UTC][foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.until(arg); + + TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js new file mode 100644 index 0000000000..974d14d117 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-wrong-type.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for ZonedDateTime +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const primitiveTests = [ + [undefined, "undefined"], + [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"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.until(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.until(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js new file mode 100644 index 0000000000..f409f8fd2e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/balance-negative-time-units.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-differencetime step 8: + 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-differenceisodatetime step 2: + 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + sec-temporal-differencezoneddatetime step 7: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + sec-temporal.zoneddatetime.prototype.until step 15: + 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const datetime = new Temporal.ZonedDateTime(830998861_001_001_001n, timeZone); +const options = { largestUnit: "days" }; + +const result1 = new Temporal.ZonedDateTime(830995200_000_000_002n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance"); + +const result2 = new Temporal.ZonedDateTime(830995200_000_002_000n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance"); + +const result3 = new Temporal.ZonedDateTime(830995200_002_000_000n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance"); + +const result4 = new Temporal.ZonedDateTime(830995202_000_000_000n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance"); + +const result5 = new Temporal.ZonedDateTime(830995320_000_000_000n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance"); + +// This one is different because hours are later balanced again in BalanceDuration +const result6 = new Temporal.ZonedDateTime(831002400_000_000_000n, timeZone).until(datetime, options); +TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/branding.js new file mode 100644 index 0000000000..12d8ab85d8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const until = Temporal.ZonedDateTime.prototype.until; + +assert.sameValue(typeof until, "function"); + +const args = [new Temporal.ZonedDateTime(123456n, new Temporal.TimeZone("UTC"))]; + +assert.throws(TypeError, () => until.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => until.apply(null, args), "null"); +assert.throws(TypeError, () => until.apply(true, args), "true"); +assert.throws(TypeError, () => until.apply("", args), "empty string"); +assert.throws(TypeError, () => until.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => until.apply(1, args), "1"); +assert.throws(TypeError, () => until.apply({}, args), "plain object"); +assert.throws(TypeError, () => until.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => until.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..4967b2b786 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const dateUntilOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateUntil"); +Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateUntil should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.until(new Temporal.ZonedDateTime(1_100_000_000_000_000_000n, "UTC")); + +Object.defineProperty(Temporal.Calendar.prototype, "dateUntil", dateUntilOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..7686dbafc1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.until(new Temporal.ZonedDateTime(1_100_000_000_000_000_000n, "UTC")); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js new file mode 100644 index 0000000000..7183c7345c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Tests that Temporal.ZonedDateTime.prototype.until + 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.ZonedDateTime.prototype.until), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.until), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.until), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.until.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..5710b9c2b4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +// Basic difference with largestUnit larger than days. +// The call comes from this path: +// ZonedDateTime.until() -> DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() + +const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar); +earlier.until(later1, { largestUnit: "weeks" }); +assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days"); + +// Difference with rounding, with smallestUnit a calendar unit. +// The calls come from these paths: +// ZonedDateTime.until() -> +// DifferenceZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd() +// MoveRelativeDate -> calendar.dateAdd() +// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd() + +calendar.dateAddCallCount = 0; + +earlier.until(later1, { smallestUnit: "weeks" }); +assert.sameValue(calendar.dateAddCallCount, 4, "rounding difference with calendar smallestUnit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..a60c47d66f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +const arg = { year: 2000, month: 5, day: 2, timeZone, calendar }; +instance.until(arg); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js new file mode 100644 index 0000000000..49eef7f158 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-copy-of-options.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The dateUntil() method on the calendar is called with a copy of the options bag +features: [Temporal] +---*/ + +const originalOptions = { + largestUnit: "year", + shouldBeCopied: {}, +}; +let called = false; + +class Calendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + + dateUntil(d1, d2, options) { + called = true; + assert.notSameValue(options, originalOptions, "options bag should be a copy"); + assert.sameValue(options.shouldBeCopied, originalOptions.shouldBeCopied, "options bag should be a shallow copy"); + return new Temporal.Duration(1); + } +} +const calendar = new Calendar(); +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +// exactly one year later; avoids NanosecondsToDays path +const later = new Temporal.ZonedDateTime(1_031_536_000_000_000_000n, "UTC", calendar); +earlier.until(later, originalOptions); +assert(called, "calendar.dateUntil must be called"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js new file mode 100644 index 0000000000..3cac430ecd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-null-prototype-options.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Calendar.dateUntil method is called with a null-prototype object as the + options value when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckOptionsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const argument = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +instance.until(argument, { largestUnit: "year" }); +assert.sameValue(calendar.dateUntilCallCount, 1, "dateUntil should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..e283cf25c8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,117 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.zoneddatetime.prototype.until steps 13–17: + 13. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + c. Return ... + 14. ... + 15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_). + 16. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + 17. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + earlier.until(later, { largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Additionally check the path that goes through AdjustRoundedDurationDays + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const earlier = new Temporal.ZonedDateTime(-31536000_000_000_000n /* = -365 days */, "UTC", calendar); + const later = new Temporal.ZonedDateTime(86_399_999_999_999n, "UTC", calendar); + earlier.until(later, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' }); + }, + { + years: ["year", "year", "year"], + months: ["month", "month", "month"], + weeks: ["week", "week", "week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Also check the path that goes through RoundDuration when smallestUnit is +// given + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, smallestUnit) => { + const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC", calendar); + earlier.until(later, { smallestUnit }); + }, + { + years: ["year", "year", "year"], + months: ["month", "month", "month"], + weeks: ["week", "week", "week"], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js new file mode 100644 index 0000000000..11a2409c47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-fields-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.until step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar1 = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1); +const calendar2 = TemporalHelpers.calendarFieldsIterable(); +datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: calendar2 }); + +assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called"); +assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js new file mode 100644 index 0000000000..6fa9b6970b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.prototype.until step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", temporalObject); + datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC", calendar: temporalObject }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..22d5780862 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/constructor-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.until(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js new file mode 100644 index 0000000000..8facb0f633 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/date-and-time-durations-opposite-signs.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2024 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Rounding calculation in difference method can result in duration date and + time components with opposite signs +info: | + DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ) + 17. If _roundingGranularityIsNoop_ is *false*, then + ... + e. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], + _roundResult_.[[Weeks]], _days_, _daysResult_.[[NormalizedTime]], _settings_.[[RoundingIncrement]], + _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_, _calendarRec_, _timeZoneRec_, + _precalculatedPlainDateTime_). + f. Let _balanceResult_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], + _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _settings_.[[SmallestUnit]], + _plainRelativeTo_, _calendarRec_). + g. Set _result_ to ? CombineDateAndNormalizedTimeDuration(_balanceResult_, _adjustResult_.[[NormalizedTime]]). +features: [Temporal] +---*/ + +// Based on a test case by André Bargull + +const calendar = new class extends Temporal.Calendar { + #dateUntil = 0; + + dateUntil(one, two, options) { + let result = super.dateUntil(one, two, options); + if (++this.#dateUntil === 2) { + result = result.negated(); + } + return result; + } +}("iso8601"); + +const oneDay = 86400_000_000_000; +const start = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const end = new Temporal.ZonedDateTime(BigInt(500.5 * oneDay), "UTC", calendar); + +assert.throws(RangeError, () => start.until(end, { + largestUnit: "years", + smallestUnit: "hours", +})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js new file mode 100644 index 0000000000..d0f6eb0bf2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/duplicate-calendar-fields.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['monthCode'], ['nanosecond'], ['second'], ['year'], ['timeZone'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const timeZone = 'Europe/Paris' + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; + const instance = new Temporal.ZonedDateTime(0n, timeZone); + + assert.throws(RangeError, () => instance.until(arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..9848b12866 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.prototype.until +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321, timeZone: "UTC" }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.until({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.until({ ...base, [prop]: obj })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js new file mode 100644 index 0000000000..a55f710a75 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when largestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string" +]; +for (const largestUnit of badValues) { + assert.throws(RangeError, () => earlier.until(later, { largestUnit }), + `"${largestUnit}" is not a valid value for largestUnit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..acfa1f76b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => earlier.until(later, { largestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js new file mode 100644 index 0000000000..08735ae401 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-smallestunit-mismatch.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when smallestUnit is larger than largestUnit +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; +for (let largestIdx = 1; largestIdx < units.length; largestIdx++) { + for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) { + const largestUnit = units[largestIdx]; + const smallestUnit = units[smallestIdx]; + assert.throws(RangeError, () => earlier.until(later, { largestUnit, smallestUnit })); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js new file mode 100644 index 0000000000..52b267a051 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fallback value for largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); + +const explicit = earlier.until(later, { largestUnit: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour"); +const implicit = earlier.until(later, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default largestUnit is hour"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js new file mode 100644 index 0000000000..ca09a1645c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/largestunit-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Type conversions for largestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("largestUnit", "year", + (largestUnit) => earlier.until(later, { largestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 1, 1, 1, 987, 654, 321, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js new file mode 100644 index 0000000000..b581353e6a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/leap-second.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Leap second is a valid ISO string for ZonedDateTime +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_483_228_799_000_000_000n, timeZone); + +let arg = "2016-12-31T23:59:60+00:00[UTC]"; +const result = instance.until(arg); +TemporalHelpers.assertDuration( + result, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for ZonedDateTime" +); + +arg = "2000-05-02T12:34:56+23:59[+23:59:60]"; +assert.throws( + RangeError, + () => instance.until(arg), + "leap second in time zone name not valid" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js new file mode 100644 index 0000000000..f020c9be0f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Temporal.ZonedDateTime.prototype.until.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.ZonedDateTime.prototype.until, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js new file mode 100644 index 0000000000..df895120eb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Temporal.ZonedDateTime.prototype.until.name is "until". +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.ZonedDateTime.prototype.until, "name", { + value: "until", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js new file mode 100644 index 0000000000..1d0145e333 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/negative-epochnanoseconds.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.until(new Temporal.ZonedDateTime(0n, "UTC"), { largestUnit: "month" }); +TemporalHelpers.assertDuration(result, 0, 5, 0, 7, 7, 9, 24, 999, 999, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js new file mode 100644 index 0000000000..f057d733d9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-loop-arbitrarily.js @@ -0,0 +1,78 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer +info: | + NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] ) + ... + 21. Repeat, while done is false, + a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]], + relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign). + b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]], + relativeResult.[[EpochNanoseconds]]). + c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs). + c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then + i. Set norm to oneDayLess. + ii. Set relativeResult to oneDayFarther. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const dayLengthNs = 86400000000000n; +const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601"); + +function createRelativeTo(count) { + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(50); +calls.splice(0); // Reset calls list after ZonedDateTime construction +zdt.until(other, { + largestUnit: "day", +}); +assert.sameValue( + calls.length, + 50 + 1, + "Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(100); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +zdt.until(other, { + largestUnit: "day", +}); +assert.sameValue( + calls.length, + 100 + 1, + "Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(105); +assert.throws(RangeError, () => zdt.until(other, { largestUnit: "day" }), "105 days > 2⁵³ ns"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js new file mode 100644 index 0000000000..178b1c981b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/normalized-time-duration-to-days-range-errors.js @@ -0,0 +1,126 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Abstract operation NormalizedTimeDurationToDays can throw four different + RangeErrors. +info: | + NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] ) + 22. If days < 0 and sign = 1, throw a RangeError exception. + 23. If days > 0 and sign = -1, throw a RangeError exception. + ... + 25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception. + ... + 28. If dayLength ≥ 2⁵³, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +const dayNs = 86_400_000_000_000; +const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC"); +const oneZDT = new Temporal.ZonedDateTime(1n, "UTC"); +const epochInstant = new Temporal.Instant(0n); +const options = { largestUnit: "days" }; + +// Step 22: days < 0 and sign = 1 +let start = new Temporal.ZonedDateTime( + 0n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[epochInstant]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned in step 8, setting _startDateTime_ + -dayNs + 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.until( + oneZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 23: days > 0 and sign = -1 +start = new Temporal.ZonedDateTime( + 1n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[epochInstant]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + -dayNs + 1, // Returned in step 8, setting _startDateTime_ + dayNs - 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.until( + zeroZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 25: nanoseconds > 0 and sign = -1 +start = new Temporal.ZonedDateTime( + 1n, // Sets DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_ + [ + // Behave normally in 2 calls made prior to NormalizedTimeDurationToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned in step 8, setting _startDateTime_ + -dayNs + 1, // Returned in step 9, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + start.until( + zeroZDT, // Sets DifferenceZonedDateTime _ns2_ + options + ) +); + +// Step 28: day length is an unsafe integer +start = new Temporal.ZonedDateTime( + 0n, + timeZoneSubstituteValues( + // Not called in step 16 because _days_ = 0 + // Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_ + [[new Temporal.Instant(2n ** 53n)]], + [] + ) +); +assert.throws(RangeError, () => + start.until( + oneZDT, + options + ), + "Should throw RangeError when time zone calculates an outrageous day length" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js new file mode 100644 index 0000000000..e2d3e5f308 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + Temporal.ZonedDateTime.prototype.until 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.ZonedDateTime.prototype.until(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.until), false, + "isConstructor(Temporal.ZonedDateTime.prototype.until)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js new file mode 100644 index 0000000000..e1de3222d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-object.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Empty or a function object may be used as options +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), {}); +TemporalHelpers.assertDuration( + result1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + "options may be an empty plain object" +); + +const result2 = instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), () => {}); +TemporalHelpers.assertDuration( + result2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js new file mode 100644 index 0000000000..c802ea499c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(959949296_987_654_322n, "UTC"); + +const explicit = earlier.until(later, undefined); +assert.sameValue(explicit.years, 0, "default largest unit is hours"); +assert.sameValue(explicit.months, 0, "default largest unit is hours"); +assert.sameValue(explicit.weeks, 0, "default largest unit is hours"); +assert.sameValue(explicit.days, 0, "default largest unit is hours"); +assert.sameValue(explicit.hours, 744, "default largest unit is hours"); +assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding"); + +const implicit = earlier.until(later); +assert.sameValue(implicit.years, 0, "default largest unit is hours"); +assert.sameValue(implicit.months, 0, "default largest unit is hours"); +assert.sameValue(implicit.weeks, 0, "default largest unit is hours"); +assert.sameValue(implicit.days, 0, "default largest unit is hours"); +assert.sameValue(implicit.hours, 744, "default largest unit is hours"); +assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-wrong-type.js new file mode 100644 index 0000000000..ce6b990e33 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.until(new Temporal.ZonedDateTime(3600_000_000_000n, "UTC"), value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js new file mode 100644 index 0000000000..6a233dc989 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js @@ -0,0 +1,408 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Properties on objects passed to until() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalZonedDateTime + "get other.calendar", + "has other.calendar.dateAdd", + "has other.calendar.dateFromFields", + "has other.calendar.dateUntil", + "has other.calendar.day", + "has other.calendar.dayOfWeek", + "has other.calendar.dayOfYear", + "has other.calendar.daysInMonth", + "has other.calendar.daysInWeek", + "has other.calendar.daysInYear", + "has other.calendar.fields", + "has other.calendar.id", + "has other.calendar.inLeapYear", + "has other.calendar.mergeFields", + "has other.calendar.month", + "has other.calendar.monthCode", + "has other.calendar.monthDayFromFields", + "has other.calendar.monthsInYear", + "has other.calendar.weekOfYear", + "has other.calendar.year", + "has other.calendar.yearMonthFromFields", + "has other.calendar.yearOfWeek", + "get other.calendar.dateFromFields", + "get other.calendar.fields", + "call other.calendar.fields", + "get other.day", + "get other.day.valueOf", + "call other.day.valueOf", + "get other.hour", + "get other.hour.valueOf", + "call other.hour.valueOf", + "get other.microsecond", + "get other.microsecond.valueOf", + "call other.microsecond.valueOf", + "get other.millisecond", + "get other.millisecond.valueOf", + "call other.millisecond.valueOf", + "get other.minute", + "get other.minute.valueOf", + "call other.minute.valueOf", + "get other.month", + "get other.month.valueOf", + "call other.month.valueOf", + "get other.monthCode", + "get other.monthCode.toString", + "call other.monthCode.toString", + "get other.nanosecond", + "get other.nanosecond.valueOf", + "call other.nanosecond.valueOf", + "get other.offset", + "get other.offset.toString", + "call other.offset.toString", + "get other.second", + "get other.second.valueOf", + "call other.second.valueOf", + "get other.timeZone", + "get other.year", + "get other.year.valueOf", + "call other.year.valueOf", + "has other.timeZone.getOffsetNanosecondsFor", + "has other.timeZone.getPossibleInstantsFor", + "has other.timeZone.id", + "call other.calendar.dateFromFields", + "get other.timeZone.getOffsetNanosecondsFor", + "get other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getOffsetNanosecondsFor", + // CalendarEquals + "get this.calendar.id", + "get other.calendar.id", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.roundingIncrement", + "get options.roundingIncrement", + "getOwnPropertyDescriptor options.roundingMode", + "get options.roundingMode", + "getOwnPropertyDescriptor options.largestUnit", + "get options.largestUnit", + "getOwnPropertyDescriptor options.smallestUnit", + "get options.smallestUnit", + "getOwnPropertyDescriptor options.additional", + "get options.additional", + // GetDifferenceSettings + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", +]; +const actual = []; + +const ownTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar); + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const ownDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); +const otherDstTimeZone = TemporalHelpers.timeZoneObserver(actual, "other.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); +/* 2000-10-29T01:30-07:00, in the middle of the first repeated hour: */ +const fallBackInstance = new Temporal.ZonedDateTime(972808200_000_000_000n, ownDstTimeZone, ownCalendar); + +const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2004, + month: 5, + monthCode: "M05", + day: 12, + hour: 1, + minute: 46, + second: 40, + millisecond: 250, + microsecond: 500, + nanosecond: 750, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"), +}, "other"); + +function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1 } = {}) { + return TemporalHelpers.propertyBagObserver(actual, { + // order is significant, due to iterating through properties in order to + // copy them to an internal null-prototype object: + roundingIncrement, + roundingMode, + largestUnit, + smallestUnit, + additional: "property", + }, "options"); +} + +// clear any observable things that happened while constructing the objects +actual.splice(0); + +// basic order of observable operations, without rounding: +instance.until(otherDateTimePropertyBag, createOptionsObserver()); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +// short-circuit for identical objects will still test TimeZoneEquals if +// largestUnit is a calendar unit: +const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 9, + monthCode: "M09", + day: 9, + hour: 1, + minute: 46, + second: 40, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "other.timeZone"), +}, "other"); + +instance.until(identicalPropertyBag, createOptionsObserver({ largestUnit: "years" })); +assert.compareArray(actual, expected.concat([ + "get this.timeZone.id", + "get other.timeZone.id", +]), "order of operations with identical dates and largestUnit a calendar unit"); +actual.splice(0); // clear + +// two ZonedDateTimes that denote the same wall-clock time in the time zone can +// avoid calling some calendar methods: +const fallBackPropertyBag = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "-08:00", + calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"), + timeZone: otherDstTimeZone, +}, "other"); +fallBackInstance.until(fallBackPropertyBag, createOptionsObserver({ largestUnit: "days" })); +assert.compareArray(actual, [ + // ToTemporalZonedDateTime + "get other.calendar", + "has other.calendar.dateAdd", + "has other.calendar.dateFromFields", + "has other.calendar.dateUntil", + "has other.calendar.day", + "has other.calendar.dayOfWeek", + "has other.calendar.dayOfYear", + "has other.calendar.daysInMonth", + "has other.calendar.daysInWeek", + "has other.calendar.daysInYear", + "has other.calendar.fields", + "has other.calendar.id", + "has other.calendar.inLeapYear", + "has other.calendar.mergeFields", + "has other.calendar.month", + "has other.calendar.monthCode", + "has other.calendar.monthDayFromFields", + "has other.calendar.monthsInYear", + "has other.calendar.weekOfYear", + "has other.calendar.year", + "has other.calendar.yearMonthFromFields", + "has other.calendar.yearOfWeek", + "get other.calendar.dateFromFields", + "get other.calendar.fields", + "call other.calendar.fields", + "get other.day", + "get other.day.valueOf", + "call other.day.valueOf", + "get other.hour", + "get other.hour.valueOf", + "call other.hour.valueOf", + "get other.microsecond", + "get other.microsecond.valueOf", + "call other.microsecond.valueOf", + "get other.millisecond", + "get other.millisecond.valueOf", + "call other.millisecond.valueOf", + "get other.minute", + "get other.minute.valueOf", + "call other.minute.valueOf", + "get other.month", + "get other.month.valueOf", + "call other.month.valueOf", + "get other.monthCode", + "get other.monthCode.toString", + "call other.monthCode.toString", + "get other.nanosecond", + "get other.nanosecond.valueOf", + "call other.nanosecond.valueOf", + "get other.offset", + "get other.offset.toString", + "call other.offset.toString", + "get other.second", + "get other.second.valueOf", + "call other.second.valueOf", + "get other.timeZone", + "get other.year", + "get other.year.valueOf", + "call other.year.valueOf", + "has other.timeZone.getOffsetNanosecondsFor", + "has other.timeZone.getPossibleInstantsFor", + "has other.timeZone.id", + "call other.calendar.dateFromFields", + "get other.timeZone.getOffsetNanosecondsFor", + "get other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getPossibleInstantsFor", + "call other.timeZone.getOffsetNanosecondsFor", + // NOTE: extra because of wall-clock time ambiguity: + "call other.timeZone.getOffsetNanosecondsFor", + // CalendarEquals + "get this.calendar.id", + "get other.calendar.id", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.roundingIncrement", + "get options.roundingIncrement", + "getOwnPropertyDescriptor options.roundingMode", + "get options.roundingMode", + "getOwnPropertyDescriptor options.largestUnit", + "get options.largestUnit", + "getOwnPropertyDescriptor options.smallestUnit", + "get options.smallestUnit", + "getOwnPropertyDescriptor options.additional", + "get options.additional", + // GetDifferenceSettings + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // TimeZoneEquals + "get this.timeZone.id", + "get other.timeZone.id", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + "get this.calendar.dateUntil", + // DifferenceZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", +], "order of operations with identical wall-clock times and largestUnit a calendar unit"); +actual.splice(0); // clear + +// Making largestUnit a calendar unit adds the following observable operations: +const expectedOpsForCalendarDifference = [ + // TimeZoneEquals + "get this.timeZone.id", + "get other.timeZone.id", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "get this.calendar.dateAdd", + "get this.calendar.dateUntil", + // precalculate PlainDateTime + "call this.timeZone.getOffsetNanosecondsFor", + // DifferenceZonedDateTime + "call this.timeZone.getOffsetNanosecondsFor", + // DifferenceISODateTime + "call this.calendar.dateUntil", + // AddZonedDateTime + "call this.calendar.dateAdd", + "call this.timeZone.getPossibleInstantsFor", + // NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]; + +const expectedOpsForCalendarRounding = [ + // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime + "call this.calendar.dateAdd", + "call this.timeZone.getPossibleInstantsFor", + // RoundDuration → NanosecondsToDays + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime + "call this.timeZone.getPossibleInstantsFor", +]; + +// code path that skips RoundDuration: +instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 })); +assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest year: +const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateAdd", // 12.d + "call this.calendar.dateAdd", // 12.f + "call this.calendar.dateUntil", // 12.n + "call this.calendar.dateAdd", // 12.x MoveRelativeDate + // (12.r not called because other units can't add up to >1 year at this point) + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 9.c + "call this.calendar.dateUntil" // 9.d +]); +instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" })); +assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest month: +const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateAdd", // 13.c + "call this.calendar.dateAdd", // 13.e + "call this.calendar.dateUntil", // 13.m + "call this.calendar.dateAdd", // 13.w MoveRelativeDate + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 10.d + "call this.calendar.dateUntil", // 10.e +]); +instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" })); +assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest week: +const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [ + // RoundDuration + "call this.calendar.dateUntil", // 14.f + "call this.calendar.dateAdd", // 14.p MoveRelativeDate + // BalanceDateDurationRelative + "call this.calendar.dateAdd", // 16 + "call this.calendar.dateUntil", // 17 +]); +instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" })); +assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks"); +actual.splice(0); // clear + +instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" })); +assert.compareArray(actual, expected, "order of operations with largestUnit being a time unit"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js new file mode 100644 index 0000000000..b2752f131d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The "until" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.until, + "function", + "`typeof ZonedDateTime.prototype.until` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "until", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js new file mode 100644 index 0000000000..969bbcb955 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/proto-in-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const timeZone = 'Europe/Paris' +const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: timeZone }; +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +assert.throws(RangeError, () => instance.until(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..834a70f798 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/read-time-fields-before-datefromfields.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.prototype.until step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 2.e: + e. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); +const duration = datetime.until({ year: 2001, month: 9, day: 9, timeZone: "UTC", calendar }); + +TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -1, -46, -40, -987, -654, -321); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js new file mode 100644 index 0000000000..02b3c0cc07 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/round-cross-unit-boundary.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Rounding can cross unit boundaries up to largestUnit +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Date units +{ + const earlier = new Temporal.ZonedDateTime(1640995200_000_000_000n /* = 2022-01-01T00 */, "UTC"); + const later = new Temporal.ZonedDateTime(1703462400_000_000_000n /* = 2023-12-25T00 */, "UTC"); + const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "months", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "1 year 11 months balances to 2 years"); +} + +// Time units +{ + const earlier = new Temporal.ZonedDateTime(0n, "UTC"); + const later = new Temporal.ZonedDateTime(7199_000_000_000n, "UTC"); + const duration = earlier.until(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, "1:59 balances to 2 hours"); +} + +// Both +{ + const earlier = new Temporal.ZonedDateTime(0n, "UTC"); + const later = new Temporal.ZonedDateTime(63071999_999_999_999n /* = 1971-12-31T23:59:59.999999999 */, "UTC"); + const duration = earlier.until(later, { largestUnit: "years", smallestUnit: "microseconds", roundingMode: "expand" }); + TemporalHelpers.assertDuration(duration, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounding up 1 ns balances to 2 years"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js new file mode 100644 index 0000000000..c8990eb9b3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/rounding-zero-year-month-week-length.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: > + A malicious calendar resulting in a year, month, or week length of zero is + handled correctly +info: | + RoundDuration + 10.z. If _oneYearDays_ = 0, throw a *RangeError* exception. + ... + 11.z. If _oneMonthDays_ = 0, throw a *RangeError* exception. + ... + 12.s. If _oneWeekDays_ = 0, throw a *RangeError* exception. +features: [Temporal] +---*/ + +const cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Called several times, last call sets oneYear/Month/WeekDays to 0 + return new Temporal.PlainDate(1970, 1, 1); + } +}("iso8601"); + +const dt1 = new Temporal.ZonedDateTime(0n, "UTC", cal); +const dt2 = new Temporal.ZonedDateTime(365n * 86400_000_000_000n + 1n, "UTC", cal); + +assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "years" }), "zero year length handled correctly"); +assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "months" }), "zero month length handled correctly"); +assert.throws(RangeError, () => dt1.until(dt2, { smallestUnit: "weeks" }), "zero week length handled correctly"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js new file mode 100644 index 0000000000..d5e6a9ffda --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-nan.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when roundingIncrement option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.until step 12: + 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js new file mode 100644 index 0000000000..c986890d69 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-non-integer.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Rounding for roundingIncrement option +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" }); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2"); +const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" }); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js new file mode 100644 index 0000000000..01b093aa0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-out-of-range.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when roundingIncrement option out of range +info: | + ToTemporalRoundingIncrement ( _normalizedOptions_ ) + + 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>𝔽</sub>). + 2. If _increment_ is not finite, throw a *RangeError* exception. + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. + 5. Return _integerIncrement_. +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC"); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -Infinity })); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: -1 })); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0 })); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 0.9 })); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: 1e9 + 1 })); +assert.throws(RangeError, () => earlier.until(later, { roundingIncrement: Infinity })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js new file mode 100644 index 0000000000..fe1ee88506 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fallback value for roundingIncrement option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.until step 12: + 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); + +const explicit = earlier.until(later, { roundingIncrement: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1"); + +const implicit = earlier.until(later, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, "default roundingIncrement is 1"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js new file mode 100644 index 0000000000..a481e25f28 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingincrement-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Type conversions for roundingIncrement option +info: | + sec-getoption step 8.a: + a. Set _value_ to ? ToNumber(value). + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.zoneddatetime.prototype.until step 12: + 12. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_988_655_322n, "UTC"); + +TemporalHelpers.checkRoundingIncrementOptionWrongType( + (roundingIncrement) => earlier.until(later, { roundingIncrement }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 1, descr), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 1, 1, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js new file mode 100644 index 0000000000..5a3e5d1ac9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-ceil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "ceil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-2]], + ["months", [0, 32], [0, -31]], + ["weeks", [0, 0, 140], [0, 0, -139]], + ["days", [0, 0, 0, 974], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -4]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "ceil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js new file mode 100644 index 0000000000..28c3a35162 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-expand.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "expand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 140], [0, 0, -140]], + ["days", [0, 0, 0, 974], [0, 0, 0, -974]], + ["hours", [0, 0, 0, 0, 23357], [0, 0, 0, 0, -23357]], + ["minutes", [0, 0, 0, 0, 23356, 18], [0, 0, 0, 0, -23356, -18]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 865], [0, 0, 0, 0, -23356, -17, -4, -865]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "expand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js new file mode 100644 index 0000000000..1abcf3b687 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-floor.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "floor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [2], [-3]], + ["months", [0, 31], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -140]], + ["days", [0, 0, 0, 973], [0, 0, 0, -974]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23357]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -18]], + ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -865]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "floor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js new file mode 100644 index 0000000000..e8a89b9dad --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfCeil.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "halfCeil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfCeil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js new file mode 100644 index 0000000000..f109364f85 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfEven.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "halfEven". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfEven"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js new file mode 100644 index 0000000000..5036a3287c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfExpand.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "halfExpand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 198], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfExpand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js new file mode 100644 index 0000000000..4bca263cd9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfFloor.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "halfFloor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -198]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfFloor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js new file mode 100644 index 0000000000..6d489a21c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-halfTrunc.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "halfTrunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [3], [-3]], + ["months", [0, 32], [0, -32]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 5], [0, 0, 0, 0, -23356, -17, -5]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "halfTrunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js new file mode 100644 index 0000000000..82c969ab77 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-invalid-string.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => earlier.until(later, { smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js new file mode 100644 index 0000000000..10dd33e8db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-trunc.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Tests calculations with roundingMode "trunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1546935756_123_456_789n /* 2019-01-08T08:22:36.123456789+00:00 */, "UTC"); +const later = new Temporal.ZonedDateTime(1631018380_987_654_289n /* 2021-09-07T12:39:40.987654289+00:00 */, "UTC"); + +const expected = [ + ["years", [2], [-2]], + ["months", [0, 31], [0, -31]], + ["weeks", [0, 0, 139], [0, 0, -139]], + ["days", [0, 0, 0, 973], [0, 0, 0, -973]], + ["hours", [0, 0, 0, 0, 23356], [0, 0, 0, 0, -23356]], + ["minutes", [0, 0, 0, 0, 23356, 17], [0, 0, 0, 0, -23356, -17]], + ["seconds", [0, 0, 0, 0, 23356, 17, 4], [0, 0, 0, 0, -23356, -17, -4]], + ["milliseconds", [0, 0, 0, 0, 23356, 17, 4, 864], [0, 0, 0, 0, -23356, -17, -4, -864]], + ["microseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197], [0, 0, 0, 0, -23356, -17, -4, -864, -197]], + ["nanoseconds", [0, 0, 0, 0, 23356, 17, 4, 864, 197, 500], [0, 0, 0, 0, -23356, -17, -4, -864, -197, -500]], +]; + +const roundingMode = "trunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + earlier.until(later, { smallestUnit, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + later.until(earlier, { smallestUnit, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js new file mode 100644 index 0000000000..8c797f1cd4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-undefined.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fallback value for roundingMode option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); + +const explicit1 = earlier.until(later, { smallestUnit: "microsecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc"); +const implicit1 = earlier.until(later, { smallestUnit: "microsecond" }); +TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, "default roundingMode is trunc"); + +const explicit2 = earlier.until(later, { smallestUnit: "millisecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc"); +const implicit2 = earlier.until(later, { smallestUnit: "millisecond" }); +TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 25, 1, 1, 123, 0, 0, "default roundingMode is trunc"); + +const explicit3 = earlier.until(later, { smallestUnit: "second", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc"); +const implicit3 = earlier.until(later, { smallestUnit: "second" }); +TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 25, 1, 1, 0, 0, 0, "default roundingMode is trunc"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js new file mode 100644 index 0000000000..cc073f9165 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/roundingmode-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_123_987_500n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc", + (roundingMode) => earlier.until(later, { smallestUnit: "microsecond", roundingMode }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 123, 987, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js new file mode 100644 index 0000000000..c875186a93 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => earlier.until(later, { smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..9ce243e4e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const later = new Temporal.ZonedDateTime(1_086_403_661_988_655_322n, "UTC"); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => earlier.until(later, { smallestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js new file mode 100644 index 0000000000..86614c4af8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Fallback value for smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); + +const explicit = earlier.until(later, { smallestUnit: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond"); +const implicit = earlier.until(later, {}); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 25, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js new file mode 100644 index 0000000000..20536d6f0c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/smallestunit-wrong-type.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const later = new Temporal.ZonedDateTime(1_000_090_061_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => earlier.until(later, { smallestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 25, 1, 1, 987, 654, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..74c097a2df --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..8ce2f12932 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.until(other, { largestUnit: "days" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..a1887854a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(RangeError, () => datetime.until(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..72e8b1ed59 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const other = new Temporal.ZonedDateTime(1_100_000_000_123_456_789n, timeZone); + assert.throws(TypeError, () => datetime.until(other, { largestUnit: "days" })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..ed8b71c90e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.until step 3: + 3. Set _other_ to ? ToTemporalZonedDateTime(_other_). + sec-temporal-totemporalzoneddatetime step 7: + 7. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Not called on the instance's time zone + +const expected1 = []; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.until({ year: 2005, month: 6, day: 2, timeZone: "UTC" }); +}, expected1); + +// Called on the argument's time zone + +const expected2 = [ + "2005-06-02T00:00:00", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + datetime.until({ year: 2005, month: 6, day: 2, timeZone }); +}, expected2); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js new file mode 100644 index 0000000000..a62bcbbfa0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/year-zero.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-0000000-01-01T00:02Z[UTC]", + "-0000000-01-01T00:02+00:00[UTC]", + "-0000000-01-01T00:02:00.000000000+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.until(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js new file mode 100644 index 0000000000..135a441fac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string-multiple-offsets.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Sub-minute offset trailing zeroes allowed in ISO string but not in bracketed offset +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("+01:35"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +let str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35]"; + +const result = instance.until(str); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, "ISO offset, sub-minute offset trailing-zeroes"); + +str = "1970-01-01T01:35:30+01:35:00.000000000[+01:35:00.000000000]"; +assert.throws( + RangeError, + () => instance.until(str), + "Trailing zeroes not allowed for sub-minute time zone identifiers" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js new file mode 100644 index 0000000000..59a371d40d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/until/zoneddatetime-string.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.until +description: Conversion of ISO date-time strings to Temporal.ZonedDateTime instances +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +let str = "1970-01-01T00:00"; +assert.throws(RangeError, () => instance.until(str), "bare date-time string is not a ZonedDateTime"); +str = "1970-01-01T00:00Z"; +assert.throws(RangeError, () => instance.until(str), "date-time + Z is not a ZonedDateTime"); +str = "1970-01-01T00:00+01:00"; +assert.throws(RangeError, () => instance.until(str), "date-time + offset is not a ZonedDateTime"); + +str = "1970-01-01T00:00[+01:00]"; +const result1 = instance.until(str); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + IANA annotation preserves wall time in the time zone"); + +str = "1970-01-01T00:00Z[+01:00]"; +const result2 = instance.until(str); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation preserves exact time in the time zone"); + +str = "1970-01-01T00:00+01:00[+01:00]"; +const result3 = instance.until(str); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation ensures both exact and wall time match"); + +str = "1970-01-01T00:00-04:15[+01:00]"; +assert.throws(RangeError, () => instance.until(str), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js new file mode 100644 index 0000000000..2be86ffaa6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/basic.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: Basic tests for valueOf(). +features: [Temporal] +---*/ + +const zonedDateTime = new Temporal.ZonedDateTime(100n, "UTC"); +const zonedDateTime2 = new Temporal.ZonedDateTime(987654321n, "UTC"); + +assert.throws(TypeError, () => zonedDateTime.valueOf(), "valueOf"); +assert.throws(TypeError, () => zonedDateTime < zonedDateTime, "<"); +assert.throws(TypeError, () => zonedDateTime <= zonedDateTime, "<="); +assert.throws(TypeError, () => zonedDateTime > zonedDateTime, ">"); +assert.throws(TypeError, () => zonedDateTime >= zonedDateTime, ">="); +assert.sameValue(zonedDateTime === zonedDateTime, true, "==="); +assert.sameValue(zonedDateTime === zonedDateTime2, false, "==="); +assert.sameValue(zonedDateTime !== zonedDateTime, false, "!=="); +assert.sameValue(zonedDateTime !== zonedDateTime2, true, "!=="); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js new file mode 100644 index 0000000000..432906a228 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const valueOf = Temporal.ZonedDateTime.prototype.valueOf; + +assert.sameValue(typeof valueOf, "function"); + +assert.throws(TypeError, () => valueOf.call(undefined), "undefined"); +assert.throws(TypeError, () => valueOf.call(null), "null"); +assert.throws(TypeError, () => valueOf.call(true), "true"); +assert.throws(TypeError, () => valueOf.call(""), "empty string"); +assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol"); +assert.throws(TypeError, () => valueOf.call(1), "1"); +assert.throws(TypeError, () => valueOf.call({}), "plain object"); +assert.throws(TypeError, () => valueOf.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => valueOf.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js new file mode 100644 index 0000000000..41a92e9ba0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: > + Tests that Temporal.ZonedDateTime.prototype.valueOf + 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.ZonedDateTime.prototype.valueOf), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.valueOf), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.valueOf), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.valueOf.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js new file mode 100644 index 0000000000..8a9e4da58e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: Temporal.ZonedDateTime.prototype.valueOf.length is 0 +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.ZonedDateTime.prototype.valueOf, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js new file mode 100644 index 0000000000..a3d0a305e0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: Temporal.ZonedDateTime.prototype.valueOf.name is "valueOf". +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.ZonedDateTime.prototype.valueOf, "name", { + value: "valueOf", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js new file mode 100644 index 0000000000..8c48ecb23a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: > + Temporal.ZonedDateTime.prototype.valueOf 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.ZonedDateTime.prototype.valueOf(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.valueOf), false, + "isConstructor(Temporal.ZonedDateTime.prototype.valueOf)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js new file mode 100644 index 0000000000..01f09f4692 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.valueof +description: The "valueOf" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.valueOf, + "function", + "`typeof ZonedDateTime.prototype.valueOf` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "valueOf", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/valueOf/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.js new file mode 100644 index 0000000000..3b78894aef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const weekOfYear = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "weekOfYear").get; + +assert.sameValue(typeof weekOfYear, "function"); + +assert.throws(TypeError, () => weekOfYear.call(undefined), "undefined"); +assert.throws(TypeError, () => weekOfYear.call(null), "null"); +assert.throws(TypeError, () => weekOfYear.call(true), "true"); +assert.throws(TypeError, () => weekOfYear.call(""), "empty string"); +assert.throws(TypeError, () => weekOfYear.call(Symbol()), "symbol"); +assert.throws(TypeError, () => weekOfYear.call(1), "1"); +assert.throws(TypeError, () => weekOfYear.call({}), "plain object"); +assert.throws(TypeError, () => weekOfYear.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => weekOfYear.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..f8207ff213 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.weekofyear +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const weekOfYearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "weekOfYear"); +Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("weekOfYear should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.weekOfYear; + +Object.defineProperty(Temporal.Calendar.prototype, "weekOfYear", weekOfYearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..f4c6165031 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.weekofyear +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.weekOfYear; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js new file mode 100644 index 0000000000..20192188ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: Custom calendar tests for weekOfYear(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + weekOfYear(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "weekOfYear arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.weekOfYear; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js new file mode 100644 index 0000000000..524cf17210 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: The "weekOfYear" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "weekOfYear"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..dc62383bce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.weekOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..8c366c3818 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.weekOfYear, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..4f131781ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.weekOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7b22299451 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.weekOfYear); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js new file mode 100644 index 0000000000..8b18332ce4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/weekOfYear/validate-calendar-value.js @@ -0,0 +1,41 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.weekofyear +description: Validate result returned from calendar weekOfYear() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [null, TypeError], + [false, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [NaN, RangeError], + [-7, RangeError], + [-0.1, RangeError], + ["string", TypeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [{}, TypeError], + [true, TypeError], + [7.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + weekOfYear() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.weekOfYear, `${typeof result} ${String(result)} not converted to positive integer`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js new file mode 100644 index 0000000000..e7935824fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/balance-negative-time-units.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Negative time fields are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-addtime step 8: + 8. Return ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-adddatetime step 1: + 1. Let _timeResult_ be ? AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-builtintimezonegetinstantfor step 13.a: + a. Let _earlier_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, 0, 0, 0, 0, −_nanoseconds_, *"constrain"*). + sec-temporal-interpretisodatetimeoffset steps 4–10: + 4. If _offsetNanoseconds_ is *null*, or _offset_ is *"ignore"*, then + a. Let _instant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _dateTime_, _disambiguation_). + ... + ... + 6. Assert: _offset_ is *"prefer"* or *"reject"*. + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + ... + 9. If _offset_ is *"reject"*, throw a *RangeError* exception. + 10. Let _instant_ be ? DisambiguatePossibleInstants(_possibleInstants_, _timeZone_, _dateTime_, _disambiguation_). + sec-temporal.zoneddatetime.prototype.with step 26: + 26. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], _dateTimeResult_.[[Month]], _dateTimeResult_.[[Day]], _dateTimeResult_.[[Hour]], _dateTimeResult_.[[Minute]], _dateTimeResult_.[[Second]], _dateTimeResult_.[[Millisecond]], _dateTimeResult_.[[Microsecond]], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const shiftInstant = new Temporal.Instant(3661_001_001_001n); +const tz1 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); +const datetime1 = new Temporal.ZonedDateTime(3661_001_001_000n, tz1); + +// This code path is encountered if offset is `ignore` or `prefer`, +// disambiguation is `earlier` and the shift is a spring-forward change +datetime1.with({ nanosecond: 1 }, { offset: "ignore", disambiguation: "earlier" }); + +const expected = [ + "1970-01-01T01:01:01.001001001", + "1970-01-01T01:01:01.001000999", +]; +assert.compareArray(tz1.getPossibleInstantsForCalledWith, expected); + +const tz2 = TemporalHelpers.oneShiftTimeZone(shiftInstant, 2); +const datetime2 = new Temporal.ZonedDateTime(3661_001_001_000n, tz2); + +datetime2.with({ nanosecond: 1 }, { offset: "prefer", disambiguation: "earlier" }); + +assert.compareArray(tz2.getPossibleInstantsForCalledWith, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js new file mode 100644 index 0000000000..089d357535 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const with_ = Temporal.ZonedDateTime.prototype.with; + +assert.sameValue(typeof with_, "function"); + +const args = [{ year: 2022 }]; + +assert.throws(TypeError, () => with_.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => with_.apply(null, args), "null"); +assert.throws(TypeError, () => with_.apply(true, args), "true"); +assert.throws(TypeError, () => with_.apply("", args), "empty string"); +assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => with_.apply(1, args), "1"); +assert.throws(TypeError, () => with_.apply({}, args), "plain object"); +assert.throws(TypeError, () => with_.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => with_.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..29b963ecf8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-array-iteration.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ day: 5 }); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..0d3f1edc2f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-calendar-no-observable-calls.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const fieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "fields"); +Object.defineProperty(Temporal.Calendar.prototype, "fields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("fields should not be looked up"); + }, +}); +const mergeFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "mergeFields"); +Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("mergeFields should not be looked up"); + }, +}); +const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields"); +Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("dateFromFields should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ year: 2001 }); + +Object.defineProperty(Temporal.Calendar.prototype, "fields", fieldsOriginal); +Object.defineProperty(Temporal.Calendar.prototype, "mergeFields", mergeFieldsOriginal); +Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..d40d49f7fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.with({ year: 2001 }); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js new file mode 100644 index 0000000000..c18d00211d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Tests that Temporal.ZonedDateTime.prototype.with + 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.ZonedDateTime.prototype.with), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.with), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.with), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.with.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js new file mode 100644 index 0000000000..7993c23c19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fields-iterable.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.with step 9: + 9. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.with({ year: 2005 }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..35d0c27f9f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-fromfields-called-with-null-prototype-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +instance.with({ day: 24 }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js new file mode 100644 index 0000000000..7f1e187cbf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-merge-fields-returns-primitive.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + with() should throw a TypeError if mergeFields() returns a primitive, + without passing the value on to any other calendar methods +includes: [compareArray.js, temporalHelpers.js] +features: [BigInt, Symbol, Temporal] +---*/ + +[undefined, null, true, 3.14159, "bad value", Symbol("no"), 7n].forEach((primitive) => { + const calendar = TemporalHelpers.calendarMergeFieldsReturnsPrimitive(primitive); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(TypeError, () => instance.with({ year: 2005 }), "bad return from mergeFields() throws"); + assert.sameValue(calendar.dateFromFieldsCallCount, 0, "dateFromFields() never called"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..6a75d5ce22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-mergefields-called-with-null-prototype-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Calendar.mergeFields method is called with null-prototype fields objects +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckMergeFieldsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(0n, "UTC", calendar); +instance.with({ day: 24 }); +assert.sameValue(calendar.mergeFieldsCallCount, 1, "mergeFields should have been called on the calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js new file mode 100644 index 0000000000..2daa8e28ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/calendar-options.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + The options argument is copied and the copy is passed to + Calendar#dateFromFields. +features: [Temporal] +---*/ + +const options = { + extra: "property", +}; +let calledDateFromFields = 0; +class Calendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateFromFields(fields, optionsArg) { + ++calledDateFromFields; + assert.notSameValue(optionsArg, options, "should pass copied options object"); + assert.sameValue(optionsArg.extra, "property", "should copy all properties from options object"); + assert.sameValue(Object.getPrototypeOf(optionsArg), null, "Copy has null prototype"); + return super.dateFromFields(fields, optionsArg); + } +}; +const calendar = new Calendar(); +const datetime = new Temporal.ZonedDateTime(0n, "UTC", calendar); +const result = datetime.with({ year: 1971 }, options); +assert.sameValue(result.epochNanoseconds, 365n * 86400_000_000_000n, "year changed from 1970 to 1971") +assert.sameValue(calledDateFromFields, 1, "should have called overridden dateFromFields once"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js new file mode 100644 index 0000000000..640cd8d689 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/constructor-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js new file mode 100644 index 0000000000..dc159e00f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copies-merge-fields-object.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.with +description: The object returned from mergeFields() is copied before being passed to dateFromFields(). +info: | + sec-temporal.plaindatetime.prototype.with steps 18–19 and 23: + 18. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDate_). + 19. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"timeZone"* »). + 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields step 2: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get day", + "get day.valueOf", + "call day.valueOf", + "get hour", + "get hour.valueOf", + "call hour.valueOf", + "get microsecond", + "get microsecond.valueOf", + "call microsecond.valueOf", + "get millisecond", + "get millisecond.valueOf", + "call millisecond.valueOf", + "get minute", + "get minute.valueOf", + "call minute.valueOf", + "get month", + "get month.valueOf", + "call month.valueOf", + "get monthCode", + "get monthCode.toString", + "call monthCode.toString", + "get nanosecond", + "get nanosecond.valueOf", + "call nanosecond.valueOf", + "get offset", + "get offset.toString", + "call offset.toString", + "get second", + "get second.valueOf", + "call second.valueOf", + "get year", + "get year.valueOf", + "call year.valueOf", +]; + +const calendar = TemporalHelpers.calendarMergeFieldsGetters(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +datetime.with({ year: 2022 }); + +assert.compareArray(calendar.mergeFieldsReturnOperations, expected, "getters called on mergeFields return"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js new file mode 100644 index 0000000000..6afaf10645 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/copy-properties-not-undefined.js @@ -0,0 +1,52 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: PreparePartialTemporalFields copies only defined properties of source object +info: | + 4. For each value _property_ of _fieldNames_, do + a. Let _value_ be ? Get(_fields_, _property_). + b. If _value_ is not *undefined*, then + ... + iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_). +features: [Temporal] +---*/ + +const d1 = new Temporal.ZonedDateTime(1_000_000_000_000_000_789n, "UTC"); + +const d2 = d1.with({ day: 1, hour: 10, year: undefined }); + +assert.sameValue(d2.year, 2001, + "only the properties that are present and defined in the plain object are copied (year value)" +); + +assert.sameValue(d2.month, 9, + "only the properties that are present and defined in the plain object are copied (month value)" +); + +assert.sameValue(d2.day, 1, + "only the properties that are present and defined in the plain object are copied (day value)" +); + +assert.sameValue(d2.hour, 10, + "only the properties that are present and defined in the plain object are copied (hour value)" +); +assert.sameValue(d2.minute, 46, + "only the properties that are present and defined in the plain object are copied (minute value)" +); +assert.sameValue(d2.second, 40, + "only the properties that are present and defined in the plain object are copied (second value)" +); +assert.sameValue(d2.millisecond, 0, + "only the properties that are present and defined in the plain object are copied (millisecond value)" +); +assert.sameValue(d2.microsecond, 0, + "only the properties that are present and defined in the plain object are copied (microsecond value)" +); +assert.sameValue(d2.nanosecond, 789, + "only the properties that are present and defined in the plain object are copied (nanosecond value)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js new file mode 100644 index 0000000000..8cf415e441 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-invalid-string.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.with +description: RangeError thrown when disambiguation option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => datetime.with({ hour: 2 }, { disambiguation: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js new file mode 100644 index 0000000000..e81165eeb7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-undefined.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Fallback value for disambiguation option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const springForwardDatetime = new Temporal.ZonedDateTime(954702001_000_000_000n, timeZone); +const fallBackDatetime = new Temporal.ZonedDateTime(972849601_000_000_000n, timeZone); +const offset = "ignore"; + +[ + [springForwardDatetime, { hour: 2, minute: 30 }, 954671401_000_000_000n], + [fallBackDatetime, { hour: 1, minute: 30 }, 972808201_000_000_000n], +].forEach(([datetime, fields, expected]) => { + const explicit = datetime.with(fields, { offset, disambiguation: undefined }); + assert.sameValue(explicit.epochNanoseconds, expected, "default disambiguation is compatible"); + const implicit = datetime.with(fields, { offset }); + assert.sameValue(implicit.epochNanoseconds, expected, "default disambiguation is compatible"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js new file mode 100644 index 0000000000..90358a2752 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/disambiguation-wrong-type.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Type conversions for disambiguation option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaldisambiguation step 1: + 1. Return ? GetOption(_normalizedOptions_, *"disambiguation"*, « String », « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). + sec-temporal.zoneddatetime.protoype.with step 14: + 14. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("disambiguation", "compatible", + (disambiguation) => datetime.with({ hour: 2 }, { disambiguation }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js new file mode 100644 index 0000000000..bcf2046f1c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/duplicate-calendar-fields.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['monthCode'], ['day'], ['hour'], ['microsecond'], ['millisecond'], ['minute'], ['month'], ['nanosecond'], ['second'], ['year'], ['offset']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + + assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..67a199f72f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); + +for (const disambiguation of ["earlier", "later", "compatible"]) { + const timeZone = new SkippedDateTime(); + const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); + + instance.with({ day: 1 }, { disambiguation }); + + assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..2dc6beff59 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/infinity-throws-rangeerror.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.prototype.with +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + ["constrain", "reject"].forEach((overflow) => { + assert.throws(RangeError, () => instance.with({ [prop]: inf }, { overflow }), `${prop} property cannot be ${inf} (overflow ${overflow}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.with({ [prop]: obj }, { overflow })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js new file mode 100644 index 0000000000..89ac918633 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Temporal.ZonedDateTime.prototype.with.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.ZonedDateTime.prototype.with, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js new file mode 100644 index 0000000000..09ebb9c90b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/minimum-instant-with-one-hour-offset.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError when ZonedDateTime at minimum instant and an explicit +1h offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(-86_40000_00000_00000_00000n, "UTC"); + +let temporalZonedDateTimeLike = { + offset: "+01", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js new file mode 100644 index 0000000000..cfff0818fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Temporal.ZonedDateTime.prototype.with.name is "with". +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.ZonedDateTime.prototype.with, "name", { + value: "with", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js new file mode 100644 index 0000000000..6487ca2e44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Temporal.ZonedDateTime.prototype.with 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.ZonedDateTime.prototype.with(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.with), false, + "isConstructor(Temporal.ZonedDateTime.prototype.with)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js new file mode 100644 index 0000000000..d099f8857a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-invalid-string.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.with +description: RangeError thrown when offset option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(RangeError, () => datetime.with({ hour: 2 }, { offset: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js new file mode 100644 index 0000000000..cc48282502 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-property-invalid-string.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const offsetOptions = ['use', 'prefer', 'ignore', 'reject']; + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // must be a string + null, // must be a string + true, // must be a string + 1000n, // must be a string +]; +offsetOptions.forEach((offsetOption) => { + badOffsets.forEach((offset) => { + assert.throws( + typeof(offset) === 'string' ? RangeError : TypeError, + () => instance.with({ offset }, { offset: offsetOption }), + `"${offset} is not a valid offset string (with ${offsetOption} offset option)`, + ); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js new file mode 100644 index 0000000000..f48b9c750e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-undefined.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Fallback value for offset option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("-03:30"); + +const datetime = new Temporal.ZonedDateTime(1572757201_000_000_000n, timeZone); +const explicit = datetime.with({ minute: 31 }, { offset: undefined }); +assert.sameValue(explicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer"); +const implicit = datetime.with({ minute: 31 }, {}); +assert.sameValue(implicit.epochNanoseconds, 1572757261_000_000_000n, "default offset is prefer"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js new file mode 100644 index 0000000000..af450155ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/offset-wrong-type.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Type conversions for offset option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloffset step 1: + 1. Return ? GetOption(_normalizedOptions_, *"offset"*, « String », « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _fallback_). + sec-temporal.zoneddatetime.protoype.with step 15: + 15. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +TemporalHelpers.checkStringOptionWrongType("offset", "prefer", + (offset) => datetime.with({ hour: 2 }, { offset }), + (result, descr) => assert.sameValue(result.epochNanoseconds, 1_000_003_600_987_654_321n, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js new file mode 100644 index 0000000000..9674f868a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-object.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const result1 = instance.with({ day: 5 }, {}); +assert.sameValue( + result1.epochNanoseconds, 345600000000000n, "UTC", + "options may be an empty plain object" +); + +const result2 = instance.with({ day: 5 }, () => {}); +assert.sameValue( + result2.epochNanoseconds, 345600000000000n, "UTC", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js new file mode 100644 index 0000000000..e648410e0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-undefined.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Verify that undefined options are handled correctly. +features: [BigInt, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(949494896_987_654_321n, "UTC"); +const fields = { day: 31 }; + +const explicit = datetime.with(fields, undefined); +assert.sameValue(explicit.month, 2, "default overflow is constrain"); +assert.sameValue(explicit.day, 29, "default overflow is constrain"); + +const implicit = datetime.with(fields); +assert.sameValue(implicit.month, 2, "default overflow is constrain"); +assert.sameValue(implicit.day, 29, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js new file mode 100644 index 0000000000..94ea786d19 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.with({ day: 5 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js new file mode 100644 index 0000000000..18387ceb9a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js @@ -0,0 +1,177 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Properties on objects passed to with() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // RejectObjectWithCalendarOrTimeZone + "get fields.calendar", + "get fields.timeZone", + // CopyDataProperties + "ownKeys options", + "getOwnPropertyDescriptor options.overflow", + "get options.overflow", + "getOwnPropertyDescriptor options.disambiguation", + "get options.disambiguation", + "getOwnPropertyDescriptor options.offset", + "get options.offset", + "getOwnPropertyDescriptor options.extra", + "get options.extra", + // lookup + "get this.calendar.dateFromFields", + "get this.calendar.fields", + "get this.calendar.mergeFields", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetOffsetNanosecondsFor on receiver + "call this.timeZone.getOffsetNanosecondsFor", + // CalendarFields + "call this.calendar.fields", + // PrepareTemporalFields on receiver + "get this.calendar.day", + "call this.calendar.day", + "get this.calendar.month", + "call this.calendar.month", + "get this.calendar.monthCode", + "call this.calendar.monthCode", + "get this.calendar.year", + "call this.calendar.year", + // PrepareTemporalFields on argument + "get fields.day", + "get fields.day.valueOf", + "call fields.day.valueOf", + "get fields.hour", + "get fields.hour.valueOf", + "call fields.hour.valueOf", + "get fields.microsecond", + "get fields.microsecond.valueOf", + "call fields.microsecond.valueOf", + "get fields.millisecond", + "get fields.millisecond.valueOf", + "call fields.millisecond.valueOf", + "get fields.minute", + "get fields.minute.valueOf", + "call fields.minute.valueOf", + "get fields.month", + "get fields.month.valueOf", + "call fields.month.valueOf", + "get fields.monthCode", + "get fields.monthCode.toString", + "call fields.monthCode.toString", + "get fields.nanosecond", + "get fields.nanosecond.valueOf", + "call fields.nanosecond.valueOf", + "get fields.offset", + "get fields.offset.toString", + "call fields.offset.toString", + "get fields.second", + "get fields.second.valueOf", + "call fields.second.valueOf", + "get fields.year", + "get fields.year.valueOf", + "call fields.year.valueOf", + // CalendarMergeFields + "call this.calendar.mergeFields", + // InterpretTemporalDateTimeFields + "get options.disambiguation.toString", + "call options.disambiguation.toString", + "get options.offset.toString", + "call options.offset.toString", + "get options.overflow.toString", + "call options.overflow.toString", + "call this.calendar.dateFromFields", + // InterpretISODateTimeOffset + "call this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getOffsetNanosecondsFor", +]; +const actual = []; + +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone"); +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.ZonedDateTime(0n, timeZone, calendar); +// clear observable operations that occurred during the constructor call +actual.splice(0); + +const fields = TemporalHelpers.propertyBagObserver(actual, { + year: 1.7, + month: 1.7, + monthCode: "M01", + day: 1.7, + hour: 1.7, + minute: 1.7, + second: 1.7, + millisecond: 1.7, + microsecond: 1.7, + nanosecond: 1.7, + offset: "+00:00", +}, "fields"); + +const options = TemporalHelpers.propertyBagObserver(actual, { + overflow: "constrain", + disambiguation: "compatible", + offset: "prefer", + extra: "property", +}, "options"); + +instance.with(fields, options); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); + +const dstInstance = new Temporal.ZonedDateTime(37800_000_000_000n /* 1970-01-01T02:30-08:00 */, dstTimeZoneObserver, calendar); +actual.splice(0); // clear calls that happened in constructor + +const fallBackFields = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", // ignored +}, "fields"); +dstInstance.with(fallBackFields, options); +assert.compareArray(actual, expected.concat([ + // extra call in InterpretISODateTimeOffset + "call this.timeZone.getOffsetNanosecondsFor", +]), "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +const springForwardFields = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 4, + monthCode: "M04", + day: 2, + hour: 2, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", // ignored +}, "fields"); +dstInstance.with(springForwardFields, options); +assert.compareArray(actual, expected.concat([ + // DisambiguatePossibleInstants + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js new file mode 100644 index 0000000000..8e2be9a65f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-invalid-string.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: RangeError thrown when overflow option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const badOverflows = ["", "CONSTRAIN", "balance", "other string", "constra\u0131n", "reject\0"]; +for (const overflow of badOverflows) { + assert.throws( + RangeError, + () => datetime.with({ minute: 45 }, { overflow }), + `invalid overflow ("${overflow}")` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js new file mode 100644 index 0000000000..17e7dd007f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-undefined.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Fallback value for overflow option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +const explicit = datetime.with({ second: 67 }, { overflow: undefined }); +assert.sameValue(explicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain"); +const implicit = datetime.with({ second: 67 }, {}); +assert.sameValue(implicit.epochNanoseconds, 1_000_000_019_987_654_321n, "default overflow is constrain"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js new file mode 100644 index 0000000000..1ce7632251 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/overflow-wrong-type.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaindatetime.prototype.with +description: Type conversions for overflow option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaloverflow step 1: + 1. Return ? GetOption(_normalizedOptions_, *"overflow"*, « String », « *"constrain"*, *"reject"* », *"constrain"*). + sec-temporal-interprettemporaldatetimefields steps 2–3: + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). + 3. Let _overflow_ be ? ToTemporalOverflow(_options_). + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + +// See TemporalHelpers.checkStringOptionWrongType(); this code path has +// different expectations for observable calls + +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: null }), "null"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: true }), "true"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: false }), "false"); +assert.throws(TypeError, () => datetime.with({ second: 41 }, { overflow: Symbol() }), "symbol"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2 }), "number"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: 2n }), "bigint"); +assert.throws(RangeError, () => datetime.with({ second: 41 }, { overflow: {} }), "plain object"); + +// toString property should only be read and converted to a string once, because +// a copied object with the resulting string on it is passed to +// Calendar.dateFromFields(). +const expected = [ + "get overflow.toString", + "call overflow.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "constrain", "overflow"); +const result = datetime.with({ second: 41 }, { overflow: observer }); +assert.sameValue(result.epochNanoseconds, 1_000_000_001_987_654_321n, "object with toString"); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js new file mode 100644 index 0000000000..510a4ed819 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: The "with" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.with, + "function", + "`typeof ZonedDateTime.prototype.with` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "with", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js new file mode 100644 index 0000000000..e7eca2b136 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/proto-in-calendar-fields.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const zoneddatetime = new Temporal.ZonedDateTime(1682892000000000000n, 'Europe/Madrid', calendar); + +assert.throws(RangeError, () => zoneddatetime.with({hour: 12})); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..0977701e40 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/read-time-fields-before-datefromfields.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.zoneddatetime.prototype.with step 23: + 23. Let _dateTimeResult_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInfinityTime(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); +const newDatetime = datetime.with({ year: 2022 }); + +assert.sameValue(newDatetime.hour, 1, "hour value"); +assert.sameValue(newDatetime.minute, 46, "minute value"); +assert.sameValue(newDatetime.second, 40, "second value"); +assert.sameValue(newDatetime.millisecond, 987, "millisecond value"); +assert.sameValue(newDatetime.microsecond, 654, "microsecond value"); +assert.sameValue(newDatetime.nanosecond, 321, "nanosecond value"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js new file mode 100644 index 0000000000..c47d2634eb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/receiver-offset-broken.js @@ -0,0 +1,59 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.protoype.with +description: > + TypeError thrown when the offset field of the argument or the object returned + from mergeFields is broken +info: | + 7. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + 8. Append *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"nanosecond"*, *"offset"*, and *"second"* to _fieldNames_. + 9. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"offset"* »). + 10. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~). + 11. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_). + 12. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"offset"* »). +features: [Temporal] +---*/ + +class ObservedCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + this.resetCalls(); + } + + toString() { + return "observed-calendar"; + } + + mergeFields(original, additional) { + this.mergeFieldsCalled++; + const result = super.mergeFields(original, additional); + result.offset = Symbol("can't convert to string"); + return result; + } + + resetCalls() { + this.mergeFieldsCalled = 0; + } +} + +const calendar = new ObservedCalendar(); +const dateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + +// Test throw in step 10 + +assert.throws(TypeError, () => dateTime.with({ offset: Symbol("can't convert to string") }), "conversion failure on ZonedDateTime-like"); +assert.sameValue(calendar.mergeFieldsCalled, 0, "calendar.mergeFields should not be called"); + +calendar.resetCalls(); + +// Test throw in step 12 (before sabotaging the ZonedDateTime instance) + +assert.throws(TypeError, () => dateTime.with({ year: 2002 }), "conversion failure on sabotaged return value from mergeFields"); +assert.sameValue(calendar.mergeFieldsCalled, 1, "calendar.mergeFields was called once"); + +calendar.resetCalls(); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js new file mode 100644 index 0000000000..7435848f8d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "with", + [{ year: 2000 }], + (result) => { + assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result"); + assert.sameValue(result.year, 2000, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..abcf246b03 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..49db5085a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.with({ day: 27 }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no disambiguation case)` + ); + + const badTimeZone = { + id: "Etc/Bad", + getPossibleInstantsFor() { return []; }, + getOffsetNanosecondsFor: notCallable, + }; + const badDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, badTimeZone); + assert.throws( + TypeError, + () => badDateTime.with({ day: 27 }, { offset: "ignore" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=ignore and no possible instants case)` + ); + assert.throws( + TypeError, + () => badDateTime.with({ day: 27 }, { offset: "prefer" }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError (in offset=prefer and no possible instants case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..cec9c2b6e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..7d1d89673b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.with({ day: 27 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..c24bad7373 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.zoneddatetime.prototype.with step 24: + 24. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_dateTimeResult_.[[Year]], [...], _dateTimeResult_.[[Nanosecond]], _offsetNanoseconds_, _timeZone_, _disambiguation_, _offset_). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2005-09-09T01:46:40", +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + datetime.with({ year: 2005 }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js new file mode 100644 index 0000000000..f4fbeff05b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time-with-offset.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset and an explicit offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00", +}; + +let options = { + offset: "use", +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js new file mode 100644 index 0000000000..fe7a12d195 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/with/zoned-datetime-like-at-minimum-date-time.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.with +description: > + Throws a RangeError for the minimum date/value with UTC offset. +info: | + Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] ) + ... + 21. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[Year]], + dateTimeResult.[[Month]], dateTimeResult.[[Day]], dateTimeResult.[[Hour]], + dateTimeResult.[[Minute]], dateTimeResult.[[Second]], dateTimeResult.[[Millisecond]], + dateTimeResult.[[Microsecond]], dateTimeResult.[[Nanosecond]], option, offsetNanoseconds, + timeZone, disambiguation, offset, match exactly). + ... +features: [Temporal] +---*/ + +let zdt = new Temporal.ZonedDateTime(0n, "UTC"); + +let temporalZonedDateTimeLike = { + year: -271821, + month: 4, + day: 19, + hour: 1, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}; + +assert.throws(RangeError, () => zdt.with(temporalZonedDateTimeLike)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js new file mode 100644 index 0000000000..b8fbe1f637 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const withCalendar = Temporal.ZonedDateTime.prototype.withCalendar; + +assert.sameValue(typeof withCalendar, "function"); + +const args = [new Temporal.Calendar("iso8601")]; + +assert.throws(TypeError, () => withCalendar.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => withCalendar.apply(null, args), "null"); +assert.throws(TypeError, () => withCalendar.apply(true, args), "true"); +assert.throws(TypeError, () => withCalendar.apply("", args), "empty string"); +assert.throws(TypeError, () => withCalendar.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => withCalendar.apply(1, args), "1"); +assert.throws(TypeError, () => withCalendar.apply({}, args), "plain object"); +assert.throws(TypeError, () => withCalendar.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => withCalendar.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..8c5ba81c9d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.withCalendar("iso8601"); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js new file mode 100644 index 0000000000..373ccd5e4a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: > + Tests that Temporal.ZonedDateTime.prototype.withCalendar + 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.ZonedDateTime.prototype.withCalendar), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withCalendar), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withCalendar), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.withCalendar.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js new file mode 100644 index 0000000000..34ca85e532 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Calendar names are case-insensitive +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + +const arg = "iSo8601"; +const result = instance.withCalendar(arg); +assert.sameValue(result.calendarId, "iso8601", "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js new file mode 100644 index 0000000000..ba5a6653a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-number.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: A number is not allowed to be a calendar +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + +const numbers = [ + 1, + -19761118, + 19761118, + 1234567890, +]; + +for (const arg of numbers) { + assert.throws( + TypeError, + () => instance.withCalendar(arg), + "A number is not a valid ISO string for Calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js new file mode 100644 index 0000000000..d1b40c2f7a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string-leap-second.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Leap second is a valid ISO string for Calendar +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + +const arg = "2016-12-31T23:59:60"; +const result = instance.withCalendar(arg); +assert.sameValue( + result.calendarId, + "iso8601", + "leap second is a valid ISO string for Calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js new file mode 100644 index 0000000000..8acaca9dec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-string.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: A calendar ID is valid input for Calendar +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + +const arg = "iso8601"; + +const result = instance.withCalendar(arg); +assert.sameValue(result.getISOFields().calendar, "iso8601", `Calendar created from string "${arg}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js new file mode 100644 index 0000000000..452864682f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-temporal-object.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal-totemporalcalendar step 1.b: + b. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(2000, 5, 2); +const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321); +const plainMonthDay = new Temporal.PlainMonthDay(5, 2); +const plainYearMonth = new Temporal.PlainYearMonth(2000, 5); +const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +[plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((arg) => { + const actual = []; + const expected = []; + + const calendar = arg.getISOFields().calendar; + + Object.defineProperty(arg, "calendar", { + get() { + actual.push("get calendar"); + return calendar; + }, + }); + + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + const result = instance.withCalendar(arg); + assert.sameValue(result.getISOFields().calendar, calendar, "Temporal object coerced to calendar"); + + assert.compareArray(actual, expected, "calendar getter not called"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js new file mode 100644 index 0000000000..34ff322aea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/calendar-wrong-type.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for Calendar +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", { + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + day() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "replace-me", + inLeapYear() {}, + mergeFields() {}, + month() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + year() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.withCalendar(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.withCalendar(arg), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js new file mode 100644 index 0000000000..46193d5f90 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Temporal.ZonedDateTime.prototype.withCalendar.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.ZonedDateTime.prototype.withCalendar, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js new file mode 100644 index 0000000000..c59dfc0273 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/missing-argument.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: TypeError thrown when calendar argument not given +features: [Temporal] +---*/ + +const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); +assert.throws(TypeError, () => zonedDateTime.withCalendar(), "missing argument"); +assert.throws(TypeError, () => zonedDateTime.withCalendar(undefined), "undefined argument"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js new file mode 100644 index 0000000000..e5e77eef78 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Temporal.ZonedDateTime.prototype.withCalendar.name is "withCalendar". +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.ZonedDateTime.prototype.withCalendar, "name", { + value: "withCalendar", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js new file mode 100644 index 0000000000..68de93d8ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: > + Temporal.ZonedDateTime.prototype.withCalendar 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.ZonedDateTime.prototype.withCalendar(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withCalendar), false, + "isConstructor(Temporal.ZonedDateTime.prototype.withCalendar)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js new file mode 100644 index 0000000000..4afd544193 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: The "withCalendar" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.withCalendar, + "function", + "`typeof ZonedDateTime.prototype.withCalendar` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "withCalendar", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js new file mode 100644 index 0000000000..88167a59f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withCalendar/subclassing-ignored.js @@ -0,0 +1,57 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withcalendar +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const customCalendar = { + year() { return 1900; }, + month() { return 2; }, + day() { return 5; }, + toString() { return "custom-calendar"; }, + dateAdd() {}, + dateFromFields() {}, + dateUntil() {}, + dayOfWeek() {}, + dayOfYear() {}, + daysInMonth() {}, + daysInWeek() {}, + daysInYear() {}, + fields() {}, + id: "custom-calendar", + inLeapYear() {}, + mergeFields() {}, + monthCode() {}, + monthDayFromFields() {}, + monthsInYear() {}, + weekOfYear() {}, + yearMonthFromFields() {}, + yearOfWeek() {}, +}; + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "withCalendar", + [customCalendar], + (result) => { + assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result"); + assert.sameValue(result.year, 1900, "year result"); + assert.sameValue(result.month, 2, "month result"); + assert.sameValue(result.day, 5, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + assert.sameValue(result.getCalendar(), customCalendar, "calendar result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js new file mode 100644 index 0000000000..a5b009fd98 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-builtin-calendar-no-array-iteration.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Calling the method with a property bag argument with a builtin calendar causes + no observable array iteration when getting the calendar fields. +features: [Temporal] +---*/ + +const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator]; +Array.prototype[Symbol.iterator] = function arrayIterator() { + throw new Test262Error("Array should not be iterated"); +} + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const arg = { year: 2000, month: 5, day: 2, calendar: "iso8601" }; +instance.withPlainDate(arg); + +Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..a91d766133 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const arg = { year: 2000, month: 5, day: 2, calendar }; +instance.withPlainDate(arg); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js new file mode 100644 index 0000000000..cc96585bc9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-constructor-in-calendar-fields.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']); +const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar}; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +assert.throws(RangeError, () => instance.withPlainDate(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js new file mode 100644 index 0000000000..4ef2d3ba44 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-duplicate-calendar-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) { + const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields); + const arg = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar }; + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + + assert.throws(RangeError, () => instance.withPlainDate(arg)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js new file mode 100644 index 0000000000..8f0b291f86 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-leap-second.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Leap second is a valid ISO string for PlainDate +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +let arg = "2016-12-31T23:59:60"; +const result1 = instance.withPlainDate(arg); +assert.sameValue( + result1.epochNanoseconds, + 1_483_148_800_000_000_000n, + "leap second is a valid ISO string for PlainDate" +); + +arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result2 = instance.withPlainDate(arg); +assert.sameValue( + result2.epochNanoseconds, + 1_483_148_800_000_000_000n, + "second: 60 is ignored in property bag for PlainDate" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js new file mode 100644 index 0000000000..107e93afd1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-number.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: A number cannot be used in place of a Temporal.PlainDate +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const numbers = [ + 1, + 19761118, + -19761118, + 1234567890, +]; + +for (const arg of numbers) { + assert.throws( + TypeError, + () => instance.withPlainDate(arg), + 'Numbers cannot be used in place of an ISO string for PlainDate' + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js new file mode 100644 index 0000000000..e94e741220 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-plaindatetime.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.withplaindate +description: Fast path for converting Temporal.PlainDateTime to Temporal.PlainDate by reading internal slots +info: | + sec-temporal.zoneddatetime.prototype.withplaindate step 3: + 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_). + sec-temporal-totemporaldate step 2.b: + b. If _item_ has an [[InitializedTemporalDateTime]] internal slot, then + i. Return ! CreateTemporalDate(_item_.[[ISOYear]], _item_.[[ISOMonth]], _item_.[[ISODay]], _item_.[[Calendar]]). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkPlainDateTimeConversionFastPath((datetime) => { + const receiver = new Temporal.ZonedDateTime(1_000_000_000_123_456_789n, "UTC"); + const result = receiver.withPlainDate(datetime); + assert.sameValue(result.year, 2000, "year result"); + assert.sameValue(result.month, 5, "month result"); + assert.sameValue(result.day, 2, "day result"); + assert.sameValue(result.hour, 1, "hour result"); + assert.sameValue(result.minute, 46, "minute result"); + assert.sameValue(result.second, 40, "second result"); + assert.sameValue(result.millisecond, 123, "millisecond result"); + assert.sameValue(result.microsecond, 456, "microsecond result"); + assert.sameValue(result.nanosecond, 789, "nanosecond result"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js new file mode 100644 index 0000000000..6b91d2f8fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-case-insensitive.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: The calendar name is case-insensitive +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +const calendar = "IsO8601"; + +const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +const result = instance.withPlainDate(arg); +assert.sameValue(result.epochNanoseconds, 217_129_600_000_000_000n, "Calendar is case-insensitive"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js new file mode 100644 index 0000000000..28f8d33d32 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Leap second is a valid ISO string for a calendar in a property bag +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +const calendar = "2016-12-31T23:59:60+00:00[UTC]"; + +const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +const result = instance.withPlainDate(arg); +assert.sameValue( + result.epochNanoseconds, + 217_129_600_000_000_000n, + "leap second is a valid ISO string for calendar" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js new file mode 100644 index 0000000000..c3fb370434 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-number.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: A number as calendar in a property bag is not accepted +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +const numbers = [ + 1, + 19970327, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; + assert.throws( + TypeError, + () => instance.withPlainDate(arg), + "Numbers cannot be used as a calendar" + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js new file mode 100644 index 0000000000..cef382bdea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-string.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: A calendar ID is valid input for Calendar +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +const calendar = "iso8601"; + +const arg = { year: 1976, monthCode: "M11", day: 18, calendar }; +const result = instance.withPlainDate(arg); +assert.sameValue(result.epochNanoseconds, 217_129_600_000_000_000n, `Calendar created from string "${calendar}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..da5c268ad2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-wrong-type.js @@ -0,0 +1,46 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Appropriate error thrown when a calendar property from a property bag cannot + be converted to a calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [calendar, description] of primitiveTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + typeof calendar === 'string' ? RangeError : TypeError, + () => instance.withPlainDate(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object that doesn't implement the protocol"], + [new Temporal.TimeZone("UTC"), "time zone instance"], + [Temporal.Calendar, "Temporal.Calendar, object"], + [Temporal.Calendar.prototype, "Temporal.Calendar.prototype, object"], // fails brand check in dateFromFields() +]; + +for (const [calendar, description] of typeErrorTests) { + const arg = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.withPlainDate(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js new file mode 100644 index 0000000000..3d7d95215e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-propertybag-calendar-year-zero.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T17:45", + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+01:00", + "-000000-10-31T17:45+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js new file mode 100644 index 0000000000..89ee626a0c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-proto-in-calendar-fields.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']); +const arg = {year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar}; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +assert.throws(RangeError, () => instance.withPlainDate(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..23736baf20 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-calendar-annotation.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["2000-05-02[u-ca=iso8601]", "without time or time zone"], + ["2000-05-02[UTC][u-ca=iso8601]", "with time zone and no time"], + ["2000-05-02T15:23[u-ca=iso8601]", "without time zone"], + ["2000-05-02T15:23[UTC][u-ca=iso8601]", "with time zone"], + ["2000-05-02T15:23[!u-ca=iso8601]", "with ! and no time zone"], + ["2000-05-02T15:23[UTC][!u-ca=iso8601]", "with ! and time zone"], + ["2000-05-02T15:23[u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainDate(arg); + + assert.sameValue( + result.epochNanoseconds, + 957_225_600_000_000_000n, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..e74fc7011d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-critical-unknown-annotation.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01[!foo=bar]", + "1970-01-01T00:00[!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..238789510e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-date-with-utc-offset.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: UTC offset not valid with format that does not include a time +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const validStrings = [ + "2000-05-02T00+00:00", + "2000-05-02T00+00:00[UTC]", + "2000-05-02T00+00:00[!UTC]", + "2000-05-02T00-02:30[America/St_Johns]", +]; + +for (const arg of validStrings) { + const result = instance.withPlainDate(arg); + + assert.sameValue( + result.epochNanoseconds, + 957_225_600_000_000_000n, + `"${arg}" is a valid UTC offset with time for PlainDate` + ); +} + +const invalidStrings = [ + "2022-09-15Z", + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + `"${arg}" UTC offset without time is not valid for PlainDate` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js new file mode 100644 index 0000000000..64bac8afce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-invalid.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + RangeError thrown if an invalid ISO string (or syntactically valid ISO string + that is not supported) is used as a PlainDate +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + // invalid ISO strings: + "", + "invalid iso8601", + "2020-01-00", + "2020-01-32", + "2020-02-30", + "2021-02-29", + "2020-00-01", + "2020-13-01", + "2020-01-01T", + "2020-01-01T25:00:00", + "2020-01-01T01:60:00", + "2020-01-01T01:60:61", + "2020-01-01junk", + "2020-01-01T00:00:00junk", + "2020-01-01T00:00:00+00:00junk", + "2020-01-01T00:00:00+00:00[UTC]junk", + "2020-01-01T00:00:00+00:00[UTC][u-ca=iso8601]junk", + "02020-01-01", + "2020-001-01", + "2020-01-001", + "2020-01-01T001", + "2020-01-01T01:001", + "2020-01-01T01:01:001", + // valid, but forms not supported in Temporal: + "2020-W01-1", + "2020-001", + "+0002020-01-01", + // valid, but this calendar must not exist: + "2020-01-01[u-ca=notexist]", + // may be valid in other contexts, but insufficient information for PlainDate: + "2020-01", + "+002020-01", + "01-01", + "2020-W01", + "P1Y", + "-P12Y", + // valid, but outside the supported range: + "-999999-01-01", + "+999999-01-01", +]; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + `"${arg}" should not be a valid ISO string for a PlainDate` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..b255b8f07d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-calendar.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01[u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01[!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01[u-ca=iso8601][foo=bar][!u-ca=iso8601]", + "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..e79719e26c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-multiple-time-zone.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "1970-01-01[UTC][UTC]", + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js new file mode 100644 index 0000000000..fef493b58b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-separators.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Time separator in string argument can vary +features: [Temporal] +---*/ + +const tests = [ + ["2000-05-02T15:23", "uppercase T"], + ["2000-05-02t15:23", "lowercase T"], + ["2000-05-02 15:23", "space between date and time"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainDate(arg); + + assert.sameValue( + result.epochNanoseconds, + 957_225_600_000_000_000n, + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..831730e461 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-time-zone-annotation.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["2000-05-02[Asia/Kolkata]", "named, with no time"], + ["2000-05-02[!Europe/Vienna]", "named, with ! and no time"], + ["2000-05-02[+00:00]", "numeric, with no time"], + ["2000-05-02[!-02:30]", "numeric, with ! and no time"], + ["2000-05-02T15:23[America/Sao_Paulo]", "named, with no offset"], + ["2000-05-02T15:23[!Asia/Tokyo]", "named, with ! and no offset"], + ["2000-05-02T15:23[-02:30]", "numeric, with no offset"], + ["2000-05-02T15:23[!+00:00]", "numeric, with ! and no offset"], + ["2000-05-02T15:23+00:00[America/New_York]", "named, with offset"], + ["2000-05-02T15:23+00:00[!UTC]", "named, with offset and !"], + ["2000-05-02T15:23+00:00[+01:00]", "numeric, with offset"], + ["2000-05-02T15:23+00:00[!-08:00]", "numeric, with offset and !"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainDate(arg); + + assert.sameValue( + result.epochNanoseconds, + 957_225_600_000_000_000n, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..b7447b455c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-unknown-annotation.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Various forms of unknown annotation +features: [Temporal] +---*/ + +const tests = [ + ["2000-05-02[foo=bar]", "without time"], + ["2000-05-02T15:23[foo=bar]", "alone"], + ["2000-05-02T15:23[UTC][foo=bar]", "with time zone"], + ["2000-05-02T15:23[u-ca=iso8601][foo=bar]", "with calendar"], + ["2000-05-02T15:23[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"], + ["2000-05-02T15:23[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainDate(arg); + + assert.sameValue( + result.epochNanoseconds, + 957_225_600_000_000_000n, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js new file mode 100644 index 0000000000..74071fa90e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-string-with-utc-designator.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: RangeError thrown if a string with UTC designator is used as a PlainDate +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "2019-10-01T09:00:00Z", + "2019-10-01T09:00:00Z[UTC]", +]; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + "String with UTC designator should not be valid as a PlainDate" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js new file mode 100644 index 0000000000..8432e76956 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-wrong-type.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for PlainDate +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const primitiveTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.withPlainDate(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.withPlainDate(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js new file mode 100644 index 0000000000..c4ea148318 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-convert.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated. +features: [Temporal] +---*/ + +class TZ extends Temporal.TimeZone { + constructor() { super("UTC") } + getOffsetNanosecondsFor() { throw new Test262Error() } +} + +const tz = new TZ(); +const arg = new Temporal.ZonedDateTime(0n, tz); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +assert.throws(Test262Error, () => instance.withPlainDate(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js new file mode 100644 index 0000000000..2a50ebb455 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-slots.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Getters are not called when converting a ZonedDateTime to a PlainDate. +includes: [compareArray.js] +features: [Temporal] +---*/ + +const actual = []; +const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype); +const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"]; + +for (const property of getters) { + Object.defineProperty(Temporal.ZonedDateTime.prototype, property, { + get() { + actual.push(`get ${property}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${property}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${property}`); + return value; + }, + }; + }, + }); +} + +const arg = new Temporal.ZonedDateTime(0n, "UTC"); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +instance.withPlainDate(arg); +assert.compareArray(actual, []); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..59f7ea3c1c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.withPlainDate(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..f3661242e1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.withPlainDate(other), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..5f1207a538 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.withPlainDate(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..b53e9bc76c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.withPlainDate(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js new file mode 100644 index 0000000000..b6f48f3385 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const withPlainDate = Temporal.ZonedDateTime.prototype.withPlainDate; + +assert.sameValue(typeof withPlainDate, "function"); + +const args = [new Temporal.PlainDate(2022, 6, 22)]; + +assert.throws(TypeError, () => withPlainDate.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => withPlainDate.apply(null, args), "null"); +assert.throws(TypeError, () => withPlainDate.apply(true, args), "true"); +assert.throws(TypeError, () => withPlainDate.apply("", args), "empty string"); +assert.throws(TypeError, () => withPlainDate.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => withPlainDate.apply(1, args), "1"); +assert.throws(TypeError, () => withPlainDate.apply({}, args), "plain object"); +assert.throws(TypeError, () => withPlainDate.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => withPlainDate.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..324f89b063 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "id"); +Object.defineProperty(Temporal.Calendar.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13)); + +Object.defineProperty(Temporal.Calendar.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..0d002912dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.withPlainDate(new Temporal.PlainDate(2001, 6, 13)); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js new file mode 100644 index 0000000000..fb6037f777 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Tests that Temporal.ZonedDateTime.prototype.withPlainDate + 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.ZonedDateTime.prototype.withPlainDate), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainDate), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainDate), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainDate.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js new file mode 100644 index 0000000000..8c7a033e9b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-datefromfields-called-with-options-undefined.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Calendar.dateFromFields method is called with undefined as the options value + when call originates internally +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +instance.withPlainDate({ year: 2000, month: 5, day: 3, calendar }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js new file mode 100644 index 0000000000..397a0b4f57 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-fields-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.zoneddatetime.prototype.withplaindate step 3: + 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_). + sec-temporal-totemporaldate step 2.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "month", + "monthCode", + "year", +]; + +const calendar1 = TemporalHelpers.calendarFieldsIterable(); +const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar1); +const calendar2 = TemporalHelpers.calendarFieldsIterable(); +datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: calendar2 }); + +assert.sameValue(calendar1.fieldsCallCount, 0, "fields() method not called"); +assert.sameValue(calendar2.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar2.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar2.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js new file mode 100644 index 0000000000..a3d487d9ae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/calendar-temporal-object.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.zoneddatetime.prototype.withplaindate step 3: + 3. Let _plainDate_ be ? ToTemporalDate(_plainDateLike_). + sec-temporal-totemporaldate step 2.c: + c. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject, calendar) => { + const datetime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + // the PlainDate's calendar will override the ZonedDateTime's ISO calendar + const result = datetime.withPlainDate({ year: 2001, month: 6, day: 4, calendar: temporalObject }); + assert.sameValue(result.getCalendar(), calendar, "Temporal object coerced to calendar"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..9776d8ad73 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); + +const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); +instance.withPlainDate(new Temporal.PlainDate(2000, 5, 2, nonBuiltinISOCalendar)); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..894cd7ff7a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.zoneddatetime.prototype.withplaindate +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const base = { year: 2000, month: 5, day: 2 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day"].forEach((prop) => { + assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: inf }), `${prop} property cannot be ${inf}`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.withPlainDate({ ...base, [prop]: obj })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js new file mode 100644 index 0000000000..8ff6734457 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Temporal.ZonedDateTime.prototype.withPlainDate.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.ZonedDateTime.prototype.withPlainDate, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js new file mode 100644 index 0000000000..ba8bfba184 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Temporal.ZonedDateTime.prototype.withPlainDate.name is "withPlainDate". +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.ZonedDateTime.prototype.withPlainDate, "name", { + value: "withPlainDate", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js new file mode 100644 index 0000000000..e76365257d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/negative-epochnanoseconds.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const result = datetime.withPlainDate(new Temporal.PlainDate(2000, 5, 2)); +assert.sameValue(result.epochNanoseconds, 957286235_000_000_001n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js new file mode 100644 index 0000000000..9cb6dd4ed6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: > + Temporal.ZonedDateTime.prototype.withPlainDate 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.ZonedDateTime.prototype.withPlainDate(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate), false, + "isConstructor(Temporal.ZonedDateTime.prototype.withPlainDate)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js new file mode 100644 index 0000000000..d26b5e8e1b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js @@ -0,0 +1,114 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.withplaindate +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + // ToTemporalDate + "get plainDateLike.calendar", + "has plainDateLike.calendar.dateAdd", + "has plainDateLike.calendar.dateFromFields", + "has plainDateLike.calendar.dateUntil", + "has plainDateLike.calendar.day", + "has plainDateLike.calendar.dayOfWeek", + "has plainDateLike.calendar.dayOfYear", + "has plainDateLike.calendar.daysInMonth", + "has plainDateLike.calendar.daysInWeek", + "has plainDateLike.calendar.daysInYear", + "has plainDateLike.calendar.fields", + "has plainDateLike.calendar.id", + "has plainDateLike.calendar.inLeapYear", + "has plainDateLike.calendar.mergeFields", + "has plainDateLike.calendar.month", + "has plainDateLike.calendar.monthCode", + "has plainDateLike.calendar.monthDayFromFields", + "has plainDateLike.calendar.monthsInYear", + "has plainDateLike.calendar.weekOfYear", + "has plainDateLike.calendar.year", + "has plainDateLike.calendar.yearMonthFromFields", + "has plainDateLike.calendar.yearOfWeek", + "get plainDateLike.calendar.dateFromFields", + "get plainDateLike.calendar.fields", + "call plainDateLike.calendar.fields", + "get plainDateLike.day", + "get plainDateLike.day.valueOf", + "call plainDateLike.day.valueOf", + "get plainDateLike.month", + "get plainDateLike.month.valueOf", + "call plainDateLike.month.valueOf", + "get plainDateLike.monthCode", + "get plainDateLike.monthCode.toString", + "call plainDateLike.monthCode.toString", + "get plainDateLike.year", + "get plainDateLike.year.valueOf", + "call plainDateLike.year.valueOf", + "call plainDateLike.calendar.dateFromFields", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor + "call this.timeZone.getOffsetNanosecondsFor", + // ConsolidateCalendars + "get this.calendar.id", + "get plainDateLike.calendar.id", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", +]; + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const plainDateCalendar = TemporalHelpers.calendarObserver(actual, "plainDateLike.calendar"); +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); + +const instance = new Temporal.ZonedDateTime(37800_000_000_000n /* 1970-01-01T02:30-08:00 */, timeZone, calendar); +const fallBackInstance = new Temporal.ZonedDateTime(34200_000_000_000n /* 1970-01-01T01:30-08:00 */, timeZone, calendar); +actual.splice(0); // clear calls that happened in constructor + +const plainDateLike = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + calendar: plainDateCalendar, +}, "plainDateLike"); +instance.withPlainDate(plainDateLike); +assert.compareArray(actual, expected, "order of operations at normal wall-clock time"); +actual.splice(0); // clear + +const fallBackPlainDateLike = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + calendar: plainDateCalendar, +}, "plainDateLike"); +fallBackInstance.withPlainDate(fallBackPlainDateLike); +assert.compareArray(actual, expected, "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +const springForwardPlainDateLike = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 4, + monthCode: "M04", + day: 2, + calendar: plainDateCalendar, +}, "plainDateLike"); +instance.withPlainDate(springForwardPlainDateLike); +assert.compareArray(actual, expected.concat([ + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js new file mode 100644 index 0000000000..c41fbee16b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: The "withPlainDate" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.withPlainDate, + "function", + "`typeof ZonedDateTime.prototype.withPlainDate` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainDate", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js new file mode 100644 index 0000000000..311c25b2f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "withPlainDate", + ["2000-01-01"], + (result) => { + assert.sameValue(result.epochNanoseconds, 946684800_000_000_010n, "epochNanoseconds result"); + assert.sameValue(result.year, 2000, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 0, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..7d65f11f94 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const date = new Temporal.PlainDate(2000, 5, 2); + assert.throws(RangeError, () => datetime.withPlainDate(date)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..b33ef6da20 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const date = new Temporal.PlainDate(2000, 5, 2); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.withPlainDate(date), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..541796174f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const date = new Temporal.PlainDate(2000, 5, 2); + assert.throws(RangeError, () => datetime.withPlainDate(date)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..15e28fdf51 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const date = new Temporal.PlainDate(2000, 5, 2); + assert.throws(TypeError, () => datetime.withPlainDate(date)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js new file mode 100644 index 0000000000..933590f526 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/year-zero.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaindate +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31", + "-000000-10-31T00:45", + "-000000-10-31T00:45+01:00", + "-000000-10-31T00:45+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainDate(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js new file mode 100644 index 0000000000..3c363713a3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-number.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: A number is invalid in place of an ISO string for Temporal.PlainTime +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const numbers = [ + 1, + -123456.987654321, + 1234567, + 123456.9876543219, +]; + +for (const arg of numbers) { + assert.throws( + TypeError, + () => instance.withPlainTime(arg), + `A number (${arg}) is not a valid ISO string for PlainTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js new file mode 100644 index 0000000000..a052bf77e8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Various forms of calendar annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["12:34:56.987654321[u-ca=iso8601]", "without time zone"], + ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"], + ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"], + ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"], + ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"], + ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"], + ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"], + ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"], + ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"], + ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"], + ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"], + ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"], + ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"], + ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"], + ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"], + ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainTime(arg); + + assert.sameValue( + result.epochNanoseconds, + 45_296_987_654_321n, + `calendar annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js new file mode 100644 index 0000000000..231cb49b0b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-critical-unknown-annotation.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Unknown annotations with critical flag are rejected +features: [Temporal] +---*/ + +const invalidStrings = [ + "00:00[!foo=bar]", + "T00:00[!foo=bar]", + "1970-01-01T00:00[!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar]", + "1970-01-01T00:00[u-ca=iso8601][!foo=bar]", + "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]", + "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `reject unknown annotation with critical flag: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js new file mode 100644 index 0000000000..b59e9c1079 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-date-with-utc-offset.js @@ -0,0 +1,53 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: UTC offset not valid with format that does not include a time +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +const validStrings = [ + "12:34:56.987654321+00:00", + "12:34:56.987654321+00:00[UTC]", + "12:34:56.987654321+00:00[!UTC]", + "12:34:56.987654321-02:30[America/St_Johns]", + "1976-11-18T12:34:56.987654321+00:00", + "1976-11-18T12:34:56.987654321+00:00[UTC]", + "1976-11-18T12:34:56.987654321+00:00[!UTC]", + "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]", +]; + +for (const arg of validStrings) { + const result = instance.withPlainTime(arg); + + assert.sameValue( + result.epochNanoseconds, + 45_296_987_654_321n, + `"${arg}" is a valid UTC offset with time for PlainTime` + ); +} + +const invalidStrings = [ + "2022-09-15Z", + "2022-09-15Z[UTC]", + "2022-09-15Z[Europe/Vienna]", + "2022-09-15+00:00", + "2022-09-15+00:00[UTC]", + "2022-09-15-02:30", + "2022-09-15-02:30[America/St_Johns]", +]; + +for (const arg of invalidStrings) { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `"${arg}" UTC offset without time is not valid for PlainTime` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js new file mode 100644 index 0000000000..7997635a90 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-calendar.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + More than one calendar annotation is not syntactical if any have the criical + flag +features: [Temporal] +---*/ + +const invalidStrings = [ + "00:00[u-ca=iso8601][!u-ca=iso8601]", + "00:00[!u-ca=iso8601][u-ca=iso8601]", + "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]", + "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]", + "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]", + "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `reject more than one calendar annotation if any critical: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js new file mode 100644 index 0000000000..731ce895a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-multiple-time-zone.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: More than one time zone annotation is not syntactical +features: [Temporal] +---*/ + +const invalidStrings = [ + "00:00[UTC][UTC]", + "T00:00[UTC][UTC]", + "1970-01-01T00:00[UTC][UTC]", + "1970-01-01T00:00[!UTC][UTC]", + "1970-01-01T00:00[UTC][!UTC]", + "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]", + "1970-01-01T00:00[UTC][foo=bar][UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `reject more than one time zone annotation: ${arg}` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js new file mode 100644 index 0000000000..4f2f77be0a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-no-implicit-midnight.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if a date-only string is passed in a PlainTime context +features: [Temporal, arrow-function] +---*/ + +const arg = "2019-10-01"; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +assert.throws( + RangeError, + () => instance.withPlainTime(arg), + "Date-only string throws, does not implicitly convert to midnight" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js new file mode 100644 index 0000000000..51e897a340 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: ISO 8601 time designator "T" required in cases of ambiguity +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => { + let arg = string; + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `'${arg}' is ambiguous and requires T prefix` + ); + // The same string with a T prefix should not throw: + arg = `T${string}`; + instance.withPlainTime(arg); + + arg = ` ${string}`; + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + `space is not accepted as a substitute for T prefix: '${arg}'` + ); +}); + +// None of these should throw without a T prefix, because they are unambiguously time strings: +TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach( + (arg) => instance.withPlainTime(arg)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js new file mode 100644 index 0000000000..4698f39f6c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-separators.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Time separator in string argument can vary +features: [Temporal] +---*/ + +const tests = [ + ["1976-11-18T12:34:56.987654321", "uppercase T"], + ["1976-11-18t12:34:56.987654321", "lowercase T"], + ["1976-11-18 12:34:56.987654321", "space between date and time"], + ["T12:34:56.987654321", "time-only uppercase T"], + ["t12:34:56.987654321", "time-only lowercase T"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainTime(arg); + + assert.sameValue( + result.epochNanoseconds, + 45_296_987_654_321n, + `variant time separators (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js new file mode 100644 index 0000000000..b861c603cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js @@ -0,0 +1,51 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Various forms of time zone annotation; critical flag has no effect +features: [Temporal] +---*/ + +const tests = [ + ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"], + ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"], + ["12:34:56.987654321[+00:00]", "numeric, with no offset"], + ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"], + ["T12:34:56.987654321[UTC]", "named, with T and no offset"], + ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"], + ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"], + ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"], + ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"], + ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"], + ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"], + ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"], + ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"], + ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"], + ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"], + ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"], + ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"], + ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"], + ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"], + ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"], + ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"], + ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"], + ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"], + ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainTime(arg); + + assert.sameValue( + result.epochNanoseconds, + 45_296_987_654_321n, + `time zone annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js new file mode 100644 index 0000000000..7870e582ff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Various forms of unknown annotation +features: [Temporal] +---*/ + +const tests = [ + ["12:34:56.987654321[foo=bar]", "alone"], + ["12:34:56.987654321[UTC][foo=bar]", "with time zone"], + ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"], + ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"], + ["T12:34:56.987654321[foo=bar]", "with T"], + ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"], + ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"], + ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"], + ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"], + ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"], + ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"], + ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"], + ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"], +]; + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); + +tests.forEach(([arg, description]) => { + const result = instance.withPlainTime(arg); + + assert.sameValue( + result.epochNanoseconds, + 45_296_987_654_321n, + `unknown annotation (${description})` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js new file mode 100644 index 0000000000..5158ef1810 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-time-designator.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: ISO 8601 time designator "T" allowed at the start of PlainTime strings +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +const validStrings = [ + "T00:30", + "t00:30", + "T0030", + "t0030", + "T00:30:00", + "t00:30:00", + "T003000", + "t003000", + "T00:30:00.000000000", + "t00:30:00.000000000", + "T003000.000000000", + "t003000.000000000", +]; +validStrings.forEach((arg) => { + const result = instance.withPlainTime(arg); + assert.sameValue(result.epochNanoseconds, 999995400_000_000_000n, `T prefix is accepted: ${arg}`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js new file mode 100644 index 0000000000..5421bdb7bd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-string-with-utc-designator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if a string with UTC designator is used as a PlainTime +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "2019-10-01T09:00:00Z", + "2019-10-01T09:00:00Z[UTC]", + "09:00:00Z[UTC]", + "09:00:00Z", +]; +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + "String with UTC designator should not be valid as a PlainTime" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js new file mode 100644 index 0000000000..9ee14e55fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or property bag for PlainTime +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const primitiveTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [arg, description] of primitiveTests) { + assert.throws( + typeof arg === 'string' ? RangeError : TypeError, + () => instance.withPlainTime(arg), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainTime, "Temporal.PlainTime, object"], + [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"], +]; + +for (const [arg, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.withPlainTime(arg), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js new file mode 100644 index 0000000000..36c14236b5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-balanceisodatetime step 1: + 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_). + sec-temporal-builtintimezonegetplaindatetimefor step 3: + 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_). + sec-temporal-totemporaltime step 3.b: + b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + ... + ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]). + sec-temporal.zoneddatetime.prototype.withplaintime step 4.a: + a. Let _plainTime_ be ? ToTemporalTime(_plainTimeLike_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// This code path is encountered if the time zone offset is negative and its +// absolute value in nanoseconds is greater than the nanosecond field of the +// exact time's epoch parts +const tz = TemporalHelpers.specificOffsetTimeZone(-2); +const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz); + +const otherTimeZone = new Temporal.TimeZone("UTC"); // should not be used to convert datetime -> PlainTime +const zdt = new Temporal.ZonedDateTime(86400_000_000_000n, otherTimeZone); +const newzdt = zdt.withPlainTime(datetime); + +assert.sameValue(newzdt.microsecond, 0); +assert.sameValue(newzdt.nanosecond, 999); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..2bdf59aa9c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time. + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +const result = instance.withPlainTime(datetime); +assert.sameValue(result.epochNanoseconds, 60635_000_000_001n); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..d1064ae7ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.withPlainTime(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..65dc46f85f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.withPlainTime(other), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..3baca11c86 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.withPlainTime(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..f34255f8cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC"); + const other = new Temporal.ZonedDateTime(1_100_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.withPlainTime(other)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js new file mode 100644 index 0000000000..644b98458e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const withPlainTime = Temporal.ZonedDateTime.prototype.withPlainTime; + +assert.sameValue(typeof withPlainTime, "function"); + +assert.throws(TypeError, () => withPlainTime.call(undefined), "undefined"); +assert.throws(TypeError, () => withPlainTime.call(null), "null"); +assert.throws(TypeError, () => withPlainTime.call(true), "true"); +assert.throws(TypeError, () => withPlainTime.call(""), "empty string"); +assert.throws(TypeError, () => withPlainTime.call(Symbol()), "symbol"); +assert.throws(TypeError, () => withPlainTime.call(1), "1"); +assert.throws(TypeError, () => withPlainTime.call({}), "plain object"); +assert.throws(TypeError, () => withPlainTime.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => withPlainTime.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..e4282fd17c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin-timezone-no-observable-calls.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.withPlainTime(new Temporal.PlainTime(12, 34, 56)); + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js new file mode 100644 index 0000000000..c44365ab26 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + Tests that Temporal.ZonedDateTime.prototype.withPlainTime + 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.ZonedDateTime.prototype.withPlainTime), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withPlainTime), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withPlainTime), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.withPlainTime.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js new file mode 100644 index 0000000000..890bef4f0c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/getpossibleinstantsfor-called-with-iso8601-calendar.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + Time zone's getPossibleInstantsFor is called with a PlainDateTime with the + built-in ISO 8601 calendar +features: [Temporal] +info: | + DisambiguatePossibleInstants: + 2. Let _n_ be _possibleInstants_'s length. + ... + 5. Assert: _n_ = 0. + ... + 19. If _disambiguation_ is *"earlier"*, then + ... + c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_). + ... + 20. Assert: _disambiguation_ is *"compatible"* or *"later"*. + ... + 23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*). + 24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_). +---*/ + +class SkippedDateTime extends Temporal.TimeZone { + constructor() { + super("UTC"); + this.calls = 0; + } + + getPossibleInstantsFor(dateTime) { + // Calls occur in pairs. For the first one return no possible instants so + // that DisambiguatePossibleInstants will call it again + if (this.calls++ % 2 == 0) { + return []; + } + + assert.sameValue( + dateTime.getISOFields().calendar, + "iso8601", + "getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar" + ); + return super.getPossibleInstantsFor(dateTime); + } +} + +const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601"); +const timeZone = new SkippedDateTime(); + +const instance = new Temporal.ZonedDateTime(0n, timeZone, nonBuiltinISOCalendar); +instance.withPlainTime(); + +assert.sameValue(timeZone.calls, 2, "getPossibleInstantsFor should have been called 2 times"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js new file mode 100644 index 0000000000..79685eb7ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/leap-second.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Leap second is a valid ISO string for PlainTime +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +let arg = "2016-12-31T23:59:60"; +const result1 = instance.withPlainTime(arg); +assert.sameValue( + result1.epochNanoseconds, + 1000079999_000_000_000n, + "leap second is a valid ISO string for PlainTime" +); + +arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result2 = instance.withPlainTime(arg); +assert.sameValue( + result2.epochNanoseconds, + 1000079999_000_000_000n, + "second: 60 is ignored in property bag for PlainTime" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js new file mode 100644 index 0000000000..5e419dd76a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Temporal.ZonedDateTime.prototype.withPlainTime.length is 0 +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.ZonedDateTime.prototype.withPlainTime, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js new file mode 100644 index 0000000000..b9294ef7dd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Temporal.ZonedDateTime.prototype.withPlainTime.name is "withPlainTime". +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.ZonedDateTime.prototype.withPlainTime, "name", { + value: "withPlainTime", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js new file mode 100644 index 0000000000..9e0d86e8aa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: > + Temporal.ZonedDateTime.prototype.withPlainTime 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.ZonedDateTime.prototype.withPlainTime(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime), false, + "isConstructor(Temporal.ZonedDateTime.prototype.withPlainTime)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js new file mode 100644 index 0000000000..420678ddfd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js @@ -0,0 +1,88 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.withplaintime +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + // ToTemporalTime + "get plainTimeLike.hour", + "get plainTimeLike.hour.valueOf", + "call plainTimeLike.hour.valueOf", + "get plainTimeLike.microsecond", + "get plainTimeLike.microsecond.valueOf", + "call plainTimeLike.microsecond.valueOf", + "get plainTimeLike.millisecond", + "get plainTimeLike.millisecond.valueOf", + "call plainTimeLike.millisecond.valueOf", + "get plainTimeLike.minute", + "get plainTimeLike.minute.valueOf", + "call plainTimeLike.minute.valueOf", + "get plainTimeLike.nanosecond", + "get plainTimeLike.nanosecond.valueOf", + "call plainTimeLike.nanosecond.valueOf", + "get plainTimeLike.second", + "get plainTimeLike.second.valueOf", + "call plainTimeLike.second.valueOf", + // lookup + "get this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + // GetPlainDateTimeFor + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "call this.timeZone.getPossibleInstantsFor", +]; + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "this.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); + +const instance = new Temporal.ZonedDateTime(946713600_000_000_000n /* 2000-01-01T00:00-08:00 */, timeZone, calendar); +const fallBackInstance = new Temporal.ZonedDateTime(972802800_000_000_000n /* 2000-10-29T00:00-07:00 */, timeZone, calendar); +const springForwardInstance = new Temporal.ZonedDateTime(954662400_000_000_000n /* 2000-04-02T00:00-08:00 */, timeZone, calendar); +actual.splice(0); // clear calls that happened in constructors + +const plainTimeLike = TemporalHelpers.propertyBagObserver(actual, { + hour: 2, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}, "plainTimeLike"); + +instance.withPlainTime(plainTimeLike); +assert.compareArray(actual, expected, "order of operations at normal wall-clock time"); +actual.splice(0); // clear + +const plainTimeLike130 = TemporalHelpers.propertyBagObserver(actual, { + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}, "plainTimeLike"); + +fallBackInstance.withPlainTime(plainTimeLike130); +assert.compareArray(actual, expected, "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +springForwardInstance.withPlainTime(plainTimeLike); +assert.compareArray(actual, expected.concat([ + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js new file mode 100644 index 0000000000..62a6c07697 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/plaintime-propertybag-no-time-units.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Missing time units in property bag default to 0 +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC"); + +const props = {}; +assert.throws(TypeError, () => instance.withPlainTime(props), "TypeError if no properties are present"); + +props.minute = 30; +const result = instance.withPlainTime(props); +assert.sameValue(result.epochNanoseconds, 999995400_000_000_000n, "missing time units default to 0"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js new file mode 100644 index 0000000000..0df6a75f9f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: The "withPlainTime" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.withPlainTime, + "function", + "`typeof ZonedDateTime.prototype.withPlainTime` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "withPlainTime", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js new file mode 100644 index 0000000000..70ad2f2ea8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "withPlainTime", + ["05:43:21.123456789"], + (result) => { + assert.sameValue(result.epochNanoseconds, 20601_123_456_789n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 5, "hour result"); + assert.sameValue(result.minute, 43, "minute result"); + assert.sameValue(result.second, 21, "second result"); + assert.sameValue(result.millisecond, 123, "millisecond result"); + assert.sameValue(result.microsecond, 456, "microsecond result"); + assert.sameValue(result.nanosecond, 789, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js new file mode 100644 index 0000000000..fb56367443 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/time-undefined.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: The time is assumed to be midnight if not given +features: [BigInt, Temporal] +---*/ + +const datetime = new Temporal.ZonedDateTime(957270896_987_654_321n, "UTC"); + +const explicit = datetime.withPlainTime(undefined); +assert.sameValue(explicit.hour, 0, "default time is midnight"); +assert.sameValue(explicit.minute, 0, "default time is midnight"); +assert.sameValue(explicit.second, 0, "default time is midnight"); +assert.sameValue(explicit.millisecond, 0, "default time is midnight"); +assert.sameValue(explicit.microsecond, 0, "default time is midnight"); +assert.sameValue(explicit.nanosecond, 0, "default time is midnight"); + +const implicit = datetime.withPlainTime(); +assert.sameValue(implicit.hour, 0, "default time is midnight"); +assert.sameValue(implicit.minute, 0, "default time is midnight"); +assert.sameValue(implicit.second, 0, "default time is midnight"); +assert.sameValue(implicit.millisecond, 0, "default time is midnight"); +assert.sameValue(implicit.microsecond, 0, "default time is midnight"); +assert.sameValue(implicit.nanosecond, 0, "default time is midnight"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..abc5aab8a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); + assert.throws(RangeError, () => datetime.withPlainTime(time)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..a2a089cc9c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.withPlainTime(time), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..a08ece63c0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); + assert.throws(RangeError, () => datetime.withPlainTime(time)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..81a053e2cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321); + assert.throws(TypeError, () => datetime.withPlainTime(time)); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js new file mode 100644 index 0000000000..e092ae571b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/year-zero.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withplaintime +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-12-07T03:24:30", + "-000000-12-07T03:24:30+01:00", + "-000000-12-07T03:24:30+00:00[UTC]", +]; +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.ZonedDateTime(0n, timeZone); +invalidStrings.forEach((arg) => { + assert.throws( + RangeError, + () => instance.withPlainTime(arg), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js new file mode 100644 index 0000000000..957ee3d3d1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const withTimeZone = Temporal.ZonedDateTime.prototype.withTimeZone; + +assert.sameValue(typeof withTimeZone, "function"); + +const args = [new Temporal.TimeZone("UTC")]; + +assert.throws(TypeError, () => withTimeZone.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => withTimeZone.apply(null, args), "null"); +assert.throws(TypeError, () => withTimeZone.apply(true, args), "true"); +assert.throws(TypeError, () => withTimeZone.apply("", args), "empty string"); +assert.throws(TypeError, () => withTimeZone.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => withTimeZone.apply(1, args), "1"); +assert.throws(TypeError, () => withTimeZone.apply({}, args), "plain object"); +assert.throws(TypeError, () => withTimeZone.apply(Temporal.ZonedDateTime, args), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => withTimeZone.apply(Temporal.ZonedDateTime.prototype, args), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..c3fe4d8706 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const idOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "id"); +Object.defineProperty(Temporal.TimeZone.prototype, "id", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("id should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.withTimeZone("+01:00"); + +Object.defineProperty(Temporal.TimeZone.prototype, "id", idOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js new file mode 100644 index 0000000000..3f2dd9fb56 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: > + Tests that Temporal.ZonedDateTime.prototype.withTimeZone + 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.ZonedDateTime.prototype.withTimeZone), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.ZonedDateTime.prototype.withTimeZone), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.ZonedDateTime.prototype.withTimeZone), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.ZonedDateTime.prototype.withTimeZone.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js new file mode 100644 index 0000000000..fe8047a98a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/length.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Temporal.ZonedDateTime.prototype.withTimeZone.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.ZonedDateTime.prototype.withTimeZone, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js new file mode 100644 index 0000000000..7b5a2f228c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/name.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Temporal.ZonedDateTime.prototype.withTimeZone.name is "withTimeZone". +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.ZonedDateTime.prototype.withTimeZone, "name", { + value: "withTimeZone", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js new file mode 100644 index 0000000000..6e98f2b5a0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: > + Temporal.ZonedDateTime.prototype.withTimeZone 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.ZonedDateTime.prototype.withTimeZone(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone), false, + "isConstructor(Temporal.ZonedDateTime.prototype.withTimeZone)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js new file mode 100644 index 0000000000..74d4dce98a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: The "withTimeZone" property of Temporal.ZonedDateTime.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.ZonedDateTime.prototype.withTimeZone, + "function", + "`typeof ZonedDateTime.prototype.withTimeZone` is `function`" +); + +verifyProperty(Temporal.ZonedDateTime.prototype, "withTimeZone", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js new file mode 100644 index 0000000000..eb405c94a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/subclassing-ignored.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.ZonedDateTime, + [10n, "UTC"], + "withTimeZone", + ["+01:00"], + (result) => { + assert.sameValue(result.epochNanoseconds, 10n, "epochNanoseconds result"); + assert.sameValue(result.year, 1970, "year result"); + assert.sameValue(result.month, 1, "month result"); + assert.sameValue(result.day, 1, "day result"); + assert.sameValue(result.hour, 1, "hour result"); + assert.sameValue(result.minute, 0, "minute result"); + assert.sameValue(result.second, 0, "second result"); + assert.sameValue(result.millisecond, 0, "millisecond result"); + assert.sameValue(result.microsecond, 0, "microsecond result"); + assert.sameValue(result.nanosecond, 10, "nanosecond result"); + }, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js new file mode 100644 index 0000000000..e547deb149 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-case-insensitive.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Time zone names are case insensitive +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +const timeZone = 'uTc'; +const result = instance.withTimeZone(timeZone); +assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js new file mode 100644 index 0000000000..0aa4bf71f9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js @@ -0,0 +1,65 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance.withTimeZone(timeZone), "bare date-time string is not a time zone"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.withTimeZone(timeZone), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone` + ); +}); + +timeZone = "2021-08-19T17:30Z"; +const result1 = instance.withTimeZone(timeZone); +assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone"); + +timeZone = "2021-08-19T17:30-07:00"; +const result2 = instance.withTimeZone(timeZone); +assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone"); + +timeZone = "2021-08-19T17:30[UTC]"; +const result3 = instance.withTimeZone(timeZone); +assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30Z[UTC]"; +const result4 = instance.withTimeZone(timeZone); +assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30-07:00[UTC]"; +const result5 = instance.withTimeZone(timeZone); +assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js new file mode 100644 index 0000000000..9967c91a9c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-leap-second.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +const result = instance.withTimeZone(timeZone); +assert.sameValue(result.timeZoneId, "UTC", "leap second is a valid ISO string for TimeZone"); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.withTimeZone(timeZone), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..428fd5462d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-multiple-offsets.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +const result = instance.withTimeZone(timeZone); +assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js new file mode 100644 index 0000000000..e9812515ab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-year-zero.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.withTimeZone(timeZone), + "reject minus zero as extended year" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js new file mode 100644 index 0000000000..31e77b645d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: Time zone IDs are valid input for a time zone +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up"); + }, +}); +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(0n, "UTC"); + +["UTC", "+01:30"].forEach((timeZone) => { + const result = instance.withTimeZone(timeZone); + assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`); +}); + +Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js new file mode 100644 index 0000000000..3937d18bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.withtimezone +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.ZonedDateTime(0n, new Temporal.TimeZone("UTC")); + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => instance.withTimeZone(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, () => instance.withTimeZone(timeZone), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js new file mode 100644 index 0000000000..a825d9c545 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const year = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "year").get; + +assert.sameValue(typeof year, "function"); + +assert.throws(TypeError, () => year.call(undefined), "undefined"); +assert.throws(TypeError, () => year.call(null), "null"); +assert.throws(TypeError, () => year.call(true), "true"); +assert.throws(TypeError, () => year.call(""), "empty string"); +assert.throws(TypeError, () => year.call(Symbol()), "symbol"); +assert.throws(TypeError, () => year.call(1), "1"); +assert.throws(TypeError, () => year.call({}), "plain object"); +assert.throws(TypeError, () => year.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => year.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..3e356f42b0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.year +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const yearOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "year"); +Object.defineProperty(Temporal.Calendar.prototype, "year", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("year should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.year; + +Object.defineProperty(Temporal.Calendar.prototype, "year", yearOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..ed29c0748f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.year +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.year; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js new file mode 100644 index 0000000000..127e75615b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: Custom calendar tests for year(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + year(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "year arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.year; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js new file mode 100644 index 0000000000..b71f040c7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: The "year" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "year"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..ee90ba37fd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.year); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..0140df1601 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.year, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..afe8f737b6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.year); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..22eff03c22 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.year); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js new file mode 100644 index 0000000000..10361214ad --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/year/validate-calendar-value.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.year +description: Validate result returned from calendar year() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [NaN, RangeError], + ["string", TypeError], + [{}, TypeError], + [null, TypeError], + [true, TypeError], + [false, TypeError], + [7.1, RangeError], + [-0.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + year() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.year, `${typeof result} ${String(result)} not converted to integer`); +}); + +const preservedResults = [ + -7, +]; + +preservedResults.forEach(result => { + const calendar = new class extends Temporal.Calendar { + year() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.sameValue(instance.year, result, `${typeof result} ${String(result)} preserved`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js new file mode 100644 index 0000000000..fdb41c4b84 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const yearOfWeek = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "yearOfWeek").get; + +assert.sameValue(typeof yearOfWeek, "function"); + +assert.throws(TypeError, () => yearOfWeek.call(undefined), "undefined"); +assert.throws(TypeError, () => yearOfWeek.call(null), "null"); +assert.throws(TypeError, () => yearOfWeek.call(true), "true"); +assert.throws(TypeError, () => yearOfWeek.call(""), "empty string"); +assert.throws(TypeError, () => yearOfWeek.call(Symbol()), "symbol"); +assert.throws(TypeError, () => yearOfWeek.call(1), "1"); +assert.throws(TypeError, () => yearOfWeek.call({}), "plain object"); +assert.throws(TypeError, () => yearOfWeek.call(Temporal.ZonedDateTime), "Temporal.ZonedDateTime"); +assert.throws(TypeError, () => yearOfWeek.call(Temporal.ZonedDateTime.prototype), "Temporal.ZonedDateTime.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js new file mode 100644 index 0000000000..a090ffa2ec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-calendar-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.yearofweek +description: > + Calling the method on an instance constructed with a builtin calendar causes + no observable lookups or calls to calendar methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const yearOfWeekOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "yearOfWeek"); +Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("yearOfWeek should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.yearOfWeek; + +Object.defineProperty(Temporal.Calendar.prototype, "yearOfWeek", yearOfWeekOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js new file mode 100644 index 0000000000..ec06a59283 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/builtin-timezone-no-observable-calls.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.yearofweek +description: > + Calling the method on an instance constructed with a builtin time zone causes + no observable lookups or calls to time zone methods. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor"); +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", { + configurable: true, + enumerable: false, + get() { + TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up"); + }, +}); + +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "iso8601"); +instance.yearOfWeek; + +Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js new file mode 100644 index 0000000000..85418c9f63 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/custom.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: Custom calendar tests for yearOfWeek(). +includes: [compareArray.js] +features: [Temporal] +---*/ + +let calls = 0; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + yearOfWeek(...args) { + ++calls; + assert.compareArray(args.map(String), [instance].map((arg) => arg.toPlainDateTime().toString()), "yearOfWeek arguments"); + return 7; + } +} + +const calendar = new CustomCalendar(); +const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); +const result = instance.yearOfWeek; +assert.sameValue(result, 7, "result"); +assert.sameValue(calls, 1, "calls"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js new file mode 100644 index 0000000000..2cd9360938 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: The "yearOfWeek" property of Temporal.ZonedDateTime.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.ZonedDateTime.prototype, "yearOfWeek"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..18aa0dea68 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.yearOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..7050c29ded --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => datetime.yearOfWeek, + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..0eb761b86c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => datetime.yearOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..dfc1f0ebec --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => datetime.yearOfWeek); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js new file mode 100644 index 0000000000..9bd447f9a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/prototype/yearOfWeek/validate-calendar-value.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.zoneddatetime.prototype.yearofweek +description: Validate result returned from calendar yearOfWeek() method +features: [Temporal] +---*/ + +const badResults = [ + [undefined, TypeError], + [Infinity, RangeError], + [-Infinity, RangeError], + [Symbol("foo"), TypeError], + [7n, TypeError], + [NaN, RangeError], + ["string", TypeError], + [{}, TypeError], + [null, TypeError], + [true, TypeError], + [false, TypeError], + [7.1, RangeError], + [-0.1, RangeError], + ["7", TypeError], + ["7.5", TypeError], + [{valueOf() { return 7; }}, TypeError], +]; + +badResults.forEach(([result, error]) => { + const calendar = new class extends Temporal.Calendar { + yearOfWeek() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.throws(error, () => instance.yearOfWeek, `${typeof result} ${String(result)} not converted to integer`); +}); + +const preservedResults = [ + -7, +]; + +preservedResults.forEach(result => { + const calendar = new class extends Temporal.Calendar { + yearOfWeek() { + return result; + } + }("iso8601"); + const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + assert.sameValue(instance.yearOfWeek, result, `${typeof result} ${String(result)} preserved`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js new file mode 100644 index 0000000000..54360455db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/subclass.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Test for Temporal.ZonedDateTime subclassing. +features: [Temporal] +---*/ + +class CustomZonedDateTime extends Temporal.ZonedDateTime { +} + +const instance = new CustomZonedDateTime(0n, "UTC"); +assert.sameValue(instance.epochNanoseconds, 0n); +assert.sameValue(Object.getPrototypeOf(instance), CustomZonedDateTime.prototype, "Instance of CustomZonedDateTime"); +assert(instance instanceof CustomZonedDateTime, "Instance of CustomZonedDateTime"); +assert(instance instanceof Temporal.ZonedDateTime, "Instance of Temporal.ZonedDateTime"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js new file mode 100644 index 0000000000..afcda49591 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-case-insensitive.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime.prototype.constructor +description: Time zone names are case insensitive +features: [Temporal] +---*/ + +const timeZone = 'uTc'; +const result = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result.timeZoneId, 'UTC', `Time zone created from string "${timeZone}"`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js new file mode 100644 index 0000000000..16e8513a5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js @@ -0,0 +1,63 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, timeZone), "bare date-time string is not a time zone"); + +[ + "2021-08-19T17:30-07:00:01", + "2021-08-19T17:30-07:00:00", + "2021-08-19T17:30-07:00:00.1", + "2021-08-19T17:30-07:00:00.0", + "2021-08-19T17:30-07:00:00.01", + "2021-08-19T17:30-07:00:00.00", + "2021-08-19T17:30-07:00:00.001", + "2021-08-19T17:30-07:00:00.000", + "2021-08-19T17:30-07:00:00.0001", + "2021-08-19T17:30-07:00:00.0000", + "2021-08-19T17:30-07:00:00.00001", + "2021-08-19T17:30-07:00:00.00000", + "2021-08-19T17:30-07:00:00.000001", + "2021-08-19T17:30-07:00:00.000000", + "2021-08-19T17:30-07:00:00.0000001", + "2021-08-19T17:30-07:00:00.0000000", + "2021-08-19T17:30-07:00:00.00000001", + "2021-08-19T17:30-07:00:00.00000000", + "2021-08-19T17:30-07:00:00.000000001", + "2021-08-19T17:30-07:00:00.000000000", +].forEach((timeZone) => { + assert.throws( + RangeError, + () => new Temporal.ZonedDateTime(0n, timeZone), + `ISO string ${timeZone} with a sub-minute offset is not a valid time zone` + ); +}); + +timeZone = "2021-08-19T17:30Z"; +const result1 = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result1.timeZoneId, "UTC", "date-time + Z is UTC time zone"); + +timeZone = "2021-08-19T17:30-07:00"; +const result2 = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result2.timeZoneId, "-07:00", "date-time + offset is the offset time zone"); + +timeZone = "2021-08-19T17:30[UTC]"; +const result3 = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result3.timeZoneId, "UTC", "date-time + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30Z[UTC]"; +const result4 = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result4.timeZoneId, "UTC", "date-time + Z + IANA annotation is the IANA time zone"); + +timeZone = "2021-08-19T17:30-07:00[UTC]"; +const result5 = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result5.timeZoneId, "UTC", "date-time + offset + IANA annotation is the IANA time zone"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js new file mode 100644 index 0000000000..09113ed5f2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-leap-second.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +const result = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result.timeZoneId, "UTC", "Time zone string determined from bracket name"); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => new Temporal.ZonedDateTime(0n, timeZone), "leap second in time zone name not valid"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js new file mode 100644 index 0000000000..74d3887c5b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string-multiple-offsets.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Time zone parsing from ISO strings uses the bracketed offset, not the ISO string offset +features: [Temporal] +---*/ + +const timeZone = "2021-08-19T17:30:45.123456789-12:12[+01:46]"; + +const result = new Temporal.ZonedDateTime(0n, timeZone); +assert.sameValue(result.timeZoneId, "+01:46", "Time zone string determined from bracket name"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js new file mode 100644 index 0000000000..6f86ee18fe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-string.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +["UTC", "+01:30"].forEach((timeZone) => { + const result = new Temporal.ZonedDateTime(0n, timeZone); + assert.sameValue(result.getISOFields().timeZone, timeZone, `time zone slot should store string "${timeZone}"`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js new file mode 100644 index 0000000000..4c159defbc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/ZonedDateTime/timezone-wrong-type.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.zoneddatetime +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const primitiveTests = [ + [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"], +]; + +for (const [timeZone, description] of primitiveTests) { + assert.throws( + typeof timeZone === 'string' ? RangeError : TypeError, + () => new Temporal.ZonedDateTime(0n, timeZone), + `${description} does not convert to a valid ISO string` + ); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => new Temporal.ZonedDateTime(0n, timeZone), `${description} is not a valid object and does not convert to a string`); +} + +reportCompare(0, 0); |