From e61e7ab8695136de1a92b7bc81410628fa58b070 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Wed, 19 Jun 2024 12:04:14 +0800 Subject: [PATCH 01/13] Fix the ICU time format conversion logic Revise the ICU time format conversion logic to support all unquoted literal texts and the `B` and `b` pattern symbols. Fix #103592 --- .../src/System/Globalization/CultureData.Icu.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 7bfd0826ae183b..7e3bd90d5a6956 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -378,18 +378,15 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr } break; - case ':': - case '.': case 'H': case 'h': case 'm': case 's': - case ' ': - case '\u00A0': // no-break space - case '\u202F': // narrow no-break space result[resultPos++] = current; break; case 'a': // AM/PM + case 'b': // am, pm, noon, midnight + case 'B': // flexible day periods if (!amPmAdded) { amPmAdded = true; @@ -398,6 +395,13 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr } break; + default: + // Treat non-ASCII characters as literals + if (!char.IsAsciiLetter(current)) + { + result[resultPos++] = current; + } + break; } } From f6c866876d3abcef09cce01b2fb2e5f6eb59fb5a Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 00:21:23 +0800 Subject: [PATCH 02/13] Clarify literal texts in the conversion logic --- .../src/System/Globalization/CultureData.Icu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 7e3bd90d5a6956..848fe222731e32 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -396,7 +396,7 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr break; default: - // Treat non-ASCII characters as literals + // Characters that are not ASCII letters are literal texts if (!char.IsAsciiLetter(current)) { result[resultPos++] = current; From 06b1118f966f9ba3ecbfa418a86c755976c88913 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 00:26:38 +0800 Subject: [PATCH 03/13] Add tests for verifying time patterns Add tests verifying that all the short and long time patterns either use a 24-hour clock or have an AM/PM designator. --- .../DateTimeFormatInfoLongTimePattern.cs | 19 +++++++++++++++++++ .../DateTimeFormatInfoShortTimePattern.cs | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) 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 920dbecb14daf3..3ea6acc6828be2 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,6 +284,25 @@ public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU() } } + [Fact] + public void LongTimePattern_VerifyTimePatterns() + { + foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) + { + var pattern = culture.DateTimeFormat.LongTimePattern; + var segments = pattern.Split('\''); + bool use12Hour = false; + bool useAMPM = false; + for (var i = 0; i < segments.Length; i += 2) + { + var segment = segments[i]; + use12Hour |= segment.Contains('h', StringComparison.Ordinal); + useAMPM |= segment.Contains('t', StringComparison.Ordinal); + } + Assert.True(!use12Hour || useAMPM, $"Bad long time pattern for culture {culture.Name}: '{pattern}'"); + } + } + [Fact] public void LongTimePattern_CheckTimeFormatWithSpaces() { 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 5e54139c88fc06..b58cfa6d26f3bb 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,6 +255,25 @@ public void ShortTimePattern_SetReadOnly_ThrowsInvalidOperationException() Assert.Throws(() => DateTimeFormatInfo.InvariantInfo.ShortTimePattern = "HH:mm"); } + [Fact] + public void ShortTimePattern_VerifyTimePatterns() + { + foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) + { + var pattern = culture.DateTimeFormat.ShortTimePattern; + var segments = pattern.Split('\''); + bool use12Hour = false; + bool useAMPM = false; + for (var i = 0; i < segments.Length; i += 2) + { + var segment = segments[i]; + use12Hour |= segment.Contains('h', StringComparison.Ordinal); + useAMPM |= segment.Contains('t', StringComparison.Ordinal); + } + Assert.True(!use12Hour || useAMPM, $"Bad short time pattern for culture {culture.Name}: '{pattern}'"); + } + } + [Fact] public void ShortTimePattern_CheckTimeFormatWithSpaces() { From df099b960cd11710c8b775ac565e87980d2abcba Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 01:23:59 +0800 Subject: [PATCH 04/13] Fix literal single quote and literal backslash conversion --- .../System/Globalization/CultureData.Icu.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 848fe222731e32..ff9631e4eab641 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -364,7 +364,12 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr char current = icuFormatString[i]; switch (current) { + case '\\': + result[resultPos++] = '\\'; + result[resultPos++] = '\\'; + break; case '\'': + int i0 = i; result[resultPos++] = icuFormatString[i++]; while (i < icuFormatString.Length) { @@ -372,7 +377,24 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr result[resultPos++] = current; if (current == '\'') { - break; + if (i - i0 <= 1) + { + // Two adjacent single vertical quotes ('') represents a literal single quote, outside quoted text. + result[resultPos - 2] = '\\'; + break; + } + if (i + 1 < icuFormatString.Length && icuFormatString[i + 1] == '\'') + { + // Two adjacent single vertical quotes ('') represents a literal single quote, inside quoted text. + result[resultPos - 1] = '\\'; + result[resultPos++] = '\''; + i++; + } + else break; + } + else if (current == '\\') + { + result[resultPos++] = '\\'; } i++; } From b6a651e9f582138f88b32aeb6f03b89dcc2b60ec Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 13:01:57 +0800 Subject: [PATCH 05/13] Refactor the literal quote conversion logic --- .../System/Globalization/CultureData.Icu.cs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index ff9631e4eab641..2ed6b0511c983d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -369,34 +369,37 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr result[resultPos++] = '\\'; break; case '\'': - int i0 = i; - result[resultPos++] = icuFormatString[i++]; - while (i < icuFormatString.Length) + static bool HandleQuoteLiteral(ReadOnlySpan icuFormatString, ref int i, Span result, ref int resultPos) + { + if (i + 1 < icuFormatString.Length && icuFormatString[i + 1] == '\'') + { + result[resultPos++] = '\\'; + result[resultPos++] = '\''; + i++; + return true; + } + return false; + } + + if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos)) break; + result[resultPos++] = '\''; + for (i++; i < icuFormatString.Length; i++) { current = icuFormatString[i]; - result[resultPos++] = current; if (current == '\'') { - if (i - i0 <= 1) + if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos)) { - // Two adjacent single vertical quotes ('') represents a literal single quote, outside quoted text. - result[resultPos - 2] = '\\'; - break; + continue; } - if (i + 1 < icuFormatString.Length && icuFormatString[i + 1] == '\'') - { - // Two adjacent single vertical quotes ('') represents a literal single quote, inside quoted text. - result[resultPos - 1] = '\\'; - result[resultPos++] = '\''; - i++; - } - else break; + result[resultPos++] = '\''; + break; } - else if (current == '\\') + if (current == '\\') { result[resultPos++] = '\\'; } - i++; + result[resultPos++] = current; } break; From 26b446285f206ad130337de881ffdce8ec90ce48 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 14:37:35 +0800 Subject: [PATCH 06/13] Revise the test logic to ignore literal texts and check pattern redundancy Modify the test logic so that it recognizes literal texts correctly, and fails if 12-hour and 24-hour clocks are used at the same time. --- .../DateTimeFormatInfoLongTimePattern.cs | 24 ++++++++++++++----- .../DateTimeFormatInfoShortTimePattern.cs | 24 ++++++++++++++----- 2 files changed, 36 insertions(+), 12 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 3ea6acc6828be2..e49d13cc73bc12 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 @@ -290,16 +290,28 @@ public void LongTimePattern_VerifyTimePatterns() foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) { var pattern = culture.DateTimeFormat.LongTimePattern; - var segments = pattern.Split('\''); + bool use24Hour = false; bool use12Hour = false; bool useAMPM = false; - for (var i = 0; i < segments.Length; i += 2) + for (var i = 0; i < pattern.Length; i++) { - var segment = segments[i]; - use12Hour |= segment.Contains('h', StringComparison.Ordinal); - useAMPM |= segment.Contains('t', StringComparison.Ordinal); + switch (pattern[i]) + { + case 'H': use24Hour = true; break; + case 'h': use12Hour = true; break; + case 't': useAMPM = true; break; + case '\\': i++; break; + case '\'': + for (i++; i < pattern.Length; i++) + { + var c = pattern[i]; + if (c == '\'') break; + if (c == '\\') i++; + } + break; + } } - Assert.True(!use12Hour || useAMPM, $"Bad long time pattern for culture {culture.Name}: '{pattern}'"); + Assert.True((use24Hour || useAMPM) && (use12Hour ^ use24Hour), $"Bad long time pattern for culture {culture.Name}: '{pattern}'"); } } 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 b58cfa6d26f3bb..4685181eb6a391 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 @@ -261,16 +261,28 @@ public void ShortTimePattern_VerifyTimePatterns() foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) { var pattern = culture.DateTimeFormat.ShortTimePattern; - var segments = pattern.Split('\''); + bool use24Hour = false; bool use12Hour = false; bool useAMPM = false; - for (var i = 0; i < segments.Length; i += 2) + for (var i = 0; i < pattern.Length; i++) { - var segment = segments[i]; - use12Hour |= segment.Contains('h', StringComparison.Ordinal); - useAMPM |= segment.Contains('t', StringComparison.Ordinal); + switch (pattern[i]) + { + case 'H': use24Hour = true; break; + case 'h': use12Hour = true; break; + case 't': useAMPM = true; break; + case '\\': i++; break; + case '\'': + for (i++; i < pattern.Length; i++) + { + var c = pattern[i]; + if (c == '\'') break; + if (c == '\\') i++; + } + break; + } } - Assert.True(!use12Hour || useAMPM, $"Bad short time pattern for culture {culture.Name}: '{pattern}'"); + Assert.True((use24Hour || useAMPM) && (use12Hour ^ use24Hour), $"Bad short time pattern for culture {culture.Name}: '{pattern}'"); } } From 8f1ccf7aec380e298d98d9394b87222e506d22ba Mon Sep 17 00:00:00 2001 From: PopSlime Date: Thu, 20 Jun 2024 14:46:52 +0800 Subject: [PATCH 07/13] Revise the test logic to ensure all cultures are tested --- .../DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs | 5 ++--- .../DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs | 5 ++--- 2 files changed, 4 insertions(+), 6 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 e49d13cc73bc12..4023a10b5f91a1 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 @@ -287,8 +287,7 @@ public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU() [Fact] public void LongTimePattern_VerifyTimePatterns() { - foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) - { + Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { var pattern = culture.DateTimeFormat.LongTimePattern; bool use24Hour = false; bool use12Hour = false; @@ -312,7 +311,7 @@ public void LongTimePattern_VerifyTimePatterns() } } Assert.True((use24Hour || useAMPM) && (use12Hour ^ use24Hour), $"Bad long time pattern for culture {culture.Name}: '{pattern}'"); - } + }); } [Fact] 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 4685181eb6a391..bde1a99e90e427 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 @@ -258,8 +258,7 @@ public void ShortTimePattern_SetReadOnly_ThrowsInvalidOperationException() [Fact] public void ShortTimePattern_VerifyTimePatterns() { - foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures)) - { + Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { var pattern = culture.DateTimeFormat.ShortTimePattern; bool use24Hour = false; bool use12Hour = false; @@ -283,7 +282,7 @@ public void ShortTimePattern_VerifyTimePatterns() } } Assert.True((use24Hour || useAMPM) && (use12Hour ^ use24Hour), $"Bad short time pattern for culture {culture.Name}: '{pattern}'"); - } + }); } [Fact] From 753d2f1c71506176370faac4cfaed5a813dbc621 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 00:04:22 +0800 Subject: [PATCH 08/13] Add comments to clarify the backslash conversion --- .../src/System/Globalization/CultureData.Icu.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 2ed6b0511c983d..9bd9b13ee9d9e7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -365,6 +365,7 @@ private static string ConvertIcuTimeFormatString(ReadOnlySpan icuFormatStr switch (current) { case '\\': + // The ICU format does not use backslashes to escape while the .NET format does result[resultPos++] = '\\'; result[resultPos++] = '\\'; break; @@ -397,6 +398,7 @@ static bool HandleQuoteLiteral(ReadOnlySpan icuFormatString, ref int i, Sp } if (current == '\\') { + // The same backslash escaping mentioned above result[resultPos++] = '\\'; } result[resultPos++] = current; From 347dd36cd419dad9e625e42063365cd226cdde00 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 00:06:18 +0800 Subject: [PATCH 09/13] Refactor the conversion logic Simplify some logic and improve readability. --- .../src/System/Globalization/CultureData.Icu.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 9bd9b13ee9d9e7..75dfdfde929e73 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -379,11 +379,14 @@ static bool HandleQuoteLiteral(ReadOnlySpan icuFormatString, ref int i, Sp i++; return true; } + result[resultPos++] = '\''; return false; } - if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos)) break; - result[resultPos++] = '\''; + if (HandleQuoteLiteral(icuFormatString, ref i, result, ref resultPos)) + { + break; + } for (i++; i < icuFormatString.Length; i++) { current = icuFormatString[i]; @@ -393,7 +396,6 @@ static bool HandleQuoteLiteral(ReadOnlySpan icuFormatString, ref int i, Sp { continue; } - result[resultPos++] = '\''; break; } if (current == '\\') From b908c7b4f1dc163de28a41184ebaa17de95b808b Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 11:37:36 +0800 Subject: [PATCH 10/13] Exclude bad ICU patterns from the tests --- .../DateTimeFormatInfo/DateTimeFormatInfoData.cs | 14 ++++++++++++++ .../DateTimeFormatInfoLongTimePattern.cs | 4 ++++ .../DateTimeFormatInfoShortTimePattern.cs | 4 ++++ 3 files changed, 22 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs index 1a881675aa8d09..f06af08b502d5b 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs @@ -58,5 +58,19 @@ public static Exception GetCultureNotSupportedException(CultureInfo cultureInfo) cultureInfo.Name, cultureInfo.Calendar.GetType().Name)); } + + // These cultures have bad ICU time patterns below the corresponding versions + // They are excluded from the VerifyTimePatterns tests + public static readonly Dictionary _badIcuTimePatterns = new Dictionary() + { + { "mi", new Version(65, 0) }, + { "mi-NZ", new Version(65, 0) }, + }; + public static bool HasBadIcuTimePatterns(CultureInfo culture) + { + return PlatformDetection.IsIcuGlobalizationAndNotHybridOnBrowser + && _badIcuTimePatterns.TryGetValue(culture.Name, out var version) + && PlatformDetection.ICUVersion < version; + } } } 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 4023a10b5f91a1..b7741500a902d6 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 @@ -288,6 +288,10 @@ public void LongTimePattern_CheckReadingTimeFormatWithSingleQuotes_ICU() public void LongTimePattern_VerifyTimePatterns() { Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { + if (DateTimeFormatInfoData.HasBadIcuTimePatterns(culture)) + { + return; + } var pattern = culture.DateTimeFormat.LongTimePattern; bool use24Hour = false; bool use12Hour = false; 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 bde1a99e90e427..d69856218acba2 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 @@ -259,6 +259,10 @@ public void ShortTimePattern_SetReadOnly_ThrowsInvalidOperationException() public void ShortTimePattern_VerifyTimePatterns() { Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { + if (DateTimeFormatInfoData.HasBadIcuTimePatterns(culture)) + { + return; + } var pattern = culture.DateTimeFormat.ShortTimePattern; bool use24Hour = false; bool use12Hour = false; From 7d95dbb0773f0c7450494e4a313474155ef5a28c Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 11:41:49 +0800 Subject: [PATCH 11/13] Exclude the VerifyTimePatterns tests from hybrid globalization on browser --- .../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 b7741500a902d6..8d162c5fbe2e24 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() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] 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 d69856218acba2..34f58fdb16ae2d 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"); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] public void ShortTimePattern_VerifyTimePatterns() { Assert.All(CultureInfo.GetCultures(CultureTypes.AllCultures), culture => { From 3446a100bd32554444fbd968067ad53ec268cd51 Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 12:07:48 +0800 Subject: [PATCH 12/13] Add missing usings --- .../DateTimeFormatInfo/DateTimeFormatInfoData.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs index f06af08b502d5b..253d610184750e 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/DateTimeFormatInfo/DateTimeFormatInfoData.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Runtime.InteropServices; using Xunit; From e205dbcec057a8e57f5f8682adbd4d88c4027a1d Mon Sep 17 00:00:00 2001 From: PopSlime Date: Fri, 21 Jun 2024 15:35:54 +0800 Subject: [PATCH 13/13] Improve readability of the for-loops --- .../src/System/Globalization/CultureData.Icu.cs | 3 ++- .../DateTimeFormatInfo/DateTimeFormatInfoLongTimePattern.cs | 3 ++- .../DateTimeFormatInfo/DateTimeFormatInfoShortTimePattern.cs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index 75dfdfde929e73..cb38cba43e3b6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -387,7 +387,8 @@ static bool HandleQuoteLiteral(ReadOnlySpan icuFormatString, ref int i, Sp { break; } - for (i++; i < icuFormatString.Length; i++) + i++; + for (; i < icuFormatString.Length; i++) { current = icuFormatString[i]; if (current == '\'') 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 8d162c5fbe2e24..6cab9ab6097728 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 @@ -305,7 +305,8 @@ public void LongTimePattern_VerifyTimePatterns() case 't': useAMPM = true; break; case '\\': i++; break; case '\'': - for (i++; i < pattern.Length; i++) + i++; + for (; i < pattern.Length; i++) { var c = pattern[i]; if (c == '\'') break; 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 34f58fdb16ae2d..31cd998fd18b75 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 @@ -276,7 +276,8 @@ public void ShortTimePattern_VerifyTimePatterns() case 't': useAMPM = true; break; case '\\': i++; break; case '\'': - for (i++; i < pattern.Length; i++) + i++; + for (; i < pattern.Length; i++) { var c = pattern[i]; if (c == '\'') break;