Skip to content

Commit 70d775c

Browse files
authored
fix(unstable/temporal): implement Temporal.ZonedDateTime.getTimeZoneTransition (#27770)
This patches Temporal to support the `getTimeZoneTransition` method on `ZonedDateTime`. See #27731. Co-authored-by: printfn <[email protected]>
1 parent 36933fb commit 70d775c

File tree

4 files changed

+155
-204
lines changed

4 files changed

+155
-204
lines changed

runtime/js/99_main.js

+133-188
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const {
4242
ObjectSetPrototypeOf,
4343
PromisePrototypeThen,
4444
PromiseResolve,
45+
RangeError,
4546
StringPrototypePadEnd,
4647
Symbol,
4748
SymbolIterator,
@@ -473,6 +474,136 @@ function exposeUnstableFeaturesForWindowOrWorkerGlobalScope(unstableFeatures) {
473474
}
474475
}
475476

477+
function updateTemporal() {
478+
// Removes the obsoleted `Temporal` API.
479+
// https://github.com/tc39/proposal-temporal/pull/2895
480+
// https://github.com/tc39/proposal-temporal/pull/2914
481+
// https://github.com/tc39/proposal-temporal/pull/2925
482+
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
483+
throw "V8 removes obsoleted Temporal API now, no need to delete them";
484+
}
485+
delete globalThis.Temporal.Instant.fromEpochSeconds;
486+
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
487+
delete globalThis.Temporal.Instant.prototype.epochSeconds;
488+
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
489+
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
490+
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
491+
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
492+
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
493+
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
494+
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
495+
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
496+
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
497+
delete globalThis.Temporal.PlainTime.prototype.calendar;
498+
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
499+
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
500+
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
501+
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
502+
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
503+
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
504+
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
505+
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
506+
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
507+
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
508+
delete globalThis.Temporal.Now.zonedDateTime;
509+
delete globalThis.Temporal.Now.plainDateTime;
510+
delete globalThis.Temporal.Now.plainDate;
511+
delete globalThis.Temporal.Calendar;
512+
delete globalThis.Temporal.TimeZone;
513+
514+
// Modify `Temporal.Calendar` to calendarId string
515+
ArrayPrototypeForEach([
516+
globalThis.Temporal.PlainDate,
517+
globalThis.Temporal.PlainDateTime,
518+
globalThis.Temporal.PlainMonthDay,
519+
globalThis.Temporal.PlainYearMonth,
520+
globalThis.Temporal.ZonedDateTime,
521+
], (target) => {
522+
const getCalendar =
523+
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
524+
ObjectDefineProperty(target.prototype, "calendarId", {
525+
__proto__: null,
526+
get: function calendarId() {
527+
return FunctionPrototypeCall(getCalendar, this).id;
528+
},
529+
enumerable: false,
530+
configurable: true,
531+
});
532+
delete target.prototype.calendar;
533+
});
534+
535+
// Modify `Temporal.TimeZone` to timeZoneId string
536+
{
537+
const getTimeZone = ObjectGetOwnPropertyDescriptor(
538+
globalThis.Temporal.ZonedDateTime.prototype,
539+
"timeZone",
540+
).get;
541+
ObjectDefineProperty(
542+
globalThis.Temporal.ZonedDateTime.prototype,
543+
"timeZoneId",
544+
{
545+
__proto__: null,
546+
get: function timeZoneId() {
547+
return FunctionPrototypeCall(getTimeZone, this).id;
548+
},
549+
enumerable: false,
550+
configurable: true,
551+
},
552+
);
553+
ObjectAssign(globalThis.Temporal.ZonedDateTime.prototype, {
554+
getTimeZoneTransition(options) {
555+
if (options === undefined) {
556+
throw new TypeError("options parameter is required");
557+
}
558+
if (typeof options === "string") {
559+
options = {
560+
direction: options,
561+
};
562+
}
563+
const direction = options.direction;
564+
if (direction === undefined) {
565+
throw new TypeError("direction option is required");
566+
}
567+
const tz = FunctionPrototypeCall(getTimeZone, this);
568+
let resultInstant;
569+
switch (direction) {
570+
case "next":
571+
resultInstant = tz.getNextTransition(this.toInstant());
572+
break;
573+
case "previous":
574+
resultInstant = tz.getPreviousTransition(this.toInstant());
575+
break;
576+
default:
577+
throw new RangeError(
578+
`direction must be one of next, previous, not ${options.direction}`,
579+
);
580+
}
581+
return resultInstant?.toZonedDateTimeISO(tz.id) ?? null;
582+
},
583+
});
584+
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
585+
}
586+
{
587+
const nowTimeZone = globalThis.Temporal.Now.timeZone;
588+
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
589+
__proto__: null,
590+
value: function timeZoneId() {
591+
return nowTimeZone().id;
592+
},
593+
writable: true,
594+
enumerable: false,
595+
configurable: true,
596+
});
597+
delete globalThis.Temporal.Now.timeZone;
598+
}
599+
600+
// deno-lint-ignore prefer-primordials
601+
if (new Temporal.Duration().toLocaleString("en-US") !== "PT0S") {
602+
throw "V8 supports Temporal.Duration.prototype.toLocaleString now, no need to shim it";
603+
}
604+
shimTemporalDurationToLocaleString();
605+
}
606+
476607
function shimTemporalDurationToLocaleString() {
477608
const DurationFormat = Intl.DurationFormat;
478609
if (!DurationFormat) {
@@ -803,102 +934,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
803934
delete globalThis.Temporal;
804935
delete globalThis.Date.prototype.toTemporalInstant;
805936
} else {
806-
// Removes the obsoleted `Temporal` API.
807-
// https://github.com/tc39/proposal-temporal/pull/2895
808-
// https://github.com/tc39/proposal-temporal/pull/2914
809-
// https://github.com/tc39/proposal-temporal/pull/2925
810-
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
811-
throw "V8 removes obsoleted Temporal API now, no need to delete them";
812-
}
813-
delete globalThis.Temporal.Instant.fromEpochSeconds;
814-
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
815-
delete globalThis.Temporal.Instant.prototype.epochSeconds;
816-
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
817-
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
818-
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
819-
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
820-
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
821-
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
822-
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
823-
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
824-
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
825-
delete globalThis.Temporal.PlainTime.prototype.calendar;
826-
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
827-
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
828-
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
829-
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
830-
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
831-
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
832-
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
833-
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
834-
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
835-
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
836-
delete globalThis.Temporal.Now.zonedDateTime;
837-
delete globalThis.Temporal.Now.plainDateTime;
838-
delete globalThis.Temporal.Now.plainDate;
839-
delete globalThis.Temporal.Calendar;
840-
delete globalThis.Temporal.TimeZone;
841-
842-
// Modify `Temporal.Calendar` to calendarId string
843-
ArrayPrototypeForEach([
844-
globalThis.Temporal.PlainDate,
845-
globalThis.Temporal.PlainDateTime,
846-
globalThis.Temporal.PlainMonthDay,
847-
globalThis.Temporal.PlainYearMonth,
848-
globalThis.Temporal.ZonedDateTime,
849-
], (target) => {
850-
const getCalendar =
851-
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
852-
ObjectDefineProperty(target.prototype, "calendarId", {
853-
__proto__: null,
854-
get: function calendarId() {
855-
return FunctionPrototypeCall(getCalendar, this).id;
856-
},
857-
enumerable: false,
858-
configurable: true,
859-
});
860-
delete target.prototype.calendar;
861-
});
862-
863-
// Modify `Temporal.TimeZone` to timeZoneId string
864-
{
865-
const getTimeZone = ObjectGetOwnPropertyDescriptor(
866-
globalThis.Temporal.ZonedDateTime.prototype,
867-
"timeZone",
868-
).get;
869-
ObjectDefineProperty(
870-
globalThis.Temporal.ZonedDateTime.prototype,
871-
"timeZoneId",
872-
{
873-
__proto__: null,
874-
get: function timeZoneId() {
875-
return FunctionPrototypeCall(getTimeZone, this).id;
876-
},
877-
enumerable: false,
878-
configurable: true,
879-
},
880-
);
881-
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
882-
}
883-
{
884-
const nowTimeZone = globalThis.Temporal.Now.timeZone;
885-
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
886-
__proto__: null,
887-
value: function timeZoneId() {
888-
return nowTimeZone().id;
889-
},
890-
writable: true,
891-
enumerable: false,
892-
configurable: true,
893-
});
894-
delete globalThis.Temporal.Now.timeZone;
895-
}
896-
897-
// deno-lint-ignore prefer-primordials
898-
if (new Temporal.Duration().toLocaleString("en-US") !== "PT0S") {
899-
throw "V8 supports Temporal.Duration.prototype.toLocaleString now, no need to shim it";
900-
}
901-
shimTemporalDurationToLocaleString();
937+
updateTemporal();
902938
}
903939

904940
// Setup `Deno` global - we're actually overriding already existing global
@@ -1014,98 +1050,7 @@ function bootstrapWorkerRuntime(
10141050
delete globalThis.Temporal;
10151051
delete globalThis.Date.prototype.toTemporalInstant;
10161052
} else {
1017-
// Removes the obsoleted `Temporal` API.
1018-
// https://github.com/tc39/proposal-temporal/pull/2895
1019-
// https://github.com/tc39/proposal-temporal/pull/2914
1020-
// https://github.com/tc39/proposal-temporal/pull/2925
1021-
if (typeof globalThis.Temporal.Instant.fromEpochSeconds === "undefined") {
1022-
throw "V8 removes obsoleted Temporal API now, no need to delete them";
1023-
}
1024-
delete globalThis.Temporal.Instant.fromEpochSeconds;
1025-
delete globalThis.Temporal.Instant.fromEpochMicroseconds;
1026-
delete globalThis.Temporal.Instant.prototype.epochSeconds;
1027-
delete globalThis.Temporal.Instant.prototype.epochMicroseconds;
1028-
delete globalThis.Temporal.Instant.prototype.toZonedDateTime;
1029-
delete globalThis.Temporal.PlainDate.prototype.getISOFiels; // weird
1030-
delete globalThis.Temporal.PlainDate.prototype.getISOFields;
1031-
delete globalThis.Temporal.PlainDateTime.prototype.withPlainDate;
1032-
delete globalThis.Temporal.PlainDateTime.prototype.toPlainYearMonth;
1033-
delete globalThis.Temporal.PlainDateTime.prototype.toPlainMonthDay;
1034-
delete globalThis.Temporal.PlainDateTime.prototype.getISOFields;
1035-
delete globalThis.Temporal.PlainMonthDay.prototype.getISOFields;
1036-
delete globalThis.Temporal.PlainTime.prototype.calendar;
1037-
delete globalThis.Temporal.PlainTime.prototype.toPlainDateTime;
1038-
delete globalThis.Temporal.PlainTime.prototype.toZonedDateTime;
1039-
delete globalThis.Temporal.PlainTime.prototype.getISOFields;
1040-
delete globalThis.Temporal.PlainYearMonth.prototype.getISOFields;
1041-
delete globalThis.Temporal.ZonedDateTime.prototype.epochSeconds;
1042-
delete globalThis.Temporal.ZonedDateTime.prototype.epochMicroseconds;
1043-
delete globalThis.Temporal.ZonedDateTime.prototype.withPlainDate;
1044-
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainYearMonth;
1045-
delete globalThis.Temporal.ZonedDateTime.prototype.toPlainMonthDay;
1046-
delete globalThis.Temporal.ZonedDateTime.prototype.getISOFields;
1047-
delete globalThis.Temporal.Now.zonedDateTime;
1048-
delete globalThis.Temporal.Now.plainDateTime;
1049-
delete globalThis.Temporal.Now.plainDate;
1050-
delete globalThis.Temporal.Calendar;
1051-
delete globalThis.Temporal.TimeZone;
1052-
1053-
// Modify `Temporal.Calendar` to calendarId string
1054-
ArrayPrototypeForEach([
1055-
globalThis.Temporal.PlainDate,
1056-
globalThis.Temporal.PlainDateTime,
1057-
globalThis.Temporal.PlainMonthDay,
1058-
globalThis.Temporal.PlainYearMonth,
1059-
globalThis.Temporal.ZonedDateTime,
1060-
], (target) => {
1061-
const getCalendar =
1062-
ObjectGetOwnPropertyDescriptor(target.prototype, "calendar").get;
1063-
ObjectDefineProperty(target.prototype, "calendarId", {
1064-
__proto__: null,
1065-
get: function calendarId() {
1066-
return FunctionPrototypeCall(getCalendar, this).id;
1067-
},
1068-
enumerable: false,
1069-
configurable: true,
1070-
});
1071-
delete target.prototype.calendar;
1072-
});
1073-
1074-
// Modify `Temporal.TimeZone` to timeZoneId string
1075-
{
1076-
const getTimeZone = ObjectGetOwnPropertyDescriptor(
1077-
globalThis.Temporal.ZonedDateTime.prototype,
1078-
"timeZone",
1079-
).get;
1080-
ObjectDefineProperty(
1081-
globalThis.Temporal.ZonedDateTime.prototype,
1082-
"timeZoneId",
1083-
{
1084-
__proto__: null,
1085-
get: function timeZoneId() {
1086-
return FunctionPrototypeCall(getTimeZone, this).id;
1087-
},
1088-
enumerable: false,
1089-
configurable: true,
1090-
},
1091-
);
1092-
delete globalThis.Temporal.ZonedDateTime.prototype.timeZone;
1093-
}
1094-
{
1095-
const nowTimeZone = globalThis.Temporal.Now.timeZone;
1096-
ObjectDefineProperty(globalThis.Temporal.Now, "timeZoneId", {
1097-
__proto__: null,
1098-
value: function timeZoneId() {
1099-
return nowTimeZone().id;
1100-
},
1101-
writable: true,
1102-
enumerable: false,
1103-
configurable: true,
1104-
});
1105-
delete globalThis.Temporal.Now.timeZone;
1106-
}
1107-
1108-
shimTemporalDurationToLocaleString();
1053+
updateTemporal();
11091054
}
11101055

11111056
// Setup `Deno` global - we're actually overriding already existing global

tests/specs/run/unstable_temporal_api_list/main.out

+16-16
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,22 @@ Temporal.Instant.prototype
3939
]
4040
Temporal.ZonedDateTime.prototype
4141
[
42-
"add", "calendarId", "constructor",
43-
"day", "dayOfWeek", "dayOfYear",
44-
"daysInMonth", "daysInWeek", "daysInYear",
45-
"epochMilliseconds", "epochNanoseconds", "equals",
46-
"era", "eraYear", "hour",
47-
"hoursInDay", "inLeapYear", "microsecond",
48-
"millisecond", "minute", "month",
49-
"monthCode", "monthsInYear", "nanosecond",
50-
"offset", "offsetNanoseconds", "round",
51-
"second", "since", "startOfDay",
52-
"subtract", "timeZoneId", "toInstant",
53-
"toJSON", "toLocaleString", "toPlainDate",
54-
"toPlainDateTime", "toPlainTime", "toString",
55-
"until", "valueOf", "weekOfYear",
56-
"with", "withCalendar", "withPlainTime",
57-
"withTimeZone", "year"
42+
"add", "calendarId", "constructor",
43+
"day", "dayOfWeek", "dayOfYear",
44+
"daysInMonth", "daysInWeek", "daysInYear",
45+
"epochMilliseconds", "epochNanoseconds", "equals",
46+
"era", "eraYear", "getTimeZoneTransition",
47+
"hour", "hoursInDay", "inLeapYear",
48+
"microsecond", "millisecond", "minute",
49+
"month", "monthCode", "monthsInYear",
50+
"nanosecond", "offset", "offsetNanoseconds",
51+
"round", "second", "since",
52+
"startOfDay", "subtract", "timeZoneId",
53+
"toInstant", "toJSON", "toLocaleString",
54+
"toPlainDate", "toPlainDateTime", "toPlainTime",
55+
"toString", "until", "valueOf",
56+
"weekOfYear", "with", "withCalendar",
57+
"withPlainTime", "withTimeZone", "year"
5858
]
5959
Temporal.PlainDate.prototype
6060
[

tests/specs/run/unstable_temporal_api_patch/main.out

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ UTC
66
undefined
77
undefined
88
1 day, 6 hr, 30 min
9+
2020-03-08T03:00:00-04:00[America/New_York]

0 commit comments

Comments
 (0)