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

View file

@ -43,18 +43,18 @@ Temporal.ZonedDateTime.prototype
"day", "dayOfWeek", "dayOfYear",
"daysInMonth", "daysInWeek", "daysInYear",
"epochMilliseconds", "epochNanoseconds", "equals",
"era", "eraYear", "hour",
"hoursInDay", "inLeapYear", "microsecond",
"millisecond", "minute", "month",
"monthCode", "monthsInYear", "nanosecond",
"offset", "offsetNanoseconds", "round",
"second", "since", "startOfDay",
"subtract", "timeZoneId", "toInstant",
"toJSON", "toLocaleString", "toPlainDate",
"toPlainDateTime", "toPlainTime", "toString",
"until", "valueOf", "weekOfYear",
"with", "withCalendar", "withPlainTime",
"withTimeZone", "year"
"era", "eraYear", "getTimeZoneTransition",
"hour", "hoursInDay", "inLeapYear",
"microsecond", "millisecond", "minute",
"month", "monthCode", "monthsInYear",
"nanosecond", "offset", "offsetNanoseconds",
"round", "second", "since",
"startOfDay", "subtract", "timeZoneId",
"toInstant", "toJSON", "toLocaleString",
"toPlainDate", "toPlainDateTime", "toPlainTime",
"toString", "until", "valueOf",
"weekOfYear", "with", "withCalendar",
"withPlainTime", "withTimeZone", "year"
]
Temporal.PlainDate.prototype
[

View file

@ -6,3 +6,4 @@ UTC
undefined
undefined
1 day, 6 hr, 30 min
2020-03-08T03:00:00-04:00[America/New_York]

View file

@ -12,3 +12,8 @@ console.log(zoned.timeZone);
const duration = Temporal.Duration.from("P1DT6H30M");
console.log(duration.toLocaleString("en-US"));
const transition = Temporal.ZonedDateTime.from(
"2020-01-01T00:00:00-05:00[America/New_York]",
).getTimeZoneTransition("next");
console.log(transition);