Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JSC] Implement Intl.DateTimeFormat dayPeriod
https://bugs.webkit.org/show_bug.cgi?id=215839

Reviewed by Ross Kirsling.

JSTests:

* stress/intl-datetimeformat-day-period.js: Added.
(shouldBe):
(throw.new.Error):
* test262/config.yaml:

Source/JavaScriptCore:

This patch implements Intl.DateTimeFormat dayPeriod option[1]. We can use "narrow", "short", or "long" for dayPeriod,
and it determines how "AM" etc. is represented.

[1]: tc39/ecma402#346

* builtins/DatePrototype.js:
(toLocaleString.toDateTimeOptionsAnyAll):
(toLocaleString):
(toLocaleTimeString.toDateTimeOptionsTimeTime):
(toLocaleTimeString):
* bytecode/BytecodeIntrinsicRegistry.cpp:
(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
* bytecode/BytecodeIntrinsicRegistry.h:
* runtime/CommonIdentifiers.h:
* runtime/IntlDateTimeFormat.cpp:
(JSC::toDateTimeOptionsAnyDate):
(JSC::IntlDateTimeFormat::setFormatsFromPattern):
(JSC::IntlDateTimeFormat::initializeDateTimeFormat):
(JSC::IntlDateTimeFormat::dayPeriodString):
(JSC::IntlDateTimeFormat::resolvedOptions const):
* runtime/IntlDateTimeFormat.h:
* runtime/OptionsList.h:

Canonical link: https://commits.webkit.org/228759@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@266323 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Aug 29, 2020
1 parent 83876d4 commit 4fbb0d8
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 3 deletions.
12 changes: 12 additions & 0 deletions JSTests/ChangeLog
@@ -1,3 +1,15 @@
2020-08-28 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] Implement Intl.DateTimeFormat dayPeriod
https://bugs.webkit.org/show_bug.cgi?id=215839

Reviewed by Ross Kirsling.

* stress/intl-datetimeformat-day-period.js: Added.
(shouldBe):
(throw.new.Error):
* test262/config.yaml:

2020-08-28 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] super property with new should be accepted
Expand Down
52 changes: 52 additions & 0 deletions JSTests/stress/intl-datetimeformat-day-period.js
@@ -0,0 +1,52 @@
//@ runDefault("--useIntlDateTimeFormatDayPeriod=1")

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

{
let dtf = new Intl.DateTimeFormat("en", {hour: "numeric", dayPeriod: "short"});
shouldBe(dtf.format(new Date("2019-05-20T07:00:00")), "7 in the morning");

{
let expected = [
{type: "hour", value: "7"},
{type: "literal", value: " "},
{type: "dayPeriod", value: "in the morning"}
];
let actual = dtf.formatToParts(new Date("2019-05-20T07:00:00"));
shouldBe(actual.length, expected.length);
for (let index = 0; index < expected.length; ++index) {
shouldBe(actual[index].type, expected[index].type);
shouldBe(actual[index].value, expected[index].value);
}
}

shouldBe(dtf.format(new Date("2019-05-20T11:59:00")), "11 in the morning");
shouldBe(dtf.format(new Date("2019-05-20T12:00:00")), "12 noon");
shouldBe(dtf.format(new Date("2019-05-20T13:00:00")), "1 in the afternoon");
shouldBe(dtf.format(new Date("2019-05-20T18:00:00")), "6 in the evening");
shouldBe(dtf.format(new Date("2019-05-20T22:00:00")), "10 at night");

dtf = new Intl.DateTimeFormat("zh-Hant", {hour: "numeric", dayPeriod: "short"});
shouldBe(dtf.format(new Date("2019-05-20T07:00:00")), "清晨7時");
{
let expected = [
{type: "dayPeriod", value: "清晨"},
{type: "hour", value: "7"},
{type: "literal", value: "時"}
];
let actual = dtf.formatToParts(new Date("2019-05-20T07:00:00"));
shouldBe(actual.length, expected.length);
for (let index = 0; index < expected.length; ++index) {
shouldBe(actual[index].type, expected[index].type);
shouldBe(actual[index].value, expected[index].value);
}
}
shouldBe(dtf.format(new Date("2019-05-20T11:59:00")), "上午11時");
shouldBe(dtf.format(new Date("2019-05-20T12:00:00")), "中午12時");
shouldBe(dtf.format(new Date("2019-05-20T13:00:00")), "下午1時");
shouldBe(dtf.format(new Date("2019-05-20T18:00:00")), "下午6時");
shouldBe(dtf.format(new Date("2019-05-20T22:00:00")), "晚上10時");
}
2 changes: 1 addition & 1 deletion JSTests/test262/config.yaml
Expand Up @@ -6,6 +6,7 @@ flags:
FinalizationRegistry: useWeakRefs
class-fields-public: usePublicClassFields
class-fields-private: usePrivateClassFields
Intl.DateTimeFormat-dayPeriod: useIntlDateTimeFormatDayPeriod
skip:
features:
- SharedArrayBuffer
Expand All @@ -23,7 +24,6 @@ skip:
# https://bugs.webkit.org/show_bug.cgi?id=202475
- regexp-match-indices
- top-level-await
- Intl.DateTimeFormat-dayPeriod
- Intl.ListFormat
paths:
- test/built-ins/DataView/prototype/getBigInt64
Expand Down
30 changes: 30 additions & 0 deletions Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,33 @@
2020-08-28 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] Implement Intl.DateTimeFormat dayPeriod
https://bugs.webkit.org/show_bug.cgi?id=215839

Reviewed by Ross Kirsling.

This patch implements Intl.DateTimeFormat dayPeriod option[1]. We can use "narrow", "short", or "long" for dayPeriod,
and it determines how "AM" etc. is represented.

[1]: https://github.com/tc39/ecma402/pull/346

* builtins/DatePrototype.js:
(toLocaleString.toDateTimeOptionsAnyAll):
(toLocaleString):
(toLocaleTimeString.toDateTimeOptionsTimeTime):
(toLocaleTimeString):
* bytecode/BytecodeIntrinsicRegistry.cpp:
(JSC::BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry):
* bytecode/BytecodeIntrinsicRegistry.h:
* runtime/CommonIdentifiers.h:
* runtime/IntlDateTimeFormat.cpp:
(JSC::toDateTimeOptionsAnyDate):
(JSC::IntlDateTimeFormat::setFormatsFromPattern):
(JSC::IntlDateTimeFormat::initializeDateTimeFormat):
(JSC::IntlDateTimeFormat::dayPeriodString):
(JSC::IntlDateTimeFormat::resolvedOptions const):
* runtime/IntlDateTimeFormat.h:
* runtime/OptionsList.h:

2020-08-28 Yusuke Suzuki <ysuzuki@apple.com>

[JSC] super property with new should be accepted
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/builtins/DatePrototype.js
Expand Up @@ -46,6 +46,7 @@ function toLocaleString(/* locales, options */)
options.year === @undefined &&
options.month === @undefined &&
options.day === @undefined &&
(!@useIntlDateTimeFormatDayPeriod || options.dayPeriod === @undefined) &&
options.hour === @undefined &&
options.minute === @undefined &&
options.second === @undefined &&
Expand Down Expand Up @@ -162,6 +163,7 @@ function toLocaleTimeString(/* locales, options */)

// Check original instead of descendant to reduce lookups up the prototype chain.
var needsDefaults = !options || (
(!@useIntlDateTimeFormatDayPeriod || options.dayPeriod === @undefined) &&
options.hour === @undefined &&
options.minute === @undefined &&
options.second === @undefined &&
Expand Down
Expand Up @@ -111,6 +111,7 @@ BytecodeIntrinsicRegistry::BytecodeIntrinsicRegistry(VM& vm)
m_AsyncGeneratorSuspendReasonYield.set(m_vm, jsNumber(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Yield)));
m_AsyncGeneratorSuspendReasonAwait.set(m_vm, jsNumber(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Await)));
m_AsyncGeneratorSuspendReasonNone.set(m_vm, jsNumber(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::None)));
m_useIntlDateTimeFormatDayPeriod.set(m_vm, jsBoolean(Options::useIntlDateTimeFormatDayPeriod()));
}

Optional<BytecodeIntrinsicRegistry::Entry> BytecodeIntrinsicRegistry::lookup(const Identifier& ident) const
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Expand Up @@ -151,6 +151,7 @@ enum class LinkTimeConstant : int32_t;
macro(AsyncGeneratorSuspendReasonYield) \
macro(AsyncGeneratorSuspendReasonAwait) \
macro(AsyncGeneratorSuspendReasonNone) \
macro(useIntlDateTimeFormatDayPeriod) \

#define JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_CUSTOM_EACH_NAME(macro) \
macro(sentinelMapBucket) \
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/runtime/CommonIdentifiers.h
Expand Up @@ -95,6 +95,7 @@
macro(counters) \
macro(dateStyle) \
macro(day) \
macro(dayPeriod) \
macro(defineProperty) \
macro(deref) \
macro(description) \
Expand Down
67 changes: 65 additions & 2 deletions Source/JavaScriptCore/runtime/IntlDateTimeFormat.cpp
Expand Up @@ -248,11 +248,18 @@ static JSObject* toDateTimeOptionsAnyDate(JSGlobalObject* globalObject, JSValue
// 6. If required is "time" or "any",
// Always "any".

// a. For each of the property names "hour", "minute", "second", "fractionalSecondDigits":
// a. For each of the property names ""dayPeriod", hour", "minute", "second", "fractionalSecondDigits":
// i. Let prop be the property name.
// ii. Let value be Get(options, prop).
// iii. ReturnIfAbrupt(value).
// iv. If value is not undefined, then let needDefaults be false.
if (Options::useIntlDateTimeFormatDayPeriod()) {
JSValue dayPeriod = options->get(globalObject, vm.propertyNames->dayPeriod);
RETURN_IF_EXCEPTION(scope, nullptr);
if (!dayPeriod.isUndefined())
needDefaults = false;
}

JSValue hour = options->get(globalObject, vm.propertyNames->hour);
RETURN_IF_EXCEPTION(scope, nullptr);
if (!hour.isUndefined())
Expand Down Expand Up @@ -378,6 +385,16 @@ void IntlDateTimeFormat::setFormatsFromPattern(const StringView& pattern)
else if (count == 2)
m_day = Day::TwoDigit;
break;
case 'a':
case 'b':
case 'B':
if (count <= 3)
m_dayPeriod = DayPeriod::Short;
else if (count == 4)
m_dayPeriod = DayPeriod::Long;
else if (count == 5)
m_dayPeriod = DayPeriod::Narrow;
break;
case 'h':
case 'H':
case 'k':
Expand Down Expand Up @@ -584,6 +601,12 @@ void IntlDateTimeFormat::initializeDateTimeFormat(JSGlobalObject* globalObject,
break;
}

DayPeriod dayPeriod = DayPeriod::None;
if (Options::useIntlDateTimeFormatDayPeriod()) {
dayPeriod = intlOption<DayPeriod>(globalObject, options, vm.propertyNames->dayPeriod, { { "narrow"_s, DayPeriod::Narrow }, { "short"_s, DayPeriod::Short }, { "long"_s, DayPeriod::Long } }, "dayPeriod must be \"narrow\", \"short\", or \"long\""_s, DayPeriod::None);
RETURN_IF_EXCEPTION(scope, void());
}

Hour hour = intlOption<Hour>(globalObject, options, vm.propertyNames->hour, { { "2-digit"_s, Hour::TwoDigit }, { "numeric"_s, Hour::Numeric } }, "hour must be \"2-digit\" or \"numeric\""_s, Hour::None);
RETURN_IF_EXCEPTION(scope, void());
switch (hour) {
Expand All @@ -607,6 +630,24 @@ void IntlDateTimeFormat::initializeDateTimeFormat(JSGlobalObject* globalObject,
break;
}

if (Options::useIntlDateTimeFormatDayPeriod()) {
// dayPeriod must be set after setting hour.
// https://unicode-org.atlassian.net/browse/ICU-20731
switch (dayPeriod) {
case DayPeriod::Narrow:
skeletonBuilder.appendLiteral("BBBBB");
break;
case DayPeriod::Short:
skeletonBuilder.append('B');
break;
case DayPeriod::Long:
skeletonBuilder.appendLiteral("BBBB");
break;
case DayPeriod::None:
break;
}
}

Minute minute = intlOption<Minute>(globalObject, options, vm.propertyNames->minute, { { "2-digit"_s, Minute::TwoDigit }, { "numeric"_s, Minute::Numeric } }, "minute must be \"2-digit\" or \"numeric\""_s, Minute::None);
RETURN_IF_EXCEPTION(scope, void());
switch (minute) {
Expand Down Expand Up @@ -668,7 +709,7 @@ void IntlDateTimeFormat::initializeDateTimeFormat(JSGlobalObject* globalObject,
// ii. Let p be opt.[[<prop>]].
// iii. If p is not undefined, then
// 1. Throw a TypeError exception.
if (weekday != Weekday::None || era != Era::None || year != Year::None || month != Month::None || day != Day::None || hour != Hour::None || minute != Minute::None || second != Second::None || fractionalSecondDigits != 0 || timeZoneName != TimeZoneName::None) {
if (weekday != Weekday::None || era != Era::None || year != Year::None || month != Month::None || day != Day::None || dayPeriod != DayPeriod::None || hour != Hour::None || minute != Minute::None || second != Second::None || fractionalSecondDigits != 0 || timeZoneName != TimeZoneName::None) {
throwTypeError(globalObject, scope, "dateStyle and timeStyle may not be used with other DateTimeFormat options"_s);
return;
}
Expand Down Expand Up @@ -892,6 +933,23 @@ ASCIILiteral IntlDateTimeFormat::dayString(Day day)
return ASCIILiteral::null();
}

ASCIILiteral IntlDateTimeFormat::dayPeriodString(DayPeriod dayPeriod)
{
switch (dayPeriod) {
case DayPeriod::Narrow:
return "narrow"_s;
case DayPeriod::Short:
return "short"_s;
case DayPeriod::Long:
return "long"_s;
case DayPeriod::None:
ASSERT_NOT_REACHED();
return ASCIILiteral::null();
}
ASSERT_NOT_REACHED();
return ASCIILiteral::null();
}

ASCIILiteral IntlDateTimeFormat::hourString(Hour hour)
{
switch (hour) {
Expand Down Expand Up @@ -1002,6 +1060,11 @@ JSObject* IntlDateTimeFormat::resolvedOptions(JSGlobalObject* globalObject) cons
if (m_day != Day::None)
options->putDirect(vm, vm.propertyNames->day, jsNontrivialString(vm, dayString(m_day)));

if (Options::useIntlDateTimeFormatDayPeriod()) {
if (m_dayPeriod != DayPeriod::None)
options->putDirect(vm, vm.propertyNames->dayPeriod, jsNontrivialString(vm, dayPeriodString(m_dayPeriod)));
}

if (m_hour != Hour::None)
options->putDirect(vm, vm.propertyNames->hour, jsNontrivialString(vm, hourString(m_hour)));

Expand Down
3 changes: 3 additions & 0 deletions Source/JavaScriptCore/runtime/IntlDateTimeFormat.h
Expand Up @@ -81,6 +81,7 @@ class IntlDateTimeFormat final : public JSNonFinalObject {
enum class Year : uint8_t { None, TwoDigit, Numeric };
enum class Month : uint8_t { None, TwoDigit, Numeric, Narrow, Short, Long };
enum class Day : uint8_t { None, TwoDigit, Numeric };
enum class DayPeriod : uint8_t { None, Narrow, Short, Long };
enum class Hour : uint8_t { None, TwoDigit, Numeric };
enum class Minute : uint8_t { None, TwoDigit, Numeric };
enum class Second : uint8_t { None, TwoDigit, Numeric };
Expand All @@ -93,6 +94,7 @@ class IntlDateTimeFormat final : public JSNonFinalObject {
static ASCIILiteral yearString(Year);
static ASCIILiteral monthString(Month);
static ASCIILiteral dayString(Day);
static ASCIILiteral dayPeriodString(DayPeriod);
static ASCIILiteral hourString(Hour);
static ASCIILiteral minuteString(Minute);
static ASCIILiteral secondString(Second);
Expand All @@ -117,6 +119,7 @@ class IntlDateTimeFormat final : public JSNonFinalObject {
Year m_year { Year::None };
Month m_month { Month::None };
Day m_day { Day::None };
DayPeriod m_dayPeriod { DayPeriod::None };
Hour m_hour { Hour::None };
Minute m_minute { Minute::None };
Second m_second { Second::None };
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/runtime/OptionsList.h
Expand Up @@ -490,6 +490,7 @@ constexpr bool enableWebAssemblyStreamingApi = false;
/* FIXME: We should make sure finalizers don't run for detached windows before enabling WeakRefs by default. https://bugs.webkit.org/show_bug.cgi?id=214508 */ \
v(Bool, useWeakRefs, false, Normal, "Expose the WeakRef constructor.") \
v(Bool, useBigInt, true, Normal, "If true, we will enable BigInt support.") \
v(Bool, useIntlDateTimeFormatDayPeriod, false, Normal, "Expose the Intl.DateTimeFormat dayPeriod feature.") \
v(Bool, useArrayAllocationProfiling, true, Normal, "If true, we will use our normal array allocation profiling. If false, the allocation profile will always claim to be undecided.") \
v(Bool, forcePolyProto, false, Normal, "If true, create_this will always create an object with a poly proto structure.") \
v(Bool, forceMiniVMMode, false, Normal, "If true, it will force mini VM mode on.") \
Expand Down

0 comments on commit 4fbb0d8

Please sign in to comment.