From 2e50919a82e64a4325f22370833c5dc9dc543048 Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Mon, 19 Jun 2023 12:45:18 +0400 Subject: [PATCH 1/2] refactor(#51740): Extracting some constants. --- .../src/System/Globalization/DateTimeParse.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 9fe6ff109653c..d578500751ba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -13,6 +13,9 @@ internal static class DateTimeParse { internal const int MaxDateTimeNumberDigits = 8; + internal const char TimeDelimiter = ':'; + internal const char TimeFractionDelimiterDot = '.'; + internal static DateTime ParseExact(ReadOnlySpan s, ReadOnlySpan format, DateTimeFormatInfo dtfi, DateTimeStyles style) { DateTimeResult result = default; // The buffer to store the parsing result. @@ -521,7 +524,7 @@ private static bool ParseTimeZone(ref __DTString str, scoped ref TimeSpan result str.ConsumeSubString(sub); // See if we have minutes sub = str.GetSubString(); - if (sub.length == 1 && sub[0] == ':') + if (sub.length == 1 && sub[0] == TimeDelimiter) { // Parsing "+8:00" or "+08:00" str.ConsumeSubString(sub); @@ -642,7 +645,7 @@ private static bool Lex(DS dps, ref __DTString str, scoped ref DateTimeToken dto if (str.Index < str.Length - 1) { char nextCh = str.Value[str.Index]; - if (nextCh == '.') + if (nextCh == TimeFractionDelimiterDot) { // While ParseFraction can fail, it just means that there were no digits after // the dot. In this case ParseFraction just removes the dot. This is actually @@ -2961,7 +2964,7 @@ private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString return false; } str.SkipWhiteSpaces(); - if (!str.Match(':')) + if (!str.Match(TimeDelimiter)) { result.SetBadDateTimeFailure(); return false; @@ -2973,7 +2976,7 @@ private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString return false; } str.SkipWhiteSpaces(); - if (str.Match(':')) + if (str.Match(TimeDelimiter)) { str.SkipWhiteSpaces(); if (!ParseDigits(ref str, 2, out second)) @@ -2981,7 +2984,7 @@ private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString result.SetBadDateTimeFailure(); return false; } - if (str.Match('.')) + if (str.Match(TimeFractionDelimiterDot)) { if (!ParseFraction(ref str, out partSecond)) { From dedd9361aaa947e0df49000ad49aaa768ebe9b3b Mon Sep 17 00:00:00 2001 From: Maksim Golev Date: Mon, 19 Jun 2023 15:14:53 +0400 Subject: [PATCH 2/2] fix(#51740): Adding comma as valid time fraction delimiter. --- .../src/System/Globalization/DateTimeParse.cs | 7 +++++-- .../tests/System/DateTimeOffsetTests.cs | 16 ++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index d578500751ba2..8b518113018c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -14,6 +14,7 @@ internal static class DateTimeParse internal const int MaxDateTimeNumberDigits = 8; internal const char TimeDelimiter = ':'; + internal const char TimeFractionDelimiterComma = ','; internal const char TimeFractionDelimiterDot = '.'; internal static DateTime ParseExact(ReadOnlySpan s, ReadOnlySpan format, DateTimeFormatInfo dtfi, DateTimeStyles style) @@ -645,7 +646,8 @@ private static bool Lex(DS dps, ref __DTString str, scoped ref DateTimeToken dto if (str.Index < str.Length - 1) { char nextCh = str.Value[str.Index]; - if (nextCh == TimeFractionDelimiterDot) + if ((nextCh == TimeFractionDelimiterDot) + || (nextCh == TimeFractionDelimiterComma)) { // While ParseFraction can fail, it just means that there were no digits after // the dot. In this case ParseFraction just removes the dot. This is actually @@ -2984,7 +2986,8 @@ private static bool ParseISO8601(scoped ref DateTimeRawInfo raw, ref __DTString result.SetBadDateTimeFailure(); return false; } - if (str.Match(TimeFractionDelimiterDot)) + if ((str.Match(TimeFractionDelimiterDot)) + || (str.Match(TimeFractionDelimiterComma))) { if (!ParseFraction(ref str, out partSecond)) { diff --git a/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs index 2e2ff83a504f9..f0d5dc2120b2e 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs @@ -856,14 +856,18 @@ public static void Compare(DateTimeOffset dateTimeOffset1, DateTimeOffset dateTi } } - [Fact] - public static void Parse_String() + public static IEnumerable Parse_TestData() { - DateTimeOffset expected = DateTimeOffset.MaxValue; - string expectedString = expected.ToString(); + yield return new object[] { "2021-04-23T13:04:17,307642270+02:00", new DateTimeOffset(637547798573076423, new TimeSpan(2, 0, 0)) }; + yield return new object[] { DateTimeOffset.MaxValue.ToString("O"), DateTimeOffset.MaxValue }; + } - DateTimeOffset result = DateTimeOffset.Parse(expectedString); - Assert.Equal(expectedString, result.ToString()); + [Theory] + [MemberData(nameof(Parse_TestData))] + public static void Parse_String(string valueForParse, DateTimeOffset expectedValue) + { + DateTimeOffset actualValue = DateTimeOffset.Parse(valueForParse); + Assert.Equal(expectedValue, actualValue); } [Fact]