Skip to content
Permalink
Browse files
Implement Temporal.PlainDateTime (to the extent of current PlainDate …
…impl)

https://bugs.webkit.org/show_bug.cgi?id=244142

Reviewed by Yusuke Suzuki.

This patch implements a large part of the Temporal.PlainDateTime class:
https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-objects

Since Temporal.PlainDate is incomplete, this patch aims to be "as complete as" PlainDate,
while the next patch will aim to complete the API surface for both classes at once.
(Test262 tests for both will be enabled at that time.)

Specifically, this patch implements and tests the following functionality:
- Temporal.PlainTime:     Support `from(PlainDateTime)`.
- Temporal.PlainDate:     Support `from(PlainDateTime)`.
                          Also add `calendar` getter, getISOFields, toJSON, toLocaleString, valueOf (all trivial).
- Temporal.PlainDateTime: Implement constructor, `from` (modulo generic object path), `compare`,
                          all getters, getISOFields, toString, toJSON, toLocaleString, valueOf.

* JSTests/stress/temporal-plaindate.js:
* JSTests/stress/temporal-plaindatetime.js: Added.
* JSTests/stress/temporal-plaintime.js:
* JSTests/test262/config.yaml: Refactor Temporal skips.
* JSTests/test262/expectations.yaml: Update results.
* Source/JavaScriptCore/CMakeLists.txt:
* Source/JavaScriptCore/DerivedSources-input.xcfilelist:
* Source/JavaScriptCore/DerivedSources-output.xcfilelist:
* Source/JavaScriptCore/DerivedSources.make:
* Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj:
* Source/JavaScriptCore/Sources.txt:
* Source/JavaScriptCore/heap/Heap.h:
* Source/JavaScriptCore/heap/HeapSubspaceTypes.h:
* Source/JavaScriptCore/runtime/CommonIdentifiers.h:
* Source/JavaScriptCore/runtime/ISO8601.cpp:
(JSC::ISO8601::temporalDateTimeToString):
* Source/JavaScriptCore/runtime/ISO8601.h:
* Source/JavaScriptCore/runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildrenImpl):
* Source/JavaScriptCore/runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::plainDateTimeStructure):
* Source/JavaScriptCore/runtime/TemporalObject.cpp:
(JSC::createPlainDateTimeConstructor):
* Source/JavaScriptCore/runtime/TemporalPlainDate.cpp:
(JSC::TemporalPlainDate::toPlainDate):
(JSC::TemporalPlainDate::tryCreateIfValid):
(JSC::TemporalPlainDate::from):
(JSC::TemporalPlainDate::compare):
(JSC::toPlainDate): Deleted.
* Source/JavaScriptCore/runtime/TemporalPlainDate.h:
* Source/JavaScriptCore/runtime/TemporalPlainDateConstructor.cpp:
* Source/JavaScriptCore/runtime/TemporalPlainDatePrototype.cpp:
* Source/JavaScriptCore/runtime/TemporalPlainDateTime.cpp: Added.
* Source/JavaScriptCore/runtime/TemporalPlainDateTime.h: Added.
* Source/JavaScriptCore/runtime/TemporalPlainDateTimeConstructor.cpp: Added.
* Source/JavaScriptCore/runtime/TemporalPlainDateTimeConstructor.h: Added.
* Source/JavaScriptCore/runtime/TemporalPlainDateTimePrototype.cpp: Added.
* Source/JavaScriptCore/runtime/TemporalPlainDateTimePrototype.h: Added.
* Source/JavaScriptCore/runtime/TemporalPlainTime.cpp:
(JSC::TemporalPlainTime::toPlainTime):
(JSC::TemporalPlainTime::tryCreateIfValid):
(JSC::TemporalPlainTime::roundTime):
(JSC::TemporalPlainTime::round const):
(JSC::TemporalPlainTime::toString const):
(JSC::regulateTime):
(JSC::TemporalPlainTime::from):
(JSC::TemporalPlainTime::compare):
(JSC::toPlainTime): Deleted.
(JSC::roundTime): Deleted.
* Source/JavaScriptCore/runtime/TemporalPlainTime.h:
* Source/JavaScriptCore/runtime/TemporalPlainTimeConstructor.cpp:
* Source/JavaScriptCore/runtime/TemporalPlainTimePrototype.cpp:

Canonical link: https://commits.webkit.org/253623@main
  • Loading branch information
rkirsling committed Aug 20, 2022
1 parent 5a67bbe commit e6717cdeb6a841f4b1f6b9d8d3b0dec947850abe
Show file tree
Hide file tree
Showing 33 changed files with 1,449 additions and 79 deletions.
@@ -80,8 +80,14 @@ shouldBe(String(Temporal.PlainDate.from('2007-01-09 03:24:30+01:00[u-ca=japanese
shouldBe(String(Temporal.PlainDate.from('2007-01-09 03:24:30+01:00[Europe/Brussels][u-ca=japanese]')), `2007-01-09`);
shouldBe(String(Temporal.PlainDate.from('2007-01-09[u-ca=japanese]')), `2007-01-09`);
{
let date = Temporal.PlainDate.from('2007-01-09T03:24:30+01:00[Europe/Brussels]')
let date = Temporal.PlainDate.from('2007-01-09T03:24:30+01:00[Europe/Brussels]');
shouldBe(date === Temporal.PlainDate.from(date), false);

let dateTime = Temporal.PlainDateTime.from('2007-01-09T03:24:30+01:00[Europe/Brussels]');
shouldBe(Temporal.PlainDate.from(dateTime).toString(), date.toString());

shouldBe(date.toJSON(), date.toString());
shouldBe(date.toLocaleString(), date.toString());
}


@@ -303,6 +309,7 @@ for (let text of failures) {

{
let getterNames = [
"calendar",
"year",
"month",
"monthCode",
@@ -324,3 +331,9 @@ for (let text of failures) {
}, TypeError);
}
}

shouldThrow(() => { Temporal.PlainDate.from('2007-01-09').valueOf(); }, TypeError);
{
let time = Temporal.PlainDate.from('2007-01-09');
shouldBe(JSON.stringify(time.getISOFields()), `{"calendar":"iso8601","isoDay":9,"isoMonth":1,"isoYear":2007}`);
}
@@ -0,0 +1,215 @@
//@ requireOptions("--useTemporal=1")

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function shouldNotThrow(func) {
func();
}

function shouldThrow(func, errorType) {
let error;
try {
func();
} catch (e) {
error = e;
}

if (!(error instanceof errorType))
throw new Error(`Expected ${errorType.name}!`);
}

shouldBe(Temporal.PlainDateTime instanceof Function, true);
shouldBe(Temporal.PlainDateTime.length, 0);
shouldBe(Object.getOwnPropertyDescriptor(Temporal.PlainDateTime, 'prototype').writable, false);
shouldBe(Object.getOwnPropertyDescriptor(Temporal.PlainDateTime, 'prototype').enumerable, false);
shouldBe(Object.getOwnPropertyDescriptor(Temporal.PlainDateTime, 'prototype').configurable, false);
shouldThrow(() => Temporal.PlainDateTime(), TypeError);
shouldBe(Temporal.PlainDateTime.prototype.constructor, Temporal.PlainDateTime);

const pdt = new Temporal.PlainDateTime(1,2,3,4,5,6,7,8,9);
shouldBe(pdt instanceof Temporal.PlainDateTime, true);
{
class DerivedPlainDateTime extends Temporal.PlainDateTime {}

const dd = new DerivedPlainDateTime(0, 1, 1);
shouldBe(dd instanceof DerivedPlainDateTime, true);
shouldBe(dd instanceof Temporal.PlainDateTime, true);
shouldBe(dd.negated, Temporal.PlainDateTime.prototype.negated);
shouldBe(Object.getPrototypeOf(dd), DerivedPlainDateTime.prototype);
shouldBe(Object.getPrototypeOf(DerivedPlainDateTime.prototype), Temporal.PlainDateTime.prototype);
}

shouldThrow(() => new Temporal.PlainDateTime(), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(Infinity, 1, 1), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, Infinity, 1), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, 0, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, 0, 0, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, 0, 0, 0, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, 0, 0, 0, 0, Infinity), RangeError);
shouldThrow(() => new Temporal.PlainDateTime(0, 1, 1, 0, 0, 0, 0, 0, Infinity), RangeError);

const fields = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'];
fields.forEach((field, i) => {
shouldThrow(() => Temporal.PlainDateTime.prototype[field], TypeError);
shouldBe(pdt[field], i + 1);
});

shouldBe(pdt.calendar instanceof Temporal.Calendar, true);
shouldBe(pdt.calendar.toString(), 'iso8601');
{
const dateGetters = ['monthCode', 'dayOfWeek', 'dayOfYear', 'weekOfYear', 'daysInWeek', 'daysInMonth', 'daysInYear', 'monthsInYear', 'inLeapYear'];
const results = ['M02', 6, 34, 5, 7, 28, 365, 12, false];
dateGetters.forEach((property, i) => {
shouldThrow(() => Temporal.PlainDateTime.prototype[property], TypeError);
shouldBe(pdt[property], results[i]);
});
}

shouldBe(Temporal.PlainDateTime.prototype.getISOFields.length, 0);
shouldBe(JSON.stringify(pdt.getISOFields()), `{"calendar":"iso8601","isoDay":3,"isoHour":4,"isoMicrosecond":8,"isoMillisecond":7,"isoMinute":5,"isoMonth":2,"isoNanosecond":9,"isoSecond":6,"isoYear":1}`);

shouldBe(Temporal.PlainDateTime.from.length, 1);
shouldThrow(() => Temporal.PlainDateTime.from(), RangeError);
shouldBe(Temporal.PlainDateTime.from('0001-02-03T04:05:06.007008009').toString(), '0001-02-03T04:05:06.007008009');
{
const dateTime = Temporal.PlainDateTime.from('2007-01-09T03:24:30+01:00[Europe/Brussels]');
shouldBe(dateTime === Temporal.PlainDateTime.from(dateTime), false);

const date = Temporal.PlainDate.from('2007-01-09T03:24:30+01:00[Europe/Brussels]');
shouldBe(Temporal.PlainDateTime.from(date).toString(), '2007-01-09T00:00:00');
}

shouldThrow(() => Temporal.PlainDateTime.from(pdt, { overflow: 'bogus' }), RangeError);
shouldBe(Temporal.PlainDateTime.from(pdt, { overflow: 'constrain' }).toString(), Temporal.PlainDateTime.from(pdt).toString());

const goodStrings = [
'2007-01-09',
'2007-01-09T03:24:30',
'2007-01-09t03:24:30',
'2007-01-09 03:24:30',
'2007-01-09T03:24:30+20:20:59',
'2007-01-09T03:24:30-20:20:59',
'2007-01-09T03:24:30\u221220:20:59',
'2007-01-09T03:24:30+10',
'2007-01-09T03:24:30+1020',
'2007-01-09T03:24:30+102030',
'2007-01-09T03:24:30+10:20:30.05',
'2007-01-09T03:24:30+10:20:30.123456789',
'2007-01-09T03:24:30+01:00[Europe/Brussels]',
'2007-01-09 03:24:30+01:00[Europe/Brussels]',
'2007-01-09T03:24:30+01:00[UNKNOWN]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[.hey]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_-]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_/_]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_-./_-.]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_../_..]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[_./_.]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[Etc/GMT+20]', // TimeZone error should be ignored.
'2007-01-09T03:24:30+01:00[Etc/GMT-20]', // TimeZone error should be ignored.
'2007-01-09 03:24:30+01:00[+01]',
'2007-01-09 03:24:30+01:00[+01:00]',
'2007-01-09 03:24:30+01:00[+01:00:00]',
'2007-01-09 03:24:30+01:00[+01:00:00.123]',
'2007-01-09 03:24:30+01:00[+01:00:00.12345]',
'2007-01-09 03:24:30+01:00[+01:00:00.12345678]',
'2007-01-09 03:24:30+01:00[+01:00:00.123456789]',
'2007-01-09 03:24:30+01:00[-01:00]',
'2007-01-09 03:24:30+01:00[\u221201:00]',
'2007-01-09 03:24:30+01:00[u-ca=japanese]',
'2007-01-09 03:24:30+01:00[Europe/Brussels][u-ca=japanese]',
'2007-01-09[u-ca=japanese]',
];
for (let s of goodStrings)
shouldNotThrow(() => Temporal.PlainDateTime.from(s));

const badStrings = [
"",
"23:59:61.999999999",
"1995-1207T03:24:30",
"199512-07T03:24:30",
"2007-01-09T03:24:30+20:20:60",
"2007-01-09T03:24:30+20:2050",
"2007-01-09T03:24:30+:",
"2007-01-09T03:24:30+2:50",
"2007-01-09T03:24:30+01:00[/]",
"2007-01-09T03:24:30+01:00[///]",
"2007-01-09T03:24:30+01:00[Hey/Hello",
"2007-01-09T03:24:30+01:00[]",
"2007-01-09T03:24:30+01:00[Hey/]",
"2007-01-09T03:24:30+01:00[..]",
"2007-01-09T03:24:30+01:00[.]",
"2007-01-09T03:24:30+01:00[./.]",
"2007-01-09T03:24:30+01:00[../..]",
"2007-01-09T03:24:30+01:00[-Hey/Hello]",
"2007-01-09T03:24:30+01:00[-]",
"2007-01-09T03:24:30+01:00[-/_]",
"2007-01-09T03:24:30+01:00[_/-]",
"2007-01-09T03:24:30+01:00[CocoaCappuccinoMatcha]",
"2007-01-09T03:24:30+01:00[Etc/GMT+50]",
"2007-01-09T03:24:30+01:00[Etc/GMT+0]",
"2007-01-09T03:24:30+01:00[Etc/GMT0]",
"2007-01-09T03:24:30+10:20:30.0123456789",
"2007-01-09 03:24:30+01:00[Etc/GMT\u221201]",
"2007-01-09 03:24:30+01:00[+02:00:00.0123456789]",
"2007-01-09 03:24:30+01:00[02:00:00.123456789]",
"2007-01-09 03:24:30+01:00[02:0000.123456789]",
"2007-01-09 03:24:30+01:00[0200:00.123456789]",
"2007-01-09 03:24:30+01:00[02:00:60.123456789]",
"2007-01-09T03:24:30Z", // UTCDesignator
"2007-01-09 03:24:30[u-ca=japanese][Europe/Brussels]",
"2007-01-09 03:24:30+01:00[u-ca=japanese][Europe/Brussels]",
];
for (let s of badStrings)
shouldThrow(() => Temporal.PlainDateTime.from(s), RangeError);

shouldBe(Temporal.PlainDateTime.compare.length, 2);
shouldBe(Temporal.PlainDateTime.compare('2007-01-09T03:24', '2007-01-09'), 1);
shouldBe(Temporal.PlainDateTime.compare('2007-01-09T03:24', '2007-01-09T03:24:00.007008009'), -1);
shouldBe(Temporal.PlainDateTime.compare('2007-01-09T00:00', '2007-01-09'), 0);

// At present, toLocaleString has the same behavior as toJSON or argumentless toString.
for (const method of ['toString', 'toJSON', 'toLocaleString']) {
shouldBe(Temporal.PlainDateTime.prototype[method].length, 0);
shouldThrow(() => Temporal.PlainDateTime.prototype[method].call({}), TypeError);

shouldBe(pdt[method](), '0001-02-03T04:05:06.007008009');
}
shouldBe(pdt.toString({}), pdt.toString());

shouldThrow(() => pdt.toString({ smallestUnit: 'bogus' }), RangeError);
for (const unit of ['year', 'month', 'week', 'day', 'hour']) {
shouldThrow(() => pdt.toString({ smallestUnit: unit }), RangeError);
shouldThrow(() => pdt.toString({ smallestUnit: `${unit}s` }), RangeError);
}
shouldBe(pdt.toString({ smallestUnit: 'minute' }), '0001-02-03T04:05');
shouldBe(pdt.toString({ smallestUnit: 'second' }), '0001-02-03T04:05:06');
shouldBe(pdt.toString({ smallestUnit: 'millisecond' }), '0001-02-03T04:05:06.007');
shouldBe(pdt.toString({ smallestUnit: 'microsecond' }), '0001-02-03T04:05:06.007008');
shouldBe(pdt.toString({ smallestUnit: 'nanosecond' }), '0001-02-03T04:05:06.007008009');
for (const unit of ['minute', 'second', 'millisecond', 'microsecond', 'nanosecond'])
shouldBe(pdt.toString({ smallestUnit: unit }), pdt.toString({ smallestUnit: `${unit}s` }));

shouldThrow(() => pdt.toString({ fractionalSecondDigits: -1 }), RangeError);
shouldThrow(() => pdt.toString({ fractionalSecondDigits: 10 }), RangeError);
shouldThrow(() => pdt.toString({ fractionalSecondDigits: 'bogus' }), RangeError);
shouldBe(pdt.toString({ fractionalSecondDigits: 0 }), '0001-02-03T04:05:06');
const decimalPart = '007008009';
for (let i = 1; i < 10; i++)
shouldBe(pdt.toString({ fractionalSecondDigits: i }), `0001-02-03T04:05:06.${decimalPart.slice(0,i)}`);
shouldBe(pdt.toString({ fractionalSecondDigits: 'auto' }), pdt.toString());

shouldThrow(() => pdt.toString({ roundingMode: 'bogus' }), RangeError);
shouldBe(pdt.toString({ roundingMode: 'trunc' }), pdt.toString());
shouldBe(pdt.toString({ fractionalSecondDigits: 2, roundingMode: 'ceil' }), '0001-02-03T04:05:06.01');
shouldBe(pdt.toString({ fractionalSecondDigits: 2, roundingMode: 'floor' }), '0001-02-03T04:05:06.00');
shouldBe(new Temporal.PlainDateTime(1999,12,31,23,59,59,999,999,999).toString({ smallestUnit: 'microsecond', roundingMode: 'halfExpand' }), '2000-01-01T00:00:00.000000');

shouldBe(Temporal.PlainDateTime.prototype.valueOf.length, 0);
shouldThrow(() => pdt.valueOf(), TypeError);
@@ -146,10 +146,16 @@ shouldBe(String(Temporal.PlainTime.from('2007-01-09 03:24:30+01:00[u-ca=japanese
shouldBe(String(Temporal.PlainTime.from('2007-01-09 03:24:30+01:00[Europe/Brussels][u-ca=japanese]')), `03:24:30`);
shouldBe(String(Temporal.PlainTime.from('2007-01-09 03:24:30[u-ca=japanese]')), `03:24:30`);
{
let time = Temporal.PlainTime.from('1995-12-07T03:24:30+01:00[Europe/Brussels]')
let time = Temporal.PlainTime.from('1995-12-07T03:24:30+01:00[Europe/Brussels]');
shouldBe(time === Temporal.PlainTime.from(time), false);

let dateTime = Temporal.PlainDateTime.from('1995-12-07T03:24:30+01:00[Europe/Brussels]')
shouldBe(Temporal.PlainTime.from(dateTime).toString(), time.toString());

shouldBe(time.toJSON(), time.toString());
shouldBe(time.toLocaleString(), time.toString());
}
{
{``
let time = Temporal.PlainTime.from({
hour: 19,
minute: 39,
@@ -29,7 +29,13 @@ skip:
- test/built-ins/Temporal/Calendar
- test/built-ins/Temporal/Instant/prototype/toZonedDateTime
- test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO
- test/built-ins/Temporal/Now
- test/built-ins/Temporal/Now/plainDate
- test/built-ins/Temporal/Now/plainDateISO
- test/built-ins/Temporal/Now/plainDateTime
- test/built-ins/Temporal/Now/plainDateTimeISO
- test/built-ins/Temporal/Now/plainTimeISO
- test/built-ins/Temporal/Now/zonedDateTime
- test/built-ins/Temporal/Now/zonedDateTimeISO
- test/built-ins/Temporal/PlainDate
- test/built-ins/Temporal/PlainDateTime
- test/built-ins/Temporal/PlainMonthDay
@@ -254,11 +260,13 @@ skip:
- test/intl402/DateTimeFormat/prototype/formatToParts/temporal-objects-resolved-time-zone.js

# Depends on Temporal.PlainDateTime
- test/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js
- test/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js
- test/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js
- test/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js
- test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
- test/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js
- test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js
- test/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js
- test/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js
- test/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js
@@ -333,16 +341,6 @@ skip:
- test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
- test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js

# awaiting resolution: https://github.com/tc39/proposal-temporal/issues/1715
- test/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js
- test/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js
- test/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js
- test/built-ins/Temporal/Duration/prototype/round/round-negative-result.js
- test/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js
- test/built-ins/Temporal/Instant/prototype/until/instant-string.js
- test/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
- test/built-ins/Temporal/PlainTime/prototype/until/balance-negative-time-units.js

# Symbols as WeakMap keys proposal breaks existing tests https://github.com/tc39/proposal-symbols-as-weakmap-keys
- test/built-ins/WeakMap/prototype/set/key-not-object-throw.js
- test/built-ins/WeakSet/symbol-disallowed-as-weakset-key.js

0 comments on commit e6717cd

Please sign in to comment.