Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Disable DateTimeFormat::formatToParts for Apple platform (#1155)" #1567

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion doc/IntlAPIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ One popular implementation strategy followed by other engines, is to bundle an i
## Supported on Android only
- `Intl.NumberFormat`
- `Intl.NumberFormat.prototype.formatToParts`
- `Intl.DateTimeFormat.prototype.formatToParts`

## * Limitations on property support

Expand Down
72 changes: 71 additions & 1 deletion lib/Platform/Intl/PlatformIntlApple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,8 @@ uint8_t getCurrencyDigits(std::u16string_view code) {

std::u16string format(double jsTimeValue) noexcept;

std::vector<Part> formatToParts(double x) noexcept;

private:
void initializeNSDateFormatter(NSLocale *nsLocale) noexcept;

Expand Down Expand Up @@ -1932,8 +1934,76 @@ uint8_t getCurrencyDigits(std::u16string_view code) {
return static_cast<DateTimeFormatApple *>(this)->format(jsTimeValue);
}

static std::u16string returnTypeOfDate(const char16_t &c16) {
if (c16 == u'a')
return u"dayPeriod";
if (c16 == u'z' || c16 == u'v' || c16 == u'O')
return u"timeZoneName";
if (c16 == u'G')
return u"era";
if (c16 == u'y')
return u"year";
if (c16 == u'M')
return u"month";
if (c16 == u'E')
return u"weekday";
if (c16 == u'd')
return u"day";
if (c16 == u'h' || c16 == u'k' || c16 == u'K' || c16 == u'H')
return u"hour";
if (c16 == u'm')
return u"minute";
if (c16 == u's')
return u"second";
if (c16 == u'S')
return u"fractionalSecond";
return u"literal";
}

// Implementer note: This method corresponds roughly to
// https://402.ecma-international.org/8.0/#sec-formatdatetimetoparts
std::vector<Part> DateTimeFormatApple::formatToParts(double x) noexcept {
// NOTE: We dont have access to localeData.patterns. Instead we use
// NSDateFormatter's foramt string, and break it into components.
// 1. Let parts be ? PartitionDateTimePattern(dateTimeFormat, x).
auto fmt = nsStringToU16String(nsDateFormatter_.dateFormat);
std::unique(fmt.begin(), fmt.end());
auto formattedDate = format(x);
// 2. Let result be ArrayCreate(0).
std::vector<Part> result;
// 3. Let n be 0.
// 4. For each Record { [[Type]], [[Value]] } part in parts, do
// a. Let O be OrdinaryObjectCreate(%Object.prototype%).
// b. Perform ! CreateDataPropertyOrThrow(O, "type", part.[[Type]]).
// c. Perform ! CreateDataPropertyOrThrow(O, "value", part.[[Value]]).
// d. Perform ! CreateDataProperty(result, ! ToString(n), O).
// e. Increment n by 1.
std::u16string currentPart;
unsigned n = 0;
static auto alphanumerics = NSCharacterSet.alphanumericCharacterSet;
for (char16_t c16 : formattedDate) {
if ([alphanumerics characterIsMember:c16]) {
currentPart += c16;
continue;
}
if (currentPart != u"") {
result.push_back(
{{u"type", returnTypeOfDate(fmt[n])}, {u"value", currentPart}});
currentPart = u"";
n++;
}
result.push_back({{u"type", u"literal"}, {u"value", {c16}}});
n++;
}
// Last format string component.
result.push_back(
{{u"type", returnTypeOfDate(fmt[n])}, {u"value", currentPart}});
// 5. Return result.
return result;
}

std::vector<Part> DateTimeFormat::formatToParts(double x) noexcept {
llvm_unreachable("formatToParts is unimplemented on Apple platforms");
return static_cast<DateTimeFormatApple *>(this)->formatToParts(x);
}

class NumberFormatApple : public NumberFormat {
Expand Down
2 changes: 0 additions & 2 deletions lib/VM/JSLib/Intl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -929,15 +929,13 @@ void defineIntlDateTimeFormat(Runtime &runtime, Handle<JSObject> intl) {
false,
true);

#ifndef __APPLE__
defineMethod(
runtime,
prototype,
Predefined::getSymbolID(Predefined::formatToParts),
nullptr,
intlDateTimeFormatPrototypeFormatToParts,
1);
#endif

defineMethod(
runtime,
Expand Down
19 changes: 19 additions & 0 deletions test/hermes/intl/date-time-format-apple.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,24 @@ print(new Intl.DateTimeFormat('en-US').resolvedOptions().numberingSystem);
print(new Intl.DateTimeFormat('en-US', { timeZone: 'SGT'}).resolvedOptions().timeZone);
// CHECK-NEXT: SGT

print(JSON.stringify(new Intl.DateTimeFormat('en-US').formatToParts(date)));
// CHECK-NEXT: [{"value":"1","type":"month"},{"value":"/","type":"literal"},{"value":"2","type":"day"},{"value":"/","type":"literal"},{"value":"2020","type":"year"}]

print(JSON.stringify(new Intl.DateTimeFormat('en-GB').formatToParts(date)));
// CHECK-NEXT: [{"value":"02","type":"day"},{"value":"/","type":"literal"},{"value":"01","type":"month"},{"value":"/","type":"literal"},{"value":"2020","type":"year"}]

print(JSON.stringify(new Intl.DateTimeFormat('en-US', {weekday: 'long',
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
fractionalSecondDigits: 3,
hour12: true,
timeZone: 'UTC'
}).formatToParts(new Date(Date.UTC(2020, 0, 2, 3, 45, 00, 30)))));
// 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"}]

print(new Date(Date.UTC(2020, 0, 2)).toLocaleString("en-US", {weekday: "short", timeZone: "UTC"}))
// CHECK-NEXT: Thu
4 changes: 1 addition & 3 deletions test/hermes/intl/intl.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ testServiceGetterTypes(Intl.DateTimeFormat, 'format');
testServiceMethodTypes(Intl.DateTimeFormat, 'formatToParts');
testServiceMethodTypes(Intl.DateTimeFormat, 'resolvedOptions');
assert(typeof Intl.DateTimeFormat().format() === 'string');
if(Intl.DateTimeFormat.prototype.formatToParts) {
testParts(Intl.DateTimeFormat().formatToParts());
}
testParts(Intl.DateTimeFormat().formatToParts());

testServiceTypes(Intl.NumberFormat);
testServiceGetterTypes(Intl.NumberFormat, 'format');
Expand Down
9 changes: 8 additions & 1 deletion utils/testsuite/testsuite_skiplist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,11 @@
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/order-dayPeriod.js",
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/hourCycle-timeStyle.js",
"test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/order-style.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/related-year-zh.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-narrow-en.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-long-en.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/dayPeriod-short-en.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/fractionalSecondDigits.js",
"test262/test/intl402/DateTimeFormat/prototype/format/timedatestyle-en.js",
"test262/test/intl402/DateTimeFormat/prototype/format/dayPeriod-long-en.js",
"test262/test/intl402/DateTimeFormat/prototype/format/dayPeriod-narrow-en.js",
Expand All @@ -1123,6 +1127,7 @@
# This test assumes that "year" has some default value. That is an implementation-defined behavior.
# In our case it remains undefined, which causes this test to fail.
"test262/test/intl402/DateTimeFormat/default-options-object-prototype.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/related-year.js",
"test262/test/intl402/DateTimeFormat/prototype/format/proleptic-gregorian-calendar.js",
"test262/test/intl402/DateTimeFormat/prototype/formatRange",
"test262/test/intl402/DateTimeFormat/prototype/formatRangeToParts",
Expand Down Expand Up @@ -1954,11 +1959,13 @@
"test262/test/intl402/NumberFormat",
"test262/test/intl402/String/prototype/toLocaleLowerCase",
"test262/test/intl402/String/prototype/toLocaleUpperCase",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts",
],
"darwin": [
# Intl implementation issues on Apple.
"test262/test/intl402/Collator/ignore-invalid-unicode-ext-values.js",
"test262/test/intl402/Collator/unicode-ext-value-collation.js",
"test262/test/intl402/DateTimeFormat/prototype/formatToParts/offset-timezone-correct.js",
],
}

Expand Down
Loading