diff --git a/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..15cfaef9a6 --- /dev/null +++ b/test/built-ins/Temporal/Duration/compare/relativeto-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,94 @@ +// 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.duration.compare +description: > + Correct time zone calls are made when converting a ZonedDateTime-like + relativeTo property bag denoting an ambiguous wall-clock time +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const actual = []; + +const duration1 = new Temporal.Duration(0, 0, 0, 1); +const duration2 = new Temporal.Duration(0, 0, 0, 2); + +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"); + +let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}); + +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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToRelativeTemporalObject + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..a4c7e3e2f7 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// 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.duration.prototype.add +description: > + Correct time zone calls are made when converting a ZonedDateTime-like + relativeTo 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 instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); + +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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToRelativeTemporalObject + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..5f232ff2c5 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// 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.duration.prototype.round +description: > + Correct time zone calls are made when converting a ZonedDateTime-like + relativeTo 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 instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.round({ largestUnit: "years", relativeTo }); + +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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.round({ largestUnit: "years", relativeTo }); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToRelativeTemporalObject + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..ca3938dd12 --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// 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.duration.prototype.subtract +description: > + Correct time zone calls are made when converting a ZonedDateTime-like + relativeTo 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 instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); + +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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToRelativeTemporalObject + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..1134a8ba7c --- /dev/null +++ b/test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,93 @@ +// 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.duration.prototype.total +description: > + Correct time zone calls are made when converting a ZonedDateTime-like + relativeTo 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 instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.total({ unit: "days", relativeTo }); + +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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject + expectedSpringForward, + "order of operations converting property bag at skipped wall-clock time" +); +actual.splice(0); // clear + +relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar }; +instance.total({ unit: "days", relativeTo }); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToRelativeTemporalObject + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js new file mode 100644 index 0000000000..093c68baf7 --- /dev/null +++ b/test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/order-of-operations.js @@ -0,0 +1,95 @@ +// 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.plaindate.prototype.tozoneddatetime +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + "get item.timeZone", + "has item.timeZone.getOffsetNanosecondsFor", + "has item.timeZone.getPossibleInstantsFor", + "has item.timeZone.id", + "get item.plainTime", + // ToTemporalTime + "get item.plainTime.hour", + "get item.plainTime.hour.valueOf", + "call item.plainTime.hour.valueOf", + "get item.plainTime.microsecond", + "get item.plainTime.microsecond.valueOf", + "call item.plainTime.microsecond.valueOf", + "get item.plainTime.millisecond", + "get item.plainTime.millisecond.valueOf", + "call item.plainTime.millisecond.valueOf", + "get item.plainTime.minute", + "get item.plainTime.minute.valueOf", + "call item.plainTime.minute.valueOf", + "get item.plainTime.nanosecond", + "get item.plainTime.nanosecond.valueOf", + "call item.plainTime.nanosecond.valueOf", + "get item.plainTime.second", + "get item.plainTime.second.valueOf", + "call item.plainTime.second.valueOf", + // GetInstantFor + "get item.timeZone.getPossibleInstantsFor", + "call item.timeZone.getPossibleInstantsFor", +]; + +const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); +const instance = new Temporal.PlainDate(2000, 1, 1, calendar); +const springForwardInstance = new Temporal.PlainDate(2000, 4, 2, calendar); +const fallBackInstance = new Temporal.PlainDate(2000, 10, 29, calendar); +actual.splice(0); // clear calendar calls that happened in constructors + +const plainTime = TemporalHelpers.propertyBagObserver(actual, { + hour: 2, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}, "item.plainTime"); +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); +const item = TemporalHelpers.propertyBagObserver(actual, { + plainTime, + timeZone, +}, "item"); + +instance.toZonedDateTime(item); +assert.compareArray(actual, expected, "order of operations at normal wall-clock time"); +actual.splice(0); // clear + +const plainTime130 = TemporalHelpers.propertyBagObserver(actual, { + hour: 1, + minute: 30, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, +}, "item.plainTime"); +const item130 = TemporalHelpers.propertyBagObserver(actual, { + plainTime: plainTime130, + timeZone, +}, "item"); + +fallBackInstance.toZonedDateTime(item130); +assert.compareArray(actual, expected, "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +springForwardInstance.toZonedDateTime(item); +assert.compareArray(actual, expected.concat([ + "get item.timeZone.getOffsetNanosecondsFor", + "call item.timeZone.getOffsetNanosecondsFor", + "call item.timeZone.getOffsetNanosecondsFor", + "get item.timeZone.getPossibleInstantsFor", + "call item.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js index 813aaaac68..8d024ce4a2 100644 --- a/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js +++ b/test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/order-of-operations.js @@ -25,12 +25,38 @@ const actual = []; const calendar = TemporalHelpers.calendarObserver(actual, "this.calendar"); const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); -// clear observable operations that occurred during the constructor call +const fallBackInstance = new Temporal.PlainDateTime(2000, 10, 29, 1, 30, 0, 0, 0, 0, calendar); +const springForwardInstance = new Temporal.PlainDateTime(2000, 4, 2, 2, 30, 0, 0, 0, 0, calendar); +// clear observable operations that occurred during the constructor calls actual.splice(0); -const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone"); +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); const options = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "compatible" }, "options"); instance.toZonedDateTime(timeZone, options); -assert.compareArray(actual, expected, "order of operations"); +assert.compareArray(actual, expected, "order of operations at normal wall-clock time"); +actual.splice(0); // clear + +fallBackInstance.toZonedDateTime(timeZone, options); +assert.compareArray(actual, expected, "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +springForwardInstance.toZonedDateTime(timeZone, options); +assert.compareArray(actual, expected.concat([ + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear + +const rejectOptions = TemporalHelpers.propertyBagObserver(actual, { disambiguation: "reject" }, "options"); +assert.throws(RangeError, () => springForwardInstance.toZonedDateTime(timeZone, rejectOptions)); +assert.compareArray(actual, expected, "order of operations at skipped wall-clock time with disambiguation: reject"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js new file mode 100644 index 0000000000..5f2622396a --- /dev/null +++ b/test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/order-of-operations.js @@ -0,0 +1,116 @@ +// 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.plaintime.prototype.tozoneddatetime +description: User code calls happen in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + "get item.plainDate", + "get item.plainDate.calendar", + "has item.plainDate.calendar.dateAdd", + "has item.plainDate.calendar.dateFromFields", + "has item.plainDate.calendar.dateUntil", + "has item.plainDate.calendar.day", + "has item.plainDate.calendar.dayOfWeek", + "has item.plainDate.calendar.dayOfYear", + "has item.plainDate.calendar.daysInMonth", + "has item.plainDate.calendar.daysInWeek", + "has item.plainDate.calendar.daysInYear", + "has item.plainDate.calendar.fields", + "has item.plainDate.calendar.id", + "has item.plainDate.calendar.inLeapYear", + "has item.plainDate.calendar.mergeFields", + "has item.plainDate.calendar.month", + "has item.plainDate.calendar.monthCode", + "has item.plainDate.calendar.monthDayFromFields", + "has item.plainDate.calendar.monthsInYear", + "has item.plainDate.calendar.weekOfYear", + "has item.plainDate.calendar.year", + "has item.plainDate.calendar.yearMonthFromFields", + "has item.plainDate.calendar.yearOfWeek", + "get item.plainDate.calendar.fields", + "call item.plainDate.calendar.fields", + "get item.plainDate.day", + "get item.plainDate.day.valueOf", + "call item.plainDate.day.valueOf", + "get item.plainDate.month", + "get item.plainDate.month.valueOf", + "call item.plainDate.month.valueOf", + "get item.plainDate.monthCode", + "get item.plainDate.monthCode.toString", + "call item.plainDate.monthCode.toString", + "get item.plainDate.year", + "get item.plainDate.year.valueOf", + "call item.plainDate.year.valueOf", + "get item.plainDate.calendar.dateFromFields", + "call item.plainDate.calendar.dateFromFields", + "get item.timeZone", + "has item.timeZone.getOffsetNanosecondsFor", + "has item.timeZone.getPossibleInstantsFor", + "has item.timeZone.id", + "get item.timeZone.getPossibleInstantsFor", + "call item.timeZone.getPossibleInstantsFor", +]; + +const calendar = TemporalHelpers.calendarObserver(actual, "item.plainDate.calendar"); +const instance = new Temporal.PlainTime(2, 30); + +const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone(); +const timeZone = TemporalHelpers.timeZoneObserver(actual, "item.timeZone", { + getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor, + getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor, +}); + +const plainDate = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + calendar, +}, "item.plainDate"); +instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, { + plainDate, + timeZone, +}, "item")); +assert.compareArray(actual, expected, "order of operations at normal wall-clock time"); +actual.splice(0); // clear + +const fallBackPlainDate = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 10, + monthCode: "M10", + day: 29, + calendar, +}, "item.plainDate"); +const fallBackInstance = new Temporal.PlainTime(1, 30); +fallBackInstance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, { + plainDate: fallBackPlainDate, + timeZone, +}, "item")); +assert.compareArray(actual, expected, "order of operations at repeated wall-clock time"); +actual.splice(0); // clear + +const springForwardPlainDate = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 4, + monthCode: "M04", + day: 2, + calendar, +}, "item.plainDate"); +instance.toZonedDateTime(TemporalHelpers.propertyBagObserver(actual, { + plainDate: springForwardPlainDate, + timeZone, +}, "item")); +assert.compareArray(actual, expected.concat([ + "get item.timeZone.getOffsetNanosecondsFor", + "call item.timeZone.getOffsetNanosecondsFor", + "call item.timeZone.getOffsetNanosecondsFor", + "get item.timeZone.getPossibleInstantsFor", + "call item.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..dbd661ecb1 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,141 @@ +// 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", + // CalendarFields + "get one.calendar.fields", + "call one.calendar.fields", + // ToTemporalTimeZoneSlotValue + "has one.timeZone.getOffsetNanosecondsFor", + "has one.timeZone.getPossibleInstantsFor", + "has one.timeZone.id", + // InterpretTemporalDateTimeFields + "get one.calendar.dateFromFields", + "call one.calendar.dateFromFields", +]; + +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", + // CalendarFields + "get two.calendar.fields", + "call two.calendar.fields", + // ToTemporalTimeZoneSlotValue + "has two.timeZone.getOffsetNanosecondsFor", + "has two.timeZone.getPossibleInstantsFor", + "has two.timeZone.id", + // InterpretTemporalDateTimeFields + "get two.calendar.dateFromFields", + "call two.calendar.dateFromFields", +]; + +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([ + // InterpretISODateTimeOffset + "get one.timeZone.getPossibleInstantsFor", + "call one.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get one.timeZone.getOffsetNanosecondsFor", + "call one.timeZone.getOffsetNanosecondsFor", + "call one.timeZone.getOffsetNanosecondsFor", + "get one.timeZone.getPossibleInstantsFor", + "call one.timeZone.getPossibleInstantsFor", +], expectedTwo, [ + // InterpretISODateTimeOffset + "get two.timeZone.getPossibleInstantsFor", + "call two.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get two.timeZone.getOffsetNanosecondsFor", + "call two.timeZone.getOffsetNanosecondsFor", + "call two.timeZone.getOffsetNanosecondsFor", + "get two.timeZone.getPossibleInstantsFor", + "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([ + // InterpretISODateTimeOffset + "get one.timeZone.getPossibleInstantsFor", + "call one.timeZone.getPossibleInstantsFor", +], expectedTwo, [ + // InterpretISODateTimeOffset + "get two.timeZone.getPossibleInstantsFor", + "call two.timeZone.getPossibleInstantsFor", +]); +assert.compareArray(actual, expectedFallBack, "order of operations converting property bags at repeated wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..c08780dffe --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,142 @@ +// 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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", +]; + +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 + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "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 + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "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 + "get timeZone.getPossibleInstantsFor", + "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 + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + "get timeZone.getOffsetNanosecondsFor", + "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 + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", +]), "order of operations converting property bag at repeated wall-clock time with offset: reject"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..11ef9d460f --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,94 @@ +// 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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "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); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToTemporalZonedDateTime + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js new file mode 100644 index 0000000000..63e93affa6 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/order-of-operations.js @@ -0,0 +1,109 @@ +// 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 = [ + // GetPlainDateTimeFor + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "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, [ + // GetPlainDateTimeFor + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // Note, no call to dateAdd as addition takes place in the ISO calendar + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // Note, no call to dateAdd here either + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +], "order of operations with both midnights at skipped wall-clock times"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js index cf37b4287a..ab25a4977f 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/round/order-of-operations.js @@ -41,14 +41,126 @@ const options = TemporalHelpers.propertyBagObserver(actual, { 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"), - TemporalHelpers.calendarObserver(actual, "this.calendar"), + 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 on the constructor +// 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", + // GetPlainDateTimeFor on receiver's instant + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // AddZonedDateTime + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + "get this.timeZone.getOffsetNanosecondsFor", + "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", + // GetPlainDateTimeFor on receiver's instant + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // AddDaysToZonedDateTime + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // InterpretISODateTimeOffset + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "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 diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..389cebfac9 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,94 @@ +// 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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "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); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToTemporalZonedDateTime + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js index 9e067ee70a..41af64d251 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js @@ -269,7 +269,6 @@ assert.compareArray(actual, [ "get other.timeZone.getOffsetNanosecondsFor", "call other.timeZone.getOffsetNanosecondsFor", // NOTE: extra because of wall-clock time ambiguity: - "get other.timeZone.getOffsetNanosecondsFor", "call other.timeZone.getOffsetNanosecondsFor", // CalendarEquals "get this.calendar.id", diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js new file mode 100644 index 0000000000..3f47efd994 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/startOfDay/order-of-operations.js @@ -0,0 +1,67 @@ +// 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 = [ + // GetPlainDateTimeFor + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor on preceding midnight + "get this.timeZone.getPossibleInstantsFor", + "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 + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations with preceding midnight at skipped wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js new file mode 100644 index 0000000000..be4ffb470a --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-ambiguous-wall-clock-time.js @@ -0,0 +1,94 @@ +// 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", + // CalendarFields + "get calendar.fields", + "call calendar.fields", + // ToTemporalTimeZoneSlotValue + "has timeZone.getOffsetNanosecondsFor", + "has timeZone.getPossibleInstantsFor", + "has timeZone.id", + // InterpretTemporalDateTimeFields + "get calendar.dateFromFields", + "call calendar.dateFromFields", +]; + +const expectedSpringForward = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", + // DisambiguatePossibleInstants + "get timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "call timeZone.getOffsetNanosecondsFor", + "get timeZone.getPossibleInstantsFor", + "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); + +const expectedFallBack = expected.concat([ + // InterpretISODateTimeOffset + "get timeZone.getPossibleInstantsFor", + "call timeZone.getPossibleInstantsFor", +]); +assert.compareArray( + actual.slice(0, expectedFallBack.length), // ignore operations after ToTemporalZonedDateTime + expectedFallBack, + "order of operations converting property bag at repeated wall-clock time" +); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js index a7ddb56da3..9c8b184934 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js @@ -269,7 +269,6 @@ assert.compareArray(actual, [ "get other.timeZone.getOffsetNanosecondsFor", "call other.timeZone.getOffsetNanosecondsFor", // NOTE: extra because of wall-clock time ambiguity: - "get other.timeZone.getOffsetNanosecondsFor", "call other.timeZone.getOffsetNanosecondsFor", // CalendarEquals "get this.calendar.id", diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js index 0374bdcb1d..d200fa2196 100644 --- a/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/with/order-of-operations.js @@ -120,3 +120,55 @@ const options = TemporalHelpers.propertyBagObserver(actual, { 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", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js new file mode 100644 index 0000000000..be5fd898f5 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainDate/order-of-operations.js @@ -0,0 +1,112 @@ +// 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.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", + "get plainDateLike.calendar.dateFromFields", + "call plainDateLike.calendar.dateFromFields", + // GetPlainDateTimeFor + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // ConsolidateCalendars + "get this.calendar.id", + "get plainDateLike.calendar.id", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "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([ + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js new file mode 100644 index 0000000000..7ef452f219 --- /dev/null +++ b/test/built-ins/Temporal/ZonedDateTime/prototype/withPlainTime/order-of-operations.js @@ -0,0 +1,86 @@ +// 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", + // GetPlainDateTimeFor + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + // GetInstantFor + "get this.timeZone.getPossibleInstantsFor", + "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([ + "get this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "call this.timeZone.getOffsetNanosecondsFor", + "get this.timeZone.getPossibleInstantsFor", + "call this.timeZone.getPossibleInstantsFor", +]), "order of operations at skipped wall-clock time"); +actual.splice(0); // clear