fix(unstable/temporal): implement Temporal.ZonedDateTime.getTimeZoneTransition (#27770)

This patches Temporal to support the `getTimeZoneTransition` method on
`ZonedDateTime`.

See https://github.com/denoland/deno/issues/27731.

Co-authored-by: printfn <printfn@users.noreply.github.com>
This commit is contained in:
printfn 2025-02-18 03:02:34 +13:00 committed by GitHub
parent 36933fb0b8
commit 70d775c57d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 155 additions and 204 deletions

View file

@ -42,6 +42,7 @@ const {
ObjectSetPrototypeOf,
PromisePrototypeThen,
PromiseResolve,
RangeError,
StringPrototypePadEnd,
Symbol,
SymbolIterator,
@ -473,6 +474,136 @@ function exposeUnstableFeaturesForWindowOrWorkerGlobalScope(unstableFeatures) {
}
}
function updateTemporal() {
// Removes the obsoleted `Temporal` API.
// https://github.com/tc39/proposal-temporal/pull/2895
// https://github.com/tc39/proposal-temporal/pull/2914
// https://github.com/tc39/proposal-temporal/pull/2925
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
throw "V8 removes obsoleted Temporal API now, no need to delete them";
}
delete globalThis.Temporal.Instant.fromEpochSeconds;
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
delete globalThis.Temporal.Instant.prototype.epochSeconds;
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
delete globalThis.Temporal.PlainTime.prototype.calendar;
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
delete globalThis.Temporal.Now.zonedDateTime;
delete globalThis.Temporal.Now.plainDateTime;
delete globalThis.Temporal.Now.plainDate;
delete globalThis.Temporal.Calendar;
delete globalThis.Temporal.TimeZone;
// Modify `Temporal.Calendar` to calendarId string
ArrayPrototypeForEach([
globalThis.Temporal.PlainDate,
globalThis.Temporal.PlainDateTime,
globalThis.Temporal.PlainMonthDay,
globalThis.Temporal.PlainYearMonth,
globalThis.Temporal.ZonedDateTime,
], (target) => {
const getCalendar =
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
ObjectDefineProperty(target.prototype, "calendarId", {
__proto__: null,
get: function calendarId() {
return FunctionPrototypeCall(getCalendar, this).id;
},
enumerable: false,
configurable: true,
});
delete target.prototype.calendar;
});
// Modify `Temporal.TimeZone` to timeZoneId string
{
const getTimeZone = ObjectGetOwnPropertyDescriptor(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZone",
).get;
ObjectDefineProperty(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZoneId",
{
__proto__: null,
get: function timeZoneId() {
return FunctionPrototypeCall(getTimeZone, this).id;
},
enumerable: false,
configurable: true,
},
);
ObjectAssign(globalThis.Temporal.ZonedDateTime.prototype, {
getTimeZoneTransition(options) {
if (options === undefined) {
throw new TypeError("options parameter is required");
}
if (typeof options === "string") {
options = {
direction: options,
};
}
const direction = options.direction;
if (direction === undefined) {
throw new TypeError("direction option is required");
}
const tz = FunctionPrototypeCall(getTimeZone, this);
let resultInstant;
switch (direction) {
case "next":
resultInstant = tz.getNextTransition(this.toInstant());
break;
case "previous":
resultInstant = tz.getPreviousTransition(this.toInstant());
break;
default:
throw new RangeError(
`direction must be one of next, previous, not ${options.direction}`,
);
}
return resultInstant?.toZonedDateTimeISO(tz.id) ?? null;
},
});
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
}
{
const nowTimeZone = globalThis.Temporal.Now.timeZone;
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
__proto__: null,
value: function timeZoneId() {
return nowTimeZone().id;
},
writable: true,
enumerable: false,
configurable: true,
});
delete globalThis.Temporal.Now.timeZone;
}
// deno-lint-ignore prefer-primordials
if (new Temporal.Duration().toLocaleString("en-US") !== "PT0S") {
throw "V8 supports Temporal.Duration.prototype.toLocaleString now, no need to shim it";
}
shimTemporalDurationToLocaleString();
}
function shimTemporalDurationToLocaleString() {
const DurationFormat = Intl.DurationFormat;
if (!DurationFormat) {
@ -803,102 +934,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
} else {
// Removes the obsoleted `Temporal` API.
// https://github.com/tc39/proposal-temporal/pull/2895
// https://github.com/tc39/proposal-temporal/pull/2914
// https://github.com/tc39/proposal-temporal/pull/2925
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
throw "V8 removes obsoleted Temporal API now, no need to delete them";
}
delete globalThis.Temporal.Instant.fromEpochSeconds;
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
delete globalThis.Temporal.Instant.prototype.epochSeconds;
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
delete globalThis.Temporal.PlainTime.prototype.calendar;
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
delete globalThis.Temporal.Now.zonedDateTime;
delete globalThis.Temporal.Now.plainDateTime;
delete globalThis.Temporal.Now.plainDate;
delete globalThis.Temporal.Calendar;
delete globalThis.Temporal.TimeZone;
// Modify `Temporal.Calendar` to calendarId string
ArrayPrototypeForEach([
globalThis.Temporal.PlainDate,
globalThis.Temporal.PlainDateTime,
globalThis.Temporal.PlainMonthDay,
globalThis.Temporal.PlainYearMonth,
globalThis.Temporal.ZonedDateTime,
], (target) => {
const getCalendar =
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
ObjectDefineProperty(target.prototype, "calendarId", {
__proto__: null,
get: function calendarId() {
return FunctionPrototypeCall(getCalendar, this).id;
},
enumerable: false,
configurable: true,
});
delete target.prototype.calendar;
});
// Modify `Temporal.TimeZone` to timeZoneId string
{
const getTimeZone = ObjectGetOwnPropertyDescriptor(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZone",
).get;
ObjectDefineProperty(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZoneId",
{
__proto__: null,
get: function timeZoneId() {
return FunctionPrototypeCall(getTimeZone, this).id;
},
enumerable: false,
configurable: true,
},
);
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
}
{
const nowTimeZone = globalThis.Temporal.Now.timeZone;
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
__proto__: null,
value: function timeZoneId() {
return nowTimeZone().id;
},
writable: true,
enumerable: false,
configurable: true,
});
delete globalThis.Temporal.Now.timeZone;
}
// deno-lint-ignore prefer-primordials
if (new Temporal.Duration().toLocaleString("en-US") !== "PT0S") {
throw "V8 supports Temporal.Duration.prototype.toLocaleString now, no need to shim it";
}
shimTemporalDurationToLocaleString();
updateTemporal();
}
// Setup `Deno` global - we're actually overriding already existing global
@ -1014,98 +1050,7 @@ function bootstrapWorkerRuntime(
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
} else {
// Removes the obsoleted `Temporal` API.
// https://github.com/tc39/proposal-temporal/pull/2895
// https://github.com/tc39/proposal-temporal/pull/2914
// https://github.com/tc39/proposal-temporal/pull/2925
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
throw "V8 removes obsoleted Temporal API now, no need to delete them";
}
delete globalThis.Temporal.Instant.fromEpochSeconds;
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
delete globalThis.Temporal.Instant.prototype.epochSeconds;
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
delete globalThis.Temporal.PlainTime.prototype.calendar;
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
delete globalThis.Temporal.Now.zonedDateTime;
delete globalThis.Temporal.Now.plainDateTime;
delete globalThis.Temporal.Now.plainDate;
delete globalThis.Temporal.Calendar;
delete globalThis.Temporal.TimeZone;
// Modify `Temporal.Calendar` to calendarId string
ArrayPrototypeForEach([
globalThis.Temporal.PlainDate,
globalThis.Temporal.PlainDateTime,
globalThis.Temporal.PlainMonthDay,
globalThis.Temporal.PlainYearMonth,
globalThis.Temporal.ZonedDateTime,
], (target) => {
const getCalendar =
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
ObjectDefineProperty(target.prototype, "calendarId", {
__proto__: null,
get: function calendarId() {
return FunctionPrototypeCall(getCalendar, this).id;
},
enumerable: false,
configurable: true,
});
delete target.prototype.calendar;
});
// Modify `Temporal.TimeZone` to timeZoneId string
{
const getTimeZone = ObjectGetOwnPropertyDescriptor(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZone",
).get;
ObjectDefineProperty(
globalThis.Temporal.ZonedDateTime.prototype,
"timeZoneId",
{
__proto__: null,
get: function timeZoneId() {
return FunctionPrototypeCall(getTimeZone, this).id;
},
enumerable: false,
configurable: true,
},
);
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
}
{
const nowTimeZone = globalThis.Temporal.Now.timeZone;
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
__proto__: null,
value: function timeZoneId() {
return nowTimeZone().id;
},
writable: true,
enumerable: false,
configurable: true,
});
delete globalThis.Temporal.Now.timeZone;
}
shimTemporalDurationToLocaleString();
updateTemporal();
}
// Setup `Deno` global - we're actually overriding already existing global