From 2eac1c34ebedde093d87046a066a7696be0f14ab Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Fri, 21 Jun 2024 10:10:42 +0000 Subject: [PATCH 1/5] Fix. --- .../Globalization/CultureData.Browser.cs | 2 +- .../DateTimeFormatInfoFullDateTimePattern.cs | 28 ++++++------- .../DateTimeFormatInfoLongDatePattern.cs | 25 +++++------ .../DateTimeFormatInfoLongTimePattern.cs | 2 +- .../DateTimeFormatInfoShortTimePattern.cs | 2 +- .../runtime/hybrid-globalization/calendar.ts | 41 ++++++++++++++++++- .../hybrid-globalization/culture-info.ts | 37 ++++++++++++++--- 7 files changed, 100 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Browser.cs index 417bd563bf357..897b14e44b3fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Browser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Browser.cs @@ -10,7 +10,7 @@ namespace System.Globalization { internal sealed partial class CultureData { - private const int CULTURE_INFO_BUFFER_LEN = 50; + private const int CULTURE_INFO_BUFFER_LEN = 60; private const int LOCALE_INFO_BUFFER_LEN = 80; private void JSInitLocaleInfo() diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoFullDateTimePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoFullDateTimePattern.cs index 96404330e027e..f84360d35dee9 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoFullDateTimePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoFullDateTimePattern.cs @@ -28,14 +28,14 @@ public static IEnumerable FullDateTimePattern_Get_TestData_HybridGloba { yield return new object[] { new CultureInfo("ar-SA").DateTimeFormat, "dddd، d MMMM yyyy h:mm:ss tt" }; // dddd، d MMMM yyyy g h:mm:ss tt yield return new object[] { new CultureInfo("am-ET").DateTimeFormat, "yyyy MMMM d, dddd h:mm:ss tt" }; - yield return new object[] { new CultureInfo("bg-BG").DateTimeFormat, PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d MMMM yyyy г. H:mm:ss ч." : "dddd, d MMMM yyyy г. H:mm:ss" }; // dddd, d MMMM yyyy 'г'. H:mm:ss + yield return new object[] { new CultureInfo("bg-BG").DateTimeFormat, PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d MMMM yyyy 'г'. H:mm:ss ч." : "dddd, d MMMM yyyy 'г'. H:mm:ss" }; // dddd, d MMMM yyyy 'г'. H:mm:ss yield return new object[] { new CultureInfo("bn-BD").DateTimeFormat, "dddd, d MMMM, yyyy h:mm:ss tt" }; yield return new object[] { new CultureInfo("bn-IN").DateTimeFormat, "dddd, d MMMM, yyyy h:mm:ss tt" }; - string catalanPattern = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d de MMMM de yyyy H:mm:ss" : "dddd, d de MMMM del yyyy H:mm:ss"; - yield return new object[] { new CultureInfo("ca-AD").DateTimeFormat, catalanPattern }; // dddd, d MMMM 'de' yyyy H:mm:ss - yield return new object[] { new CultureInfo("ca-ES").DateTimeFormat, catalanPattern }; // dddd, d MMMM 'de' yyyy H:mm:ss + string catalanPattern = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d 'de' MMMM 'de' yyyy H:mm:ss" : "dddd, d 'de' MMMM 'del' yyyy H:mm:ss"; // dddd, d MMMM 'de' yyyy H:mm:ss + yield return new object[] { new CultureInfo("ca-AD").DateTimeFormat, catalanPattern }; + yield return new object[] { new CultureInfo("ca-ES").DateTimeFormat, catalanPattern }; yield return new object[] { new CultureInfo("cs-CZ").DateTimeFormat, "dddd d. MMMM yyyy H:mm:ss" }; - yield return new object[] { new CultureInfo("da-DK").DateTimeFormat, "dddd den d. MMMM yyyy HH.mm.ss" }; // dddd 'den' d. MMMM yyyy HH.mm.ss + yield return new object[] { new CultureInfo("da-DK").DateTimeFormat, "dddd 'den' d. MMMM yyyy HH.mm.ss" }; yield return new object[] { new CultureInfo("de-AT").DateTimeFormat, "dddd, d. MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("de-BE").DateTimeFormat, "dddd, d. MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("de-CH").DateTimeFormat, "dddd, d. MMMM yyyy HH:mm:ss" }; @@ -147,16 +147,16 @@ public static IEnumerable FullDateTimePattern_Get_TestData_HybridGloba yield return new object[] { new CultureInfo("en-ZA").DateTimeFormat, "dddd, d MMMM yyyy HH:mm:ss" }; // dddd, dd MMMM yyyy HH:mm:ss yield return new object[] { new CultureInfo("en-ZM").DateTimeFormat, "dddd, d MMMM yyyy h:mm:ss tt" }; yield return new object[] { new CultureInfo("en-ZW").DateTimeFormat, "dddd, d MMMM yyyy HH:mm:ss" }; // dddd, dd MMMM yyyy HH:mm:ss - string latinAmericaSpanishFormat = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d de MMMM de yyyy HH:mm:ss" : "dddd, d de MMMM de yyyy h:mm:ss tt"; // dddd, d 'de' MMMM 'de' yyyy HH:mm:ss + string latinAmericaSpanishFormat = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d 'de' MMMM 'de' yyyy HH:mm:ss" : "dddd, d 'de' MMMM 'de' yyyy h:mm:ss tt"; // dddd, d 'de' MMMM 'de' yyyy HH:mm:ss yield return new object[] { new CultureInfo("es-419").DateTimeFormat, latinAmericaSpanishFormat }; - yield return new object[] { new CultureInfo("es-ES").DateTimeFormat, "dddd, d de MMMM de yyyy H:mm:ss" }; // dddd, d 'de' MMMM 'de' yyyy H:mm:ss + yield return new object[] { new CultureInfo("es-ES").DateTimeFormat, "dddd, d 'de' MMMM 'de' yyyy H:mm:ss" }; yield return new object[] { new CultureInfo("es-MX").DateTimeFormat, latinAmericaSpanishFormat }; yield return new object[] { new CultureInfo("et-EE").DateTimeFormat, "dddd, d. MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("fa-IR").DateTimeFormat, "yyyy MMMM d, dddd H:mm:ss" }; yield return new object[] { new CultureInfo("fi-FI").DateTimeFormat, "dddd d. MMMM yyyy H.mm.ss" }; yield return new object[] { new CultureInfo("fil-PH").DateTimeFormat, "dddd, MMMM d, yyyy h:mm:ss tt" }; yield return new object[] { new CultureInfo("fr-BE").DateTimeFormat, "dddd d MMMM yyyy HH:mm:ss" }; - yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "dddd d MMMM yyyy HH h mm min ss s" }; // dddd d MMMM yyyy HH 'h' mm 'min' ss 's' + yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "dddd d MMMM yyyy HH 'h' mm 'min' ss 's'" }; yield return new object[] { new CultureInfo("fr-CH").DateTimeFormat, "dddd, d MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("fr-FR").DateTimeFormat, "dddd d MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("gu-IN").DateTimeFormat, "dddd, d MMMM, yyyy hh:mm:ss tt" }; @@ -171,8 +171,8 @@ public static IEnumerable FullDateTimePattern_Get_TestData_HybridGloba yield return new object[] { new CultureInfo("ja-JP").DateTimeFormat, "yyyy年M月d日dddd H:mm:ss" }; yield return new object[] { new CultureInfo("kn-IN").DateTimeFormat, "dddd, MMMM d, yyyy hh:mm:ss tt" }; yield return new object[] { new CultureInfo("ko-KR").DateTimeFormat, "yyyy년 M월 d일 dddd tt h:mm:ss" }; - yield return new object[] { new CultureInfo("lt-LT").DateTimeFormat, "yyyy m. MMMM d d., dddd HH:mm:ss" }; // yyyy 'm'. MMMM d 'd'., dddd HH:mm:ss - yield return new object[] { new CultureInfo("lv-LV").DateTimeFormat, "dddd, yyyy. gada d. MMMM HH:mm:ss" }; // dddd, yyyy. 'gada' d. MMMM HH:mm:ss + yield return new object[] { new CultureInfo("lt-LT").DateTimeFormat, "yyyy 'm'. MMMM d 'd'., dddd HH:mm:ss" }; + yield return new object[] { new CultureInfo("lv-LV").DateTimeFormat, "dddd, yyyy. 'gada' d. MMMM HH:mm:ss" }; yield return new object[] { new CultureInfo("ml-IN").DateTimeFormat, "yyyy, MMMM d, dddd h:mm:ss tt" }; yield return new object[] { new CultureInfo("mr-IN").DateTimeFormat, "dddd, d MMMM, yyyy h:mm:ss tt" }; yield return new object[] { new CultureInfo("ms-BN").DateTimeFormat, "dddd, d MMMM yyyy h:mm:ss tt" }; // dd MMMM yyyy h:mm:ss tt @@ -184,10 +184,10 @@ public static IEnumerable FullDateTimePattern_Get_TestData_HybridGloba yield return new object[] { new CultureInfo("nl-BE").DateTimeFormat, "dddd d MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("nl-NL").DateTimeFormat, "dddd d MMMM yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("pl-PL").DateTimeFormat, "dddd, d MMMM yyyy HH:mm:ss" }; - yield return new object[] { new CultureInfo("pt-BR").DateTimeFormat, "dddd, d de MMMM de yyyy HH:mm:ss" }; // dddd, d 'de' MMMM 'de' yyyy HH:mm:ss - yield return new object[] { new CultureInfo("pt-PT").DateTimeFormat, "dddd, d de MMMM de yyyy HH:mm:ss" }; // dddd, d 'de' MMMM 'de' yyyy HH:mm:ss + yield return new object[] { new CultureInfo("pt-BR").DateTimeFormat, "dddd, d 'de' MMMM 'de' yyyy HH:mm:ss" }; + yield return new object[] { new CultureInfo("pt-PT").DateTimeFormat, "dddd, d 'de' MMMM 'de' yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("ro-RO").DateTimeFormat, "dddd, d MMMM yyyy HH:mm:ss" }; - yield return new object[] { new CultureInfo("ru-RU").DateTimeFormat, "dddd, d MMMM yyyy г. HH:mm:ss" }; // dddd, d MMMM yyyy 'г'. HH:mm:ss + yield return new object[] { new CultureInfo("ru-RU").DateTimeFormat, "dddd, d MMMM yyyy 'г'. HH:mm:ss" }; yield return new object[] { new CultureInfo("sk-SK").DateTimeFormat, "dddd d. MMMM yyyy H:mm:ss" }; yield return new object[] { new CultureInfo("sl-SI").DateTimeFormat, "dddd, d. MMMM yyyy HH:mm:ss" }; // dddd, dd. MMMM yyyy HH:mm:ss yield return new object[] { new CultureInfo("sr-Cyrl-RS").DateTimeFormat, "dddd, d. MMMM yyyy. HH:mm:ss" }; // dddd, dd. MMMM yyyy. HH:mm:ss @@ -206,7 +206,7 @@ public static IEnumerable FullDateTimePattern_Get_TestData_HybridGloba yield return new object[] { new CultureInfo("th-TH").DateTimeFormat, "ddddที่ d MMMM g yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("tr-CY").DateTimeFormat, "d MMMM yyyy dddd h:mm:ss tt" }; yield return new object[] { new CultureInfo("tr-TR").DateTimeFormat, "d MMMM yyyy dddd HH:mm:ss" }; - yield return new object[] { new CultureInfo("uk-UA").DateTimeFormat, "dddd, d MMMM yyyy р. HH:mm:ss" }; // dddd, d MMMM yyyy 'р'. HH:mm:ss + yield return new object[] { new CultureInfo("uk-UA").DateTimeFormat, "dddd, d MMMM yyyy 'р'. HH:mm:ss" }; yield return new object[] { new CultureInfo("vi-VN").DateTimeFormat, "dddd, d MMMM, yyyy HH:mm:ss" }; yield return new object[] { new CultureInfo("zh-CN").DateTimeFormat, "yyyy年M月d日dddd HH:mm:ss" }; // yyyy年M月d日dddd tth:mm:ss yield return new object[] { new CultureInfo("zh-Hans-HK").DateTimeFormat, "yyyy年M月d日dddd tth:mm:ss" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongDatePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongDatePattern.cs index ec983c3f98765..68e6fbdb950b5 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongDatePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongDatePattern.cs @@ -30,14 +30,14 @@ public static IEnumerable LongDatePattern_Get_TestData_HybridGlobaliza // see the comments on the right to check the non-Hybrid result, if it differs yield return new object[] {"ar-SA", "dddd، d MMMM yyyy" }; // dddd، d MMMM yyyy g yield return new object[] {"am-ET", "yyyy MMMM d, dddd" }; - yield return new object[] {"bg-BG", "dddd, d MMMM yyyy г." }; // "dddd, d MMMM yyyy 'г'." + yield return new object[] {"bg-BG", "dddd, d MMMM yyyy 'г'." }; yield return new object[] {"bn-BD", "dddd, d MMMM, yyyy" }; yield return new object[] {"bn-IN", "dddd, d MMMM, yyyy" }; - string catalanianPattern = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d de MMMM de yyyy" : "dddd, d de MMMM del yyyy"; // "dddd, d MMMM 'de' yyyy" + string catalanianPattern = PlatformDetection.IsFirefox || PlatformDetection.IsNodeJS ? "dddd, d 'de' MMMM 'de' yyyy" : "dddd, d 'de' MMMM 'del' yyyy"; // "dddd, d MMMM 'de' yyyy" yield return new object[] {"ca-AD", catalanianPattern }; yield return new object[] {"ca-ES", catalanianPattern }; yield return new object[] {"cs-CZ", "dddd d. MMMM yyyy" }; - yield return new object[] {"da-DK", "dddd den d. MMMM yyyy" }; // dddd 'den' d. MMMM yyyy + yield return new object[] {"da-DK", "dddd 'den' d. MMMM yyyy" }; yield return new object[] {"de-AT", "dddd, d. MMMM yyyy" }; yield return new object[] {"de-BE", "dddd, d. MMMM yyyy" }; yield return new object[] {"de-CH", "dddd, d. MMMM yyyy" }; @@ -150,9 +150,10 @@ public static IEnumerable LongDatePattern_Get_TestData_HybridGlobaliza yield return new object[] {"en-ZM", "dddd, d MMMM yyyy" }; yield return new object[] {"en-ZW", "dddd, d MMMM yyyy" }; // "dddd, dd MMMM yyyy" yield return new object[] {"en-US", "dddd, MMMM d, yyyy" }; - yield return new object[] {"es-419", "dddd, d de MMMM de yyyy" }; // dddd, d 'de' MMMM 'de' yyyy - yield return new object[] {"es-ES", "dddd, d de MMMM de yyyy" }; // dddd, d 'de' MMMM 'de' yyyy - yield return new object[] {"es-MX", "dddd, d de MMMM de yyyy" }; // dddd, d 'de' MMMM 'de' yyyy + string spanishPattern = "dddd, d 'de' MMMM 'de' yyyy"; + yield return new object[] {"es-419", spanishPattern }; + yield return new object[] {"es-ES", spanishPattern }; + yield return new object[] {"es-MX", spanishPattern }; yield return new object[] {"et-EE", "dddd, d. MMMM yyyy" }; yield return new object[] {"fa-IR", "yyyy MMMM d, dddd" }; yield return new object[] {"fi-FI", "dddd d. MMMM yyyy" }; @@ -173,8 +174,8 @@ public static IEnumerable LongDatePattern_Get_TestData_HybridGlobaliza yield return new object[] {"ja-JP", "yyyy年M月d日dddd" }; yield return new object[] {"kn-IN", "dddd, MMMM d, yyyy" }; yield return new object[] {"ko-KR", "yyyy년 M월 d일 dddd" }; - yield return new object[] {"lt-LT", "yyyy m. MMMM d d., dddd" }; // "yyyy 'm'. MMMM d 'd'., dddd" - yield return new object[] {"lv-LV", "dddd, yyyy. gada d. MMMM" }; // "dddd, yyyy. 'gada' d. MMMM" + yield return new object[] {"lt-LT", "yyyy 'm'. MMMM d 'd'., dddd" }; + yield return new object[] {"lv-LV", "dddd, yyyy. 'gada' d. MMMM" }; yield return new object[] {"ml-IN", "yyyy, MMMM d, dddd" }; yield return new object[] {"mr-IN", "dddd, d MMMM, yyyy" }; yield return new object[] {"ms-BN", "dddd, d MMMM yyyy" }; // "dd MMMM yyyy" @@ -186,10 +187,10 @@ public static IEnumerable LongDatePattern_Get_TestData_HybridGlobaliza yield return new object[] {"nl-BE", "dddd d MMMM yyyy" }; yield return new object[] {"nl-NL", "dddd d MMMM yyyy" }; yield return new object[] {"pl-PL", "dddd, d MMMM yyyy" }; - yield return new object[] {"pt-BR", "dddd, d de MMMM de yyyy" }; // dddd, d 'de' MMMM 'de' yyyy - yield return new object[] {"pt-PT", "dddd, d de MMMM de yyyy" }; // dddd, d 'de' MMMM 'de' yyyy + yield return new object[] {"pt-BR", "dddd, d 'de' MMMM 'de' yyyy" }; + yield return new object[] {"pt-PT", "dddd, d 'de' MMMM 'de' yyyy" }; yield return new object[] {"ro-RO", "dddd, d MMMM yyyy" }; - yield return new object[] {"ru-RU", "dddd, d MMMM yyyy г." }; // "dddd, d MMMM yyyy 'г'." + yield return new object[] {"ru-RU", "dddd, d MMMM yyyy 'г'." }; yield return new object[] {"sk-SK", "dddd d. MMMM yyyy" }; yield return new object[] {"sl-SI", "dddd, d. MMMM yyyy" }; // "dddd, dd. MMMM yyyy" yield return new object[] {"sr-Cyrl-RS", "dddd, d. MMMM yyyy." }; // "dddd, dd. MMMM yyyy" @@ -208,7 +209,7 @@ public static IEnumerable LongDatePattern_Get_TestData_HybridGlobaliza yield return new object[] {"th-TH", "ddddที่ d MMMM g yyyy" }; yield return new object[] {"tr-CY", "d MMMM yyyy dddd" }; yield return new object[] {"tr-TR", "d MMMM yyyy dddd" }; - yield return new object[] {"uk-UA", "dddd, d MMMM yyyy р." }; // "dddd, d MMMM yyyy 'р'." + yield return new object[] {"uk-UA", "dddd, d MMMM yyyy 'р'." }; yield return new object[] {"vi-VN", "dddd, d MMMM, yyyy" }; yield return new object[] {"zh-CN", "yyyy年M月d日dddd" }; yield return new object[] {"zh-Hans-HK", "yyyy年M月d日dddd" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs index 920dbecb14daf..f5ba5ae87bb96 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs @@ -146,7 +146,7 @@ public static IEnumerable LongTimePattern_Get_TestData_HybridGlobaliza yield return new object[] { new CultureInfo("fi-FI").DateTimeFormat, "H.mm.ss" }; yield return new object[] { new CultureInfo("fil-PH").DateTimeFormat, "h:mm:ss tt" }; yield return new object[] { new CultureInfo("fr-BE").DateTimeFormat, "HH:mm:ss" }; - yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "HH h mm min ss s" }; // HH 'h' mm 'min' ss 's' + yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "HH 'h' mm 'min' ss 's'" }; yield return new object[] { new CultureInfo("fr-CH").DateTimeFormat, "HH:mm:ss" }; yield return new object[] { new CultureInfo("fr-FR").DateTimeFormat, "HH:mm:ss" }; yield return new object[] { new CultureInfo("gu-IN").DateTimeFormat, "hh:mm:ss tt" }; diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs index 5e54139c88fc0..73eb51b6d2747 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs @@ -147,7 +147,7 @@ public static IEnumerable ShortTimePattern_Get_TestData_HybridGlobaliz yield return new object[] { new CultureInfo("fi-FI").DateTimeFormat, "H.mm" }; yield return new object[] { new CultureInfo("fil-PH").DateTimeFormat, "h:mm tt" }; yield return new object[] { new CultureInfo("fr-BE").DateTimeFormat, "HH:mm" }; - yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "HH h mm min" }; // HH 'h' mm + yield return new object[] { new CultureInfo("fr-CA").DateTimeFormat, "HH 'h' mm 'min'" }; // HH 'h' mm yield return new object[] { new CultureInfo("fr-CH").DateTimeFormat, "HH:mm" }; yield return new object[] { new CultureInfo("fr-FR").DateTimeFormat, "HH:mm" }; yield return new object[] { new CultureInfo("gu-IN").DateTimeFormat, "hh:mm tt" }; diff --git a/src/mono/browser/runtime/hybrid-globalization/calendar.ts b/src/mono/browser/runtime/hybrid-globalization/calendar.ts index 2a0d3ce70cbc6..cd5ef52e4471f 100644 --- a/src/mono/browser/runtime/hybrid-globalization/calendar.ts +++ b/src/mono/browser/runtime/hybrid-globalization/calendar.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. @@ -10,6 +11,7 @@ import { INNER_SEPARATOR, OUTER_SEPARATOR } from "./helpers"; const MONTH_CODE = "MMMM"; const YEAR_CODE = "yyyy"; const DAY_CODE = "d"; +const WEEKDAY_CODE = "dddd"; // this function joins all calendar info with OUTER_SEPARATOR into one string and returns it back to managed code export function mono_wasm_get_calendar_info (culture: number, cultureLength: number, calendarId: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr { @@ -192,10 +194,11 @@ function getLongDatePattern (locale: string | undefined, date: Date): string { pattern = pattern.replace(yearStr, YEAR_CODE); const weekday = date.toLocaleDateString(locale, { weekday: "long" }).toLowerCase(); const replacedWeekday = getGenitiveForName(date, pattern, weekday, new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", day: "numeric" })); - pattern = pattern.replace(replacedWeekday, "dddd"); + pattern = pattern.replace(replacedWeekday, WEEKDAY_CODE); pattern = pattern.replace("22", DAY_CODE); const dayStr = date.toLocaleDateString(locale, { day: "numeric" }); // should we replace it for localized digits? - return pattern.replace(dayStr, DAY_CODE); + pattern = pattern.replace(dayStr, DAY_CODE); + return wrapSubstrings(pattern, locale); } function getGenitiveForName (date: Date, pattern: string, name: string, formatWithoutName: Intl.DateTimeFormat) { @@ -326,3 +329,37 @@ function getEraNames (date: Date, locale: string | undefined, calendarId: number }; } } + +// wraps all substrings in the format in quotes, except for key words +// transform e.g. "dddd, d MMMM yyyy г." into "dddd, d MMMM yyyy 'г'." +function wrapSubstrings (str: string, locale: string | undefined) { + const words = str.split(" "); + // locales that write date nearly without spaces should not have format parts quoted - "ja", "zh" + // "ko" format parts should not be quoted but processing it would overcomplicate the logic + if (words.length <= 2 || locale?.startsWith("ko")) { + return str; + } + + const keyWords = [MONTH_CODE, YEAR_CODE, DAY_CODE, WEEKDAY_CODE]; + for (let i = 0; i < words.length; i++) { + if (!keyWords.includes(words[i].replace(",", "")) && + !keyWords.includes(words[i].replace(".", "")) && + !keyWords.includes(words[i].replace("\u060c", "")) && + !keyWords.includes(words[i].replace("\u05d1", ""))) { + if (words[i].endsWith(".,")) { + // if the "word" appears twice, then the occurence with punctuation is not a code but fixed part of the format + // see: "hu-HU" vs "lt-LT" format + const wordNoPuctuation = words[i].slice(0, -2); + if (words.filter(x => x == wordNoPuctuation).length == 1) + words[i] = `'${words[i].slice(0, -2)}'.,`; + } else if (words[i].endsWith(".")) { + words[i] = `'${words[i].slice(0, -1)}'.`; + } else if (words[i].endsWith(",")) { + words[i] = `'${words[i].slice(0, -1)}',`; + } else { + words[i] = `'${words[i]}'`; + } + } + } + return words.join(" "); +} diff --git a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts index 6c938198dabb7..cceaab3d3e7ed 100644 --- a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts +++ b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts @@ -6,6 +6,14 @@ import { runtimeHelpers } from "./module-exports"; import { Int32Ptr, VoidPtr } from "../types/emscripten"; import { OUTER_SEPARATOR, normalizeLocale } from "./helpers"; +const NO_PREFIX_24H = "H"; +const PREFIX_24H = "HH"; +const NO_PREFIX_12H = "h"; +const PREFIX_12H = "hh"; +const SECONDS_CODE = "ss"; +const MINUTES_CODE = "mm"; +const DESIGNATOR_CODE = "tt"; + export function mono_wasm_get_culture_info (culture: number, cultureLength: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr { try { const cultureName = runtimeHelpers.utf16ToString(culture, (culture + 2 * cultureLength)); @@ -74,7 +82,7 @@ function getLongTimePattern (locale: string | undefined, designators: any): stri const shortPmStyle = shortTime.format(pmTime); // 12:15:30 PM const minutes = pmTime.toLocaleTimeString(locale, { minute: "numeric" }); // 15 const seconds = pmTime.toLocaleTimeString(locale, { second: "numeric" }); // 30 - let pattern = shortPmStyle.replace(designators.pm, "tt").replace(minutes, "mm").replace(seconds, "ss"); // 12:mm:ss tt + let pattern = shortPmStyle.replace(designators.pm, DESIGNATOR_CODE).replace(minutes, MINUTES_CODE).replace(seconds, SECONDS_CODE); // 12:mm:ss tt const isISOStyle = pattern.includes(localizedHour24); // 24h or 12h pattern? const localized0 = (0).toLocaleString(locale); @@ -84,22 +92,22 @@ function getLongTimePattern (locale: string | undefined, designators: any): stri let hourPattern; if (isISOStyle) { // 24h const hasPrefix = h12Style.includes(hour12WithPrefix); - hourPattern = hasPrefix ? "HH" : "H"; + hourPattern = hasPrefix ? PREFIX_24H : NO_PREFIX_24H; pattern = pattern.replace(localizedHour24, hourPattern); } else { // 12h const hasPrefix = h12Style.includes(hour12WithPrefix); - hourPattern = hasPrefix ? "hh" : "h"; + hourPattern = hasPrefix ? PREFIX_12H : NO_PREFIX_12H; pattern = pattern.replace(hasPrefix ? hour12WithPrefix : localizedHour12, hourPattern); } - return pattern; + return wrapSubstrings(pattern); } function getShortTimePattern (pattern: string): string { // remove seconds: // short dotnet pattern does not contain seconds while JS's pattern always contains them - const secondsIdx = pattern.indexOf("ss"); + const secondsIdx = pattern.indexOf(SECONDS_CODE); if (secondsIdx > 0) { - const secondsWithSeparator = `${pattern[secondsIdx - 1]}ss`; + const secondsWithSeparator = `${pattern[secondsIdx - 1]}${SECONDS_CODE}`; // en-US: 12:mm:ss tt -> 12:mm tt; // fr-CA: 12 h mm min ss s -> 12 h mm min s const shortPatternNoSecondsDigits = pattern.replace(secondsWithSeparator, ""); @@ -111,3 +119,20 @@ function getShortTimePattern (pattern: string): string { } return pattern; } + +// wraps all substrings in the format in quotes, except for key words +// transform e.g. "HH h mm min ss s" into "HH 'h' mm 'min' ss 's'" +// problem with "h" that can be both hour code and a fixed (quoted) part of the format +// workaround for "fr-CA" by preventing quoting of "HH" +function wrapSubstrings (str: string) { + const keyWords = [SECONDS_CODE, MINUTES_CODE, DESIGNATOR_CODE, PREFIX_24H]; + const words = str.split(" "); + + for (let i = 0; i < words.length; i++) { + if (!words[i].includes(":") && !words[i].includes(".") && !keyWords.includes(words[i])) { + words[i] = `'${words[i]}'`; + } + } + + return words.join(" "); +} From 81502bf348b80662af1895fea75a0b89d57f68d8 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Fri, 21 Jun 2024 10:44:17 +0000 Subject: [PATCH 2/5] Feedback --- src/mono/browser/runtime/hybrid-globalization/calendar.ts | 5 ++--- .../browser/runtime/hybrid-globalization/culture-info.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mono/browser/runtime/hybrid-globalization/calendar.ts b/src/mono/browser/runtime/hybrid-globalization/calendar.ts index cd5ef52e4471f..5cbbaa4793710 100644 --- a/src/mono/browser/runtime/hybrid-globalization/calendar.ts +++ b/src/mono/browser/runtime/hybrid-globalization/calendar.ts @@ -2,7 +2,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -/* eslint-disable no-inner-declarations */ import { VoidPtrNull } from "../types/internal"; import { runtimeHelpers } from "./module-exports"; import { Int32Ptr, VoidPtr } from "../types/emscripten"; @@ -12,6 +11,7 @@ const MONTH_CODE = "MMMM"; const YEAR_CODE = "yyyy"; const DAY_CODE = "d"; const WEEKDAY_CODE = "dddd"; +const keyWords = [MONTH_CODE, YEAR_CODE, DAY_CODE, WEEKDAY_CODE]; // this function joins all calendar info with OUTER_SEPARATOR into one string and returns it back to managed code export function mono_wasm_get_calendar_info (culture: number, cultureLength: number, calendarId: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr { @@ -333,14 +333,13 @@ function getEraNames (date: Date, locale: string | undefined, calendarId: number // wraps all substrings in the format in quotes, except for key words // transform e.g. "dddd, d MMMM yyyy г." into "dddd, d MMMM yyyy 'г'." function wrapSubstrings (str: string, locale: string | undefined) { - const words = str.split(" "); + const words = str.split(/\s+/); // locales that write date nearly without spaces should not have format parts quoted - "ja", "zh" // "ko" format parts should not be quoted but processing it would overcomplicate the logic if (words.length <= 2 || locale?.startsWith("ko")) { return str; } - const keyWords = [MONTH_CODE, YEAR_CODE, DAY_CODE, WEEKDAY_CODE]; for (let i = 0; i < words.length; i++) { if (!keyWords.includes(words[i].replace(",", "")) && !keyWords.includes(words[i].replace(".", "")) && diff --git a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts index cceaab3d3e7ed..d3b0b8341ad81 100644 --- a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts +++ b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts @@ -13,6 +13,7 @@ const PREFIX_12H = "hh"; const SECONDS_CODE = "ss"; const MINUTES_CODE = "mm"; const DESIGNATOR_CODE = "tt"; +const keyWords = [SECONDS_CODE, MINUTES_CODE, DESIGNATOR_CODE, PREFIX_24H]; export function mono_wasm_get_culture_info (culture: number, cultureLength: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr { try { @@ -125,8 +126,7 @@ function getShortTimePattern (pattern: string): string { // problem with "h" that can be both hour code and a fixed (quoted) part of the format // workaround for "fr-CA" by preventing quoting of "HH" function wrapSubstrings (str: string) { - const keyWords = [SECONDS_CODE, MINUTES_CODE, DESIGNATOR_CODE, PREFIX_24H]; - const words = str.split(" "); + const words = str.split(/\s+/); for (let i = 0; i < words.length; i++) { if (!words[i].includes(":") && !words[i].includes(".") && !keyWords.includes(words[i])) { From c41e0b9fc10e822051a74ef1b541c00d8bb1f2cc Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:36:06 +0000 Subject: [PATCH 3/5] No logging --- src/mono/browser/runtime/hybrid-globalization/calendar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/browser/runtime/hybrid-globalization/calendar.ts b/src/mono/browser/runtime/hybrid-globalization/calendar.ts index 5cbbaa4793710..fd211755469d3 100644 --- a/src/mono/browser/runtime/hybrid-globalization/calendar.ts +++ b/src/mono/browser/runtime/hybrid-globalization/calendar.ts @@ -1,7 +1,7 @@ -/* eslint-disable no-console */ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +/* eslint-disable no-inner-declarations */ import { VoidPtrNull } from "../types/internal"; import { runtimeHelpers } from "./module-exports"; import { Int32Ptr, VoidPtr } from "../types/emscripten"; From 5f47133c7c65c63516059831c7de377b706bdbdf Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 24 Jun 2024 10:55:23 +0200 Subject: [PATCH 4/5] Feedback --- .../browser/runtime/hybrid-globalization/culture-info.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts index d3b0b8341ad81..b62b50854408d 100644 --- a/src/mono/browser/runtime/hybrid-globalization/culture-info.ts +++ b/src/mono/browser/runtime/hybrid-globalization/culture-info.ts @@ -13,6 +13,9 @@ const PREFIX_12H = "hh"; const SECONDS_CODE = "ss"; const MINUTES_CODE = "mm"; const DESIGNATOR_CODE = "tt"; +// Note: wrapSubstrings +// The character "h" can be ambiguous as it might represent an hour code hour code and a fixed (quoted) part of the format. +// Special Case for "fr-CA": Always recognize "HH" as a keyword and do not quote it, to avoid formatting issues. const keyWords = [SECONDS_CODE, MINUTES_CODE, DESIGNATOR_CODE, PREFIX_24H]; export function mono_wasm_get_culture_info (culture: number, cultureLength: number, dst: number, dstMaxLength: number, dstLength: Int32Ptr): VoidPtr { @@ -123,8 +126,6 @@ function getShortTimePattern (pattern: string): string { // wraps all substrings in the format in quotes, except for key words // transform e.g. "HH h mm min ss s" into "HH 'h' mm 'min' ss 's'" -// problem with "h" that can be both hour code and a fixed (quoted) part of the format -// workaround for "fr-CA" by preventing quoting of "HH" function wrapSubstrings (str: string) { const words = str.split(/\s+/); From ca3982b5f7f38d551cab839d475ae7b63dc33998 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 24 Jun 2024 11:55:08 +0200 Subject: [PATCH 5/5] Unblock tests. --- .../DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs | 2 +- .../DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs index 53385013c0348..dc02bcba5869d 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs @@ -284,7 +284,7 @@ public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void LongTimePattern_VerifyTimePatterns() { Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs index 71fbad9db2f8d..d806e19b7502c 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs @@ -255,7 +255,7 @@ public void ShortTimePattern_SetReadOnly_ThrowsInvalidOperationException() Assert.Throws(() => DateTimeFormatInfo.InvariantInfo.ShortTimePattern = "HH:mm"); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] + [Fact] public void ShortTimePattern_VerifyTimePatterns() { Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => {