Title: [283974] branches/safari-612-branch

Diff

Modified: branches/safari-612-branch/JSTests/ChangeLog (283973 => 283974)


--- branches/safari-612-branch/JSTests/ChangeLog	2021-10-12 08:40:24 UTC (rev 283973)
+++ branches/safari-612-branch/JSTests/ChangeLog	2021-10-12 09:30:55 UTC (rev 283974)
@@ -1,3 +1,14 @@
+2021-08-26  Yusuke Suzuki  <[email protected]>
+
+        Intl.DateTimeFormat incorrectly parses patterns with 'h' literal
+        https://bugs.webkit.org/show_bug.cgi?id=229313
+        rdar://82414310
+
+        Reviewed by Ross Kirsling.
+
+        * stress/intl-date-pattern-includes-literal-text.js: Added.
+        (shouldBe):
+
 2021-10-06  Russell Epstein  <[email protected]>
 
         Cherry-pick r283556. rdar://problem/83956477

Added: branches/safari-612-branch/JSTests/stress/intl-date-pattern-includes-literal-text.js (0 => 283974)


--- branches/safari-612-branch/JSTests/stress/intl-date-pattern-includes-literal-text.js	                        (rev 0)
+++ branches/safari-612-branch/JSTests/stress/intl-date-pattern-includes-literal-text.js	2021-10-12 09:30:55 UTC (rev 283974)
@@ -0,0 +1,10 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`expected ${expected} but got ${actual}`);
+}
+
+shouldBe(new Intl.DateTimeFormat("fr", {hour: "numeric", hour12: false}).resolvedOptions().hour12, false);
+shouldBe(new Intl.DateTimeFormat("fr", {hour: "numeric", hour12: false}).format(new Date(2021, 2, 3, 23)), `23 h`);
+
+shouldBe(new Intl.DateTimeFormat("fr", {hour: "numeric", hourCycle: 'h24'}).format(new Date(2021, 2, 3, 23)), '23 h');
+shouldBe(new Intl.DateTimeFormat("fr", {hour: "numeric", hourCycle: 'h23'}).format(new Date(2021, 2, 3, 23)), '23 h');

Modified: branches/safari-612-branch/Source/_javascript_Core/ChangeLog (283973 => 283974)


--- branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-10-12 08:40:24 UTC (rev 283973)
+++ branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-10-12 09:30:55 UTC (rev 283974)
@@ -1,3 +1,27 @@
+2021-08-26  Yusuke Suzuki  <[email protected]>
+
+        Intl.DateTimeFormat incorrectly parses patterns with 'h' literal
+        https://bugs.webkit.org/show_bug.cgi?id=229313
+        rdar://82414310
+
+        Reviewed by Ross Kirsling.
+
+        While DateTimeFormat pattern and skeleton can include single-quoted literal texts,
+        we are not respecting that when parsing them to extract information. As a result,
+        we are incorrectly extracting hour-cycle information for "fr" locale since it can
+        include "HH 'h'" pattern text. This patch fixes that by skipping literal text
+        correctly.
+
+        * runtime/IntlDateTimeFormat.cpp:
+        (JSC::skipLiteralText):
+        (JSC::IntlDateTimeFormat::setFormatsFromPattern):
+        (JSC::IntlDateTimeFormat::hourCycleFromPattern):
+        (JSC::IntlDateTimeFormat::replaceHourCycleInSkeleton):
+        (JSC::IntlDateTimeFormat::replaceHourCycleInPattern):
+        * runtime/IntlDateTimeFormat.h:
+        * runtime/IntlLocale.cpp:
+        (JSC::IntlLocale::hourCycles):
+
 2021-10-06  Russell Epstein  <[email protected]>
 
         Cherry-pick r283632. rdar://problem/83942704

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp (283973 => 283974)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp	2021-10-12 08:40:24 UTC (rev 283973)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.cpp	2021-10-12 09:30:55 UTC (rev 283974)
@@ -310,13 +310,55 @@
     return options;
 }
 
+template<typename Container>
+static inline unsigned skipLiteralText(const Container& container, unsigned start, unsigned length)
+{
+    // Skip literal text. We do not recognize '' single quote specially.
+    // `'ICU''s change'` is `ICU's change` literal text, but even if we split this text into two literal texts,
+    // we can anyway skip the same thing.
+    // This function returns the last character index which can be considered as a literal text.
+    ASSERT(length);
+    ASSERT(start < length);
+    ASSERT(container[start] == '\'');
+    unsigned index = start;
+    ++index;
+    if (!(index < length))
+        return length - 1;
+    for (; index < length; ++index) {
+        if (container[index] == '\'')
+            return index;
+    }
+    return length - 1;
+}
+
 void IntlDateTimeFormat::setFormatsFromPattern(const StringView& pattern)
 {
     // Get all symbols from the pattern, and set format fields accordingly.
     // http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
+    //
+    // A date pattern is a character string consisting of two types of elements:
+    // 1. Pattern fields, which repeat a specific pattern character one or more times.
+    //    These fields are replaced with date and time data from a calendar when formatting,
+    //    or used to generate data for a calendar when parsing. Currently, A..Z and a..z are
+    //    reserved for use as pattern characters (unless they are quoted, see next item).
+    //    The pattern characters currently defined, and the meaning of different fields
+    //    lengths for then, are listed in the Date Field Symbol Table below.
+    // 2. Literal text, which is output as-is when formatting, and must closely match when
+    //    parsing. Literal text can include:
+    //      1. Any characters other than A..Z and a..z, including spaces and punctuation.
+    //      2. Any text between single vertical quotes ('xxxx'), which may include A..Z and
+    //         a..z as literal text.
+    //      3. Two adjacent single vertical quotes (''), which represent a literal single quote,
+    //         either inside or outside quoted text.
     unsigned length = pattern.length();
     for (unsigned i = 0; i < length; ++i) {
-        UChar currentCharacter = pattern[i];
+        auto currentCharacter = pattern[i];
+
+        if (currentCharacter == '\'') {
+            i = skipLiteralText(pattern, i, length);
+            continue;
+        }
+
         if (!isASCIIAlpha(currentCharacter))
             continue;
 
@@ -453,9 +495,16 @@
     return HourCycle::None;
 }
 
-inline IntlDateTimeFormat::HourCycle IntlDateTimeFormat::hourCycleFromPattern(const Vector<UChar, 32>& pattern)
+IntlDateTimeFormat::HourCycle IntlDateTimeFormat::hourCycleFromPattern(const Vector<UChar, 32>& pattern)
 {
-    for (auto character : pattern) {
+    for (unsigned i = 0, length = pattern.size(); i < length; ++i) {
+        auto character = pattern[i];
+
+        if (character == '\'') {
+            i = skipLiteralText(pattern, i, length);
+            continue;
+        }
+
         switch (character) {
         case 'K':
         case 'h':
@@ -472,7 +521,16 @@
     UChar skeletonCharacter = 'H';
     if (isHour12)
         skeletonCharacter = 'h';
-    for (auto& character : skeleton) {
+    for (unsigned i = 0, length = skeleton.size(); i < length; ++i) {
+        auto& character = skeleton[i];
+
+        // ICU DateTimeFormat skeleton also has single-quoted literal text.
+        // https://github.com/unicode-org/icu/blob/main/icu4c/source/i18n/dtptngen.cpp
+        if (character == '\'') {
+            i = skipLiteralText(skeleton, i, length);
+            continue;
+        }
+
         switch (character) {
         case 'h':
         case 'H':
@@ -503,7 +561,14 @@
         return;
     }
 
-    for (auto& character : pattern) {
+    for (unsigned i = 0, length = pattern.size(); i < length; ++i) {
+        auto& character = pattern[i];
+
+        if (character == '\'') {
+            i = skipLiteralText(pattern, i, length);
+            continue;
+        }
+
         switch (character) {
         case 'K':
         case 'h':

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.h (283973 => 283974)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.h	2021-10-12 08:40:24 UTC (rev 283973)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/IntlDateTimeFormat.h	2021-10-12 09:30:55 UTC (rev 283974)
@@ -83,6 +83,9 @@
 
     static IntlDateTimeFormat* unwrapForOldFunctions(JSGlobalObject*, JSValue);
 
+    enum class HourCycle : uint8_t { None, H11, H12, H23, H24 };
+    static HourCycle hourCycleFromPattern(const Vector<UChar, 32>&);
+
 private:
     IntlDateTimeFormat(VM&, Structure*);
     void finishCreation(VM&);
@@ -92,7 +95,6 @@
 
     UDateIntervalFormat* createDateIntervalFormatIfNecessary(JSGlobalObject*);
 
-    enum class HourCycle : uint8_t { None, H11, H12, H23, H24 };
     enum class Weekday : uint8_t { None, Narrow, Short, Long };
     enum class Era : uint8_t { None, Narrow, Short, Long };
     enum class Year : uint8_t { None, TwoDigit, Numeric };
@@ -121,7 +123,6 @@
 
     static HourCycle hourCycleFromSymbol(UChar);
     static HourCycle parseHourCycle(const String&);
-    static HourCycle hourCycleFromPattern(const Vector<UChar, 32>&);
     static void replaceHourCycleInSkeleton(Vector<UChar, 32>&, bool hour12);
     static void replaceHourCycleInPattern(Vector<UChar, 32>&, HourCycle);
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to