Skip to content

Commit c2c4ee7

Browse files
committed
Revert "Disable DateTimeFormat::formatToParts for Apple platform (#1155)"
This reverts commit c5a633f.
1 parent f93242b commit c2c4ee7

File tree

6 files changed

+98
-8
lines changed

6 files changed

+98
-8
lines changed

doc/IntlAPIs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ One popular implementation strategy followed by other engines, is to bundle an i
2727
- `Intl.DateTimeFormat`*
2828
- `Intl.DateTimeFormat.supportedLocalesOf`
2929
- `Intl.DateTimeFormat.prototype.format`
30+
- `Intl.DateTimeFormat.prototype.formatToParts`
3031
- `Intl.DateTimeFormat.prototype.resolvedOptions`
3132

3233
- `Intl.getCanonicalLocales`
@@ -50,7 +51,6 @@ One popular implementation strategy followed by other engines, is to bundle an i
5051
## Supported on Android only
5152
- `Intl.NumberFormat`
5253
- `Intl.NumberFormat.prototype.formatToParts`
53-
- `Intl.DateTimeFormat.prototype.formatToParts`
5454

5555
## * Limitations on property support
5656

lib/Platform/Intl/PlatformIntlApple.mm

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,8 @@ uint8_t getCurrencyDigits(std::u16string_view code) {
12291229

12301230
std::u16string format(double jsTimeValue) noexcept;
12311231

1232+
std::vector<Part> formatToParts(double x) noexcept;
1233+
12321234
private:
12331235
void initializeNSDateFormatter() noexcept;
12341236

@@ -1894,8 +1896,76 @@ uint8_t getCurrencyDigits(std::u16string_view code) {
18941896
return static_cast<DateTimeFormatApple *>(this)->format(jsTimeValue);
18951897
}
18961898

1899+
static std::u16string returnTypeOfDate(const char16_t &c16) {
1900+
if (c16 == u'a')
1901+
return u"dayPeriod";
1902+
if (c16 == u'z' || c16 == u'v' || c16 == u'O')
1903+
return u"timeZoneName";
1904+
if (c16 == u'G')
1905+
return u"era";
1906+
if (c16 == u'y')
1907+
return u"year";
1908+
if (c16 == u'M')
1909+
return u"month";
1910+
if (c16 == u'E')
1911+
return u"weekday";
1912+
if (c16 == u'd')
1913+
return u"day";
1914+
if (c16 == u'h' || c16 == u'k' || c16 == u'K' || c16 == u'H')
1915+
return u"hour";
1916+
if (c16 == u'm')
1917+
return u"minute";
1918+
if (c16 == u's')
1919+
return u"second";
1920+
if (c16 == u'S')
1921+
return u"fractionalSecond";
1922+
return u"literal";
1923+
}
1924+
1925+
// Implementer note: This method corresponds roughly to
1926+
// https://402.ecma-international.org/8.0/#sec-formatdatetimetoparts
1927+
std::vector<Part> DateTimeFormatApple::formatToParts(double x) noexcept {
1928+
// NOTE: We dont have access to localeData.patterns. Instead we use
1929+
// NSDateFormatter's foramt string, and break it into components.
1930+
// 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
1931+
auto fmt = nsStringToU16String(nsDateFormatter_.dateFormat);
1932+
std::unique(fmt.begin(), fmt.end());
1933+
auto formattedDate = format(x);
1934+
// 2. Let result be ArrayCreate(0).
1935+
std::vector<Part> result;
1936+
// 3. Let n be 0.
1937+
// 4. For each Record { [[Type]], [[Value]] } part in parts, do
1938+
// a. Let O be OrdinaryObjectCreate(%Object.prototype%).
1939+
// b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
1940+
// c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
1941+
// d. Perform ! CreateDataProperty(result, ! ToString(n), O).
1942+
// e. Increment n by 1.
1943+
std::u16string currentPart;
1944+
unsigned n = 0;
1945+
static auto alphanumerics = NSCharacterSet.alphanumericCharacterSet;
1946+
for (char16_t c16 : formattedDate) {
1947+
if ([alphanumerics characterIsMember:c16]) {
1948+
currentPart += c16;
1949+
continue;
1950+
}
1951+
if (currentPart != u"") {
1952+
result.push_back(
1953+
{{u"type", returnTypeOfDate(fmt[n])}, {u"value", currentPart}});
1954+
currentPart = u"";
1955+
n++;
1956+
}
1957+
result.push_back({{u"type", u"literal"}, {u"value", {c16}}});
1958+
n++;
1959+
}
1960+
// Last format string component.
1961+
result.push_back(
1962+
{{u"type", returnTypeOfDate(fmt[n])}, {u"value", currentPart}});
1963+
// 5. Return result.
1964+
return result;
1965+
}
1966+
18971967
std::vector<Part> DateTimeFormat::formatToParts(double x) noexcept {
1898-
llvm_unreachable("formatToParts is unimplemented on Apple platforms");
1968+
return static_cast<DateTimeFormatApple *>(this)->formatToParts(x);
18991969
}
19001970

19011971
class NumberFormatApple : public NumberFormat {

lib/VM/JSLib/Intl.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -929,15 +929,13 @@ void defineIntlDateTimeFormat(Runtime &runtime, Handle<JSObject> intl) {
929929
false,
930930
true);
931931

932-
#ifndef __APPLE__
933932
defineMethod(
934933
runtime,
935934
prototype,
936935
Predefined::getSymbolID(Predefined::formatToParts),
937936
nullptr,
938937
intlDateTimeFormatPrototypeFormatToParts,
939938
1);
940-
#endif
941939

942940
defineMethod(
943941
runtime,

test/hermes/intl/date-time-format-apple.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,24 @@ print(new Intl.DateTimeFormat('en-US').resolvedOptions().numberingSystem);
142142
print(new Intl.DateTimeFormat('en-US', { timeZone: 'SGT'}).resolvedOptions().timeZone);
143143
// CHECK-NEXT: SGT
144144

145+
print(JSON.stringify(new Intl.DateTimeFormat('en-US').formatToParts(date)));
146+
// CHECK-NEXT: [{"value":"1","type":"month"},{"value":"/","type":"literal"},{"value":"2","type":"day"},{"value":"/","type":"literal"},{"value":"2020","type":"year"}]
147+
148+
print(JSON.stringify(new Intl.DateTimeFormat('en-GB').formatToParts(date)));
149+
// CHECK-NEXT: [{"value":"02","type":"day"},{"value":"/","type":"literal"},{"value":"01","type":"month"},{"value":"/","type":"literal"},{"value":"2020","type":"year"}]
150+
151+
print(JSON.stringify(new Intl.DateTimeFormat('en-US', {weekday: 'long',
152+
year: 'numeric',
153+
month: 'numeric',
154+
day: 'numeric',
155+
hour: 'numeric',
156+
minute: 'numeric',
157+
second: 'numeric',
158+
fractionalSecondDigits: 3,
159+
hour12: true,
160+
timeZone: 'UTC'
161+
}).formatToParts(new Date(Date.UTC(2020, 0, 2, 3, 45, 00, 30)))));
162+
// CHECK-NEXT: [{"value":"Thursday","type":"weekday"},{"value":",","type":"literal"},{"value":" ","type":"literal"},{"value":"1","type":"month"},{"value":"/","type":"literal"},{"value":"2","type":"day"},{"value":"/","type":"literal"},{"value":"2020","type":"year"},{"value":",","type":"literal"},{"value":" ","type":"literal"},{"value":"3","type":"hour"},{"value":":","type":"literal"},{"value":"45","type":"minute"},{"value":":","type":"literal"},{"value":"00","type":"second"},{"value":".","type":"literal"},{"value":"030","type":"fractionalSecond"},{"value":" ","type":"literal"},{"value":"AM","type":"dayPeriod"}]
163+
145164
print(new Date(Date.UTC(2020, 0, 2)).toLocaleString("en-US", {weekday: "short", timeZone: "UTC"}))
146165
// CHECK-NEXT: Thu

test/hermes/intl/intl.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ testServiceGetterTypes(Intl.DateTimeFormat, 'format');
8383
testServiceMethodTypes(Intl.DateTimeFormat, 'formatToParts');
8484
testServiceMethodTypes(Intl.DateTimeFormat, 'resolvedOptions');
8585
assert(typeof Intl.DateTimeFormat().format() === 'string');
86-
if(Intl.DateTimeFormat.prototype.formatToParts) {
87-
testParts(Intl.DateTimeFormat().formatToParts());
88-
}
86+
testParts(Intl.DateTimeFormat().formatToParts());
8987

9088
testServiceTypes(Intl.NumberFormat);
9189
testServiceGetterTypes(Intl.NumberFormat, 'format');

utils/testsuite/testsuite_skiplist.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,11 @@
11151115
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/order-dayPeriod.js",
11161116
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/hourCycle-timeStyle.js",
11171117
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/order-style.js",
1118-
"test262/test/intl402/DateTimeFormat/prototype/formatToParts",
1118+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/related-year-zh.js",
1119+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-narrow-en.js",
1120+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-long-en.js",
1121+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-short-en.js",
1122+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/fractionalSecondDigits.js",
11191123
"test262/test/intl402/DateTimeFormat/prototype/format/timedatestyle-en.js",
11201124
"test262/test/intl402/DateTimeFormat/prototype/format/dayPeriod-long-en.js",
11211125
"test262/test/intl402/DateTimeFormat/prototype/format/dayPeriod-narrow-en.js",
@@ -1125,6 +1129,7 @@
11251129
# This test assumes that "year" has some default value. That is an implementation-defined behavior.
11261130
# In our case it remains undefined, which causes this test to fail.
11271131
"test262/test/intl402/DateTimeFormat/default-options-object-prototype.js",
1132+
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/related-year.js",
11281133
"test262/test/intl402/DateTimeFormat/prototype/format/proleptic-gregorian-calendar.js",
11291134
"test262/test/intl402/DateTimeFormat/prototype/formatRange",
11301135
"test262/test/intl402/DateTimeFormat/prototype/formatRangeToParts",

0 commit comments

Comments
 (0)