Skip to content

Commit

Permalink
Disable DateTimeFormat::formatToParts for Apple platform (#1155)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #1155

DateTimeFormat::formatToParts is broken in several aspects, e.g.,
date components such as dayPeriod is not handled, string splitting
for other locales (such as "zh-chinese") is incorrect, etc. So we
have to disable it for now, and disable relevant tests.

Reviewed By: neildhar

Differential Revision: D50019923

fbshipit-source-id: da4b5dc0942d712bc475d9309f6e8a42fa8f0b88
lavenzg authored and facebook-github-bot committed Oct 18, 2023
1 parent cad2000 commit c5a633f
Showing 6 changed files with 8 additions and 98 deletions.
2 changes: 1 addition & 1 deletion doc/IntlAPIs.md
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ One popular implementation strategy followed by other engines, is to bundle an i
- `Intl.DateTimeFormat`*
- `Intl.DateTimeFormat.supportedLocalesOf`
- `Intl.DateTimeFormat.prototype.format`
- `Intl.DateTimeFormat.prototype.formatToParts`
- `Intl.DateTimeFormat.prototype.resolvedOptions`

- `Intl.getCanonicalLocales`
@@ -51,6 +50,7 @@ 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

72 changes: 1 addition & 71 deletions lib/Platform/Intl/PlatformIntlApple.mm
Original file line number Diff line number Diff line change
@@ -1230,8 +1230,6 @@ uint8_t getCurrencyDigits(std::u16string_view code) {

std::u16string format(double jsTimeValue) noexcept;

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

private:
void initializeNSDateFormatter() noexcept;

@@ -1897,76 +1895,8 @@ 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 {
return static_cast<DateTimeFormatApple *>(this)->formatToParts(x);
llvm_unreachable("formatToParts is unimplemented on Apple platforms");
}

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

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

defineMethod(
runtime,
19 changes: 0 additions & 19 deletions test/hermes/intl/date-time-format-apple.js
Original file line number Diff line number Diff line change
@@ -142,24 +142,5 @@ 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: 3 additions & 1 deletion test/hermes/intl/intl.js
Original file line number Diff line number Diff line change
@@ -83,7 +83,9 @@ testServiceGetterTypes(Intl.DateTimeFormat, 'format');
testServiceMethodTypes(Intl.DateTimeFormat, 'formatToParts');
testServiceMethodTypes(Intl.DateTimeFormat, 'resolvedOptions');
assert(typeof Intl.DateTimeFormat().format() === 'string');
testParts(Intl.DateTimeFormat().formatToParts());
if(Intl.DateTimeFormat.prototype.formatToParts) {
testParts(Intl.DateTimeFormat().formatToParts());
}

testServiceTypes(Intl.NumberFormat);
testServiceGetterTypes(Intl.NumberFormat, 'format');
7 changes: 1 addition & 6 deletions utils/testsuite/testsuite_skiplist.py
Original file line number Diff line number Diff line change
@@ -1115,11 +1115,7 @@
"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/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/formatToParts",
"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",
@@ -1129,7 +1125,6 @@
# 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",

0 comments on commit c5a633f

Please sign in to comment.