Skip to content

Commit

Permalink
Fix invalid datetimeoffset parsing (#87801)
Browse files Browse the repository at this point in the history
Co-authored-by: Maksim Golev <mgolev@htc-cs.ru>
  • Loading branch information
Maximys and Maksim Golev authored Jun 24, 2023
1 parent cfe30c0 commit d2e8963
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ 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<char> s, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, DateTimeStyles style)
{
DateTimeResult result = default; // The buffer to store the parsing result.
Expand Down Expand Up @@ -521,7 +525,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);
Expand Down Expand Up @@ -642,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 == '.')
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
Expand Down Expand Up @@ -2961,7 +2966,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;
Expand All @@ -2973,15 +2978,16 @@ 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))
{
result.SetBadDateTimeFailure();
return false;
}
if (str.Match('.'))
if ((str.Match(TimeFractionDelimiterDot))
|| (str.Match(TimeFractionDelimiterComma)))
{
if (!ParseFraction(ref str, out partSecond))
{
Expand Down
16 changes: 10 additions & 6 deletions src/libraries/System.Runtime/tests/System/DateTimeOffsetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -856,14 +856,18 @@ public static void Compare(DateTimeOffset dateTimeOffset1, DateTimeOffset dateTi
}
}

[Fact]
public static void Parse_String()
public static IEnumerable<object[]> 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]
Expand Down

0 comments on commit d2e8963

Please sign in to comment.