From e00c07001452e732bf597499ebc03c6eb52acca0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:21:39 +0000
Subject: [PATCH 1/9] Initial plan
From ffcbf22e1375d65f999dc5dd113623bfaf3d8403 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:28:20 +0000
Subject: [PATCH 2/9] Fix DateOnly and TimeOnly TryParse methods to throw for
invalid DateTimeStyles
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../src/System/DateOnly.cs | 44 +++++++++++++++---
.../src/System/TimeOnly.cs | 45 ++++++++++++++++---
.../System/DateOnlyTests.cs | 8 ++--
.../System/TimeOnlyTests.cs | 8 ++--
4 files changed, 86 insertions(+), 19 deletions(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index f24aa06296c2a4..066f1ff8878408 100644
--- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
@@ -433,7 +433,19 @@ public static DateOnly ParseExact(string s, [StringSyntax(StringSyntaxAttribute.
/// A bitwise combination of enumeration values that indicates the permitted format of s. A typical value to specify is None.
/// When this method returns, contains the DateOnly value equivalent to the date contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s parameter is empty string, or does not contain a valid string representation of a date. This parameter is passed uninitialized.
/// true if the s parameter was converted successfully; otherwise, false.
- public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result) => TryParseInternal(s, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ {
+ ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
@@ -482,8 +494,19 @@ private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatPr
/// A bitwise combination of one or more enumeration values that indicate the permitted format of s.
/// When this method returns, contains the DateOnly value equivalent to the date contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s is empty string, or does not contain a date that correspond to the pattern specified in format. This parameter is passed uninitialized.
/// true if s was converted successfully; otherwise, false.
- public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result) =>
- TryParseExactInternal(s, format, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ {
+ ParseFailureKind failureKind = TryParseExactInternal(s, format, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
@@ -546,8 +569,19 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
/// A bitwise combination of enumeration values that defines how to interpret the parsed date. A typical value to specify is None.
/// When this method returns, contains the DateOnly value equivalent to the date contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s parameter is Empty, or does not contain a valid string representation of a date. This parameter is passed uninitialized.
/// true if the s parameter was converted successfully; otherwise, false.
- public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result) =>
- TryParseExactInternal(s, formats, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ {
+ ParseFailureKind failureKind = TryParseExactInternal(s, formats, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 72e15d2faada6a..7fc6d2e02861c8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
@@ -602,8 +602,19 @@ public static TimeOnly ParseExact(string s, [StringSyntax(StringSyntaxAttribute.
/// When this method returns, contains the TimeOnly value equivalent to the time contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s parameter is empty string, or does not contain a valid string representation of a date. This parameter is passed uninitialized.
/// true if the s parameter was converted successfully; otherwise, false.
///
- public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result) =>
- TryParseInternal(s, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ {
+ ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
@@ -652,8 +663,19 @@ private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatPr
/// A bitwise combination of one or more enumeration values that indicate the permitted format of s.
/// When this method returns, contains the TimeOnly value equivalent to the time contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s is empty string, or does not contain a time that correspond to the pattern specified in format. This parameter is passed uninitialized.
/// true if s was converted successfully; otherwise, false.
- public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result) =>
- TryParseExactInternal(s, format, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ {
+ ParseFailureKind failureKind = TryParseExactInternal(s, format, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
@@ -716,8 +738,19 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
/// A bitwise combination of enumeration values that defines how to interpret the parsed time. A typical value to specify is None.
/// When this method returns, contains the TimeOnly value equivalent to the time contained in s, if the conversion succeeded, or MinValue if the conversion failed. The conversion fails if the s parameter is Empty, or does not contain a valid string representation of a time. This parameter is passed uninitialized.
/// true if the s parameter was converted successfully; otherwise, false.
- public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result) =>
- TryParseExactInternal(s, formats, provider, style, out result) == ParseFailureKind.None;
+ public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ {
+ ParseFailureKind failureKind = TryParseExactInternal(s, formats, provider, style, out result);
+ if (failureKind != ParseFailureKind.None)
+ {
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ ThrowOnError(failureKind, s);
+ }
+ return false;
+ }
+ return true;
+ }
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
index d32f4c391fc8e9..484162e892f236 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
@@ -349,13 +349,13 @@ public static void BasicFormatParseTest()
Assert.Equal(dateOnly, parsedDateOnly);
Assert.Equal(dateOnly, parsedDateOnly1);
- Assert.False(DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedDateOnly1));
+ Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedDateOnly1));
Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal));
- Assert.False(DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedDateOnly1));
+ Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedDateOnly1));
Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal));
- Assert.False(DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedDateOnly1));
+ Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedDateOnly1));
Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
- Assert.False(DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedDateOnly1));
+ Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedDateOnly1));
Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault));
s = " " + s + " ";
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
index 75f14b5ab4f6bd..f2ab676c11967b 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
@@ -388,13 +388,13 @@ public static void BasicFormatParseTest()
Assert.True(TimeOnly.TryParse(s.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.None, out parsedTimeOnly1));
Assert.Equal(parsedTimeOnly, parsedTimeOnly1);
- Assert.False(TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedTimeOnly1));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedTimeOnly1));
AssertExtensions.Throws("style", () => TimeOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal));
- Assert.False(TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedTimeOnly1));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedTimeOnly1));
AssertExtensions.Throws("style", () => TimeOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal));
- Assert.False(TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedTimeOnly1));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedTimeOnly1));
AssertExtensions.Throws("style", () => TimeOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
- Assert.False(TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedTimeOnly1));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedTimeOnly1));
AssertExtensions.Throws("style", () => TimeOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault));
s = " " + s + " ";
From 91093044aaeec7f1850c693ef3ded6ac7f93cb5d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:29:23 +0000
Subject: [PATCH 3/9] Add comprehensive tests for TryParseExact with invalid
DateTimeStyles
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../System/DateOnlyTests.cs | 34 +++++++++++++++++++
.../System/TimeOnlyTests.cs | 34 +++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
index 484162e892f236..686e142b118179 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
@@ -554,5 +554,39 @@ public static void TryFormatTest()
Assert.Throws(() => $"{dateOnly:hh-ss}");
}
}
+
+ [Fact]
+ public static void TryParseExact_InvalidDateTimeStyles_ThrowsArgumentException()
+ {
+ // Test that TryParseExact throws ArgumentException for invalid DateTimeStyles
+ string validInput = "2064-07-01";
+ string format = "yyyy-MM-dd";
+ DateOnly result;
+
+ // Test ReadOnlySpan overload with single format
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test string overload with single format
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test ReadOnlySpan overload with multiple formats
+ string[] formats = new[] { "yyyy-MM-dd", "MM/dd/yyyy" };
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test string overload with multiple formats
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ }
}
}
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
index f2ab676c11967b..1a2ec14759877d 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
@@ -620,5 +620,39 @@ public static void TryFormatTest()
Assert.Throws(() => $"{timeOnly:dd-yyyy}");
}
}
+
+ [Fact]
+ public static void TryParseExact_InvalidDateTimeStyles_ThrowsArgumentException()
+ {
+ // Test that TryParseExact throws ArgumentException for invalid DateTimeStyles
+ string validInput = "14:30:00";
+ string format = "HH:mm:ss";
+ TimeOnly result;
+
+ // Test ReadOnlySpan overload with single format
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test string overload with single format
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test ReadOnlySpan overload with multiple formats
+ string[] formats = new[] { "HH:mm:ss", "hh:mm:ss tt" };
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+
+ // Test string overload with multiple formats
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ }
}
}
From 8bf2e126ce054b44f9560713dafbb01ba65606ca Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:56:05 +0000
Subject: [PATCH 4/9] Use AssertExtensions.Throws with style parameter for test
consistency
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../System/DateOnlyTests.cs | 32 +++++++++----------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
index 686e142b118179..850e5ee714c77f 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
@@ -564,29 +564,29 @@ public static void TryParseExact_InvalidDateTimeStyles_ThrowsArgumentException()
DateOnly result;
// Test ReadOnlySpan overload with single format
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), format.AsSpan(), CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
// Test string overload with single format
- Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, format, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
// Test ReadOnlySpan overload with multiple formats
string[] formats = new[] { "yyyy-MM-dd", "MM/dd/yyyy" };
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput.AsSpan(), formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
// Test string overload with multiple formats
- Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
- Assert.Throws(() => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
+ AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
}
}
}
From 67645f9f7185f8ed73d9e3a75f28fb7dea8f6502 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 18:58:47 +0000
Subject: [PATCH 5/9] Fix DateOnly.TryParse tests to use
AssertExtensions.Throws for consistency
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../System.Runtime.Tests/System/DateOnlyTests.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
index 850e5ee714c77f..dc9d4f09a8d4a8 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
@@ -349,14 +349,14 @@ public static void BasicFormatParseTest()
Assert.Equal(dateOnly, parsedDateOnly);
Assert.Equal(dateOnly, parsedDateOnly1);
- Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedDateOnly1));
- Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal));
- Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedDateOnly1));
- Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal));
- Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedDateOnly1));
- Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
- Assert.Throws(() => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedDateOnly1));
- Assert.Throws(() => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault));
+ AssertExtensions.Throws("style", () => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out parsedDateOnly1));
+ AssertExtensions.Throws("style", () => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal));
+ AssertExtensions.Throws("style", () => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out parsedDateOnly1));
+ AssertExtensions.Throws("style", () => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal));
+ AssertExtensions.Throws("style", () => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out parsedDateOnly1));
+ AssertExtensions.Throws("style", () => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
+ AssertExtensions.Throws("style", () => DateOnly.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out parsedDateOnly1));
+ AssertExtensions.Throws("style", () => DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault));
s = " " + s + " ";
parsedDateOnly = DateOnly.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces);
From 9b82b31e0041f6a5827f1c21aed45656b7f1d988 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 22:19:32 +0000
Subject: [PATCH 6/9] Remove unnecessary Argument_BadFormatSpecifier check from
TryParse methods
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
src/libraries/System.Private.CoreLib/src/System/DateOnly.cs | 2 +-
src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index 066f1ff8878408..b2859678d9b8f5 100644
--- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
@@ -438,7 +438,7 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, Dat
ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
if (failureKind != ParseFailureKind.None)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles)
{
ThrowOnError(failureKind, s);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 7fc6d2e02861c8..549b16f1abd5ea 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
@@ -607,7 +607,7 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, Dat
ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
if (failureKind != ParseFailureKind.None)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ if (failureKind == ParseFailureKind.Argument_InvalidDateStyles)
{
ThrowOnError(failureKind, s);
}
From 492c226a999929a4b93adcf6727be2ce2d44f7b5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 22:46:08 +0000
Subject: [PATCH 7/9] Preserve Argument_BadFormatSpecifier from DateTimeParse
and add tests for bad format specifiers
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../src/System/DateOnly.cs | 13 +++++++++++--
.../src/System/TimeOnly.cs | 13 +++++++++++--
.../System/DateOnlyTests.cs | 18 ++++++++++++++++++
.../System/TimeOnlyTests.cs | 18 ++++++++++++++++++
4 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index b2859678d9b8f5..433353248cd60e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
@@ -537,7 +537,9 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
if (!DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, ref dtResult))
{
result = default;
- return ParseFailureKind.Format_BadDateOnly;
+ return dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier
+ ? ParseFailureKind.Argument_BadFormatSpecifier
+ : ParseFailureKind.Format_BadDateOnly;
}
if ((dtResult.flags & ParseFlagsDateMask) != 0)
@@ -592,6 +594,7 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
}
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+ ParseFailureKind lastFailure = ParseFailureKind.Format_BadDateOnly;
for (int i = 0; i < formats.Length; i++)
{
@@ -628,10 +631,16 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
result = new DateOnly(DayNumberFromDateTime(dtResult.parsedDate));
return ParseFailureKind.None;
}
+
+ // Preserve Argument_BadFormatSpecifier if encountered
+ if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ lastFailure = ParseFailureKind.Argument_BadFormatSpecifier;
+ }
}
result = default;
- return ParseFailureKind.Format_BadDateOnly;
+ return lastFailure;
}
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 549b16f1abd5ea..204dd88181a5a3 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
@@ -707,7 +707,9 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
if (!DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, ref dtResult))
{
result = default;
- return ParseFailureKind.Format_BadTimeOnly;
+ return dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier
+ ? ParseFailureKind.Argument_BadFormatSpecifier
+ : ParseFailureKind.Format_BadTimeOnly;
}
if ((dtResult.flags & ParseFlagsTimeMask) != 0)
@@ -761,6 +763,7 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
}
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+ ParseFailureKind lastFailure = ParseFailureKind.Format_BadTimeOnly;
for (int i = 0; i < formats.Length; i++)
{
@@ -797,10 +800,16 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
result = FromDateTime(dtResult.parsedDate);
return ParseFailureKind.None;
}
+
+ // Preserve Argument_BadFormatSpecifier if encountered
+ if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ lastFailure = ParseFailureKind.Argument_BadFormatSpecifier;
+ }
}
result = default;
- return ParseFailureKind.Format_BadTimeOnly;
+ return lastFailure;
}
///
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
index dc9d4f09a8d4a8..4a52dd2bbc3869 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DateOnlyTests.cs
@@ -588,5 +588,23 @@ public static void TryParseExact_InvalidDateTimeStyles_ThrowsArgumentException()
AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
AssertExtensions.Throws("style", () => DateOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
}
+
+ [Fact]
+ public static void TryParseExact_InvalidFormatSpecifier_ThrowsFormatException()
+ {
+ // Test that TryParseExact throws FormatException for null/empty format specifiers
+ string validInput = "2064-07-01";
+ DateOnly result;
+
+ // Test with array containing null format as first element
+ string?[] formatsWithNull = new string?[] { null, "yyyy-MM-dd", "MM/dd/yyyy" };
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formatsWithNull, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formatsWithNull, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+
+ // Test with array containing empty format as first element
+ string[] formatsWithEmpty = new[] { "", "yyyy-MM-dd", "MM/dd/yyyy" };
+ Assert.Throws(() => DateOnly.TryParseExact(validInput.AsSpan(), formatsWithEmpty, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ Assert.Throws(() => DateOnly.TryParseExact(validInput, formatsWithEmpty, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ }
}
}
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
index 1a2ec14759877d..d98c055b54eef4 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/TimeOnlyTests.cs
@@ -654,5 +654,23 @@ public static void TryParseExact_InvalidDateTimeStyles_ThrowsArgumentException()
AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result));
AssertExtensions.Throws("style", () => TimeOnly.TryParseExact(validInput, formats, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result));
}
+
+ [Fact]
+ public static void TryParseExact_InvalidFormatSpecifier_ThrowsFormatException()
+ {
+ // Test that TryParseExact throws FormatException for null/empty format specifiers
+ string validInput = "14:30:00";
+ TimeOnly result;
+
+ // Test with array containing null format as first element
+ string?[] formatsWithNull = new string?[] { null, "HH:mm:ss", "hh:mm:ss tt" };
+ Assert.Throws(() => TimeOnly.TryParseExact(validInput.AsSpan(), formatsWithNull, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ Assert.Throws(() => TimeOnly.TryParseExact(validInput, formatsWithNull, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+
+ // Test with array containing empty format as first element
+ string[] formatsWithEmpty = new[] { "", "HH:mm:ss", "hh:mm:ss tt" };
+ Assert.Throws(() => TimeOnly.TryParseExact(validInput.AsSpan(), formatsWithEmpty, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ Assert.Throws(() => TimeOnly.TryParseExact(validInput, formatsWithEmpty, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
+ }
}
}
From 57a5c63e47cf6684fba8971f6700282b6e150c19 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 23:40:39 +0000
Subject: [PATCH 8/9] Return immediately when Argument_BadFormatSpecifier is
encountered
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../System.Private.CoreLib/src/System/DateOnly.cs | 7 +++----
.../System.Private.CoreLib/src/System/TimeOnly.cs | 7 +++----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index 433353248cd60e..e863e8a00f6e1b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
@@ -594,7 +594,6 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
}
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
- ParseFailureKind lastFailure = ParseFailureKind.Format_BadDateOnly;
for (int i = 0; i < formats.Length; i++)
{
@@ -632,15 +631,15 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
return ParseFailureKind.None;
}
- // Preserve Argument_BadFormatSpecifier if encountered
if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
{
- lastFailure = ParseFailureKind.Argument_BadFormatSpecifier;
+ result = default;
+ return ParseFailureKind.Argument_BadFormatSpecifier;
}
}
result = default;
- return lastFailure;
+ return ParseFailureKind.Format_BadDateOnly;
}
///
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 204dd88181a5a3..5714ebc15d33c2 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
@@ -763,7 +763,6 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
}
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
- ParseFailureKind lastFailure = ParseFailureKind.Format_BadTimeOnly;
for (int i = 0; i < formats.Length; i++)
{
@@ -801,15 +800,15 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
return ParseFailureKind.None;
}
- // Preserve Argument_BadFormatSpecifier if encountered
if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
{
- lastFailure = ParseFailureKind.Argument_BadFormatSpecifier;
+ result = default;
+ return ParseFailureKind.Argument_BadFormatSpecifier;
}
}
result = default;
- return lastFailure;
+ return ParseFailureKind.Format_BadTimeOnly;
}
///
From 440212b98cc761bad6050a0f9bd979b346a0a110 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 17:15:37 +0000
Subject: [PATCH 9/9] Validate styles and formats upfront in public methods for
consistency
Co-authored-by: tarekgh <10833894+tarekgh@users.noreply.github.com>
---
.../src/System/DateOnly.cs | 100 +++++++++++-------
.../src/System/TimeOnly.cs | 100 +++++++++++-------
2 files changed, 118 insertions(+), 82 deletions(-)
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index e863e8a00f6e1b..1a38a7619b2b73 100644
--- a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
@@ -286,6 +286,11 @@ public int CompareTo(object? value)
/// An object that is equivalent to the date contained in s, as specified by provider and styles.
public static DateOnly Parse(ReadOnlySpan s, IFormatProvider? provider = default, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind result = TryParseInternal(s, provider, style, out DateOnly dateOnly);
if (result != ParseFailureKind.None)
{
@@ -309,6 +314,11 @@ public static DateOnly Parse(ReadOnlySpan s, IFormatProvider? provider = d
/// An object that is equivalent to the date contained in s, as specified by format, provider, and style.
public static DateOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider = default, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind result = TryParseExactInternal(s, format, provider, style, out DateOnly dateOnly);
if (result != ParseFailureKind.None)
@@ -339,6 +349,25 @@ public static DateOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSynt
/// An object that is equivalent to the date contained in s, as specified by format, provider, and style.
public static DateOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] string[] formats, IFormatProvider? provider, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ if (formats == null)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ // Validate formats array for null/empty entries upfront
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (string.IsNullOrEmpty(formats[i]))
+ {
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
+ }
+ }
+
ParseFailureKind result = TryParseExactInternal(s, formats, provider, style, out DateOnly dateOnly);
if (result != ParseFailureKind.None)
{
@@ -435,26 +464,17 @@ public static DateOnly ParseExact(string s, [StringSyntax(StringSyntaxAttribute.
/// true if the s parameter was converted successfully; otherwise, false.
public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
- if (failureKind != ParseFailureKind.None)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles)
- {
- ThrowOnError(failureKind, s);
- }
- return false;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
- return true;
+
+ ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
+ return failureKind == ParseFailureKind.None;
}
private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
DateTimeResult dtResult = default;
dtResult.Init(s);
@@ -496,12 +516,17 @@ private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatPr
/// true if s was converted successfully; otherwise, false.
public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind failureKind = TryParseExactInternal(s, format, provider, style, out result);
if (failureKind != ParseFailureKind.None)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ if (failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
{
- ThrowOnError(failureKind, s);
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
return false;
}
@@ -509,12 +534,6 @@ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSynta
}
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
if (format.Length == 1)
{
switch (format[0] | 0x20)
@@ -573,39 +592,38 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
/// true if the s parameter was converted successfully; otherwise, false.
public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- ParseFailureKind failureKind = TryParseExactInternal(s, formats, provider, style, out result);
- if (failureKind != ParseFailureKind.None)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ if (formats == null)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ // Validate formats array for null/empty entries upfront
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (string.IsNullOrEmpty(formats[i]))
{
- ThrowOnError(failureKind, s);
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
- return false;
}
- return true;
+
+ return TryParseExactInternal(s, formats, provider, style, out result) == ParseFailureKind.None;
}
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0 || formats == null)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
- for (int i = 0; i < formats.Length; i++)
+ for (int i = 0; i < formats!.Length; i++)
{
DateTimeFormatInfo dtfiToUse = dtfi;
string? format = formats[i];
- if (string.IsNullOrEmpty(format))
- {
- result = default;
- return ParseFailureKind.Argument_BadFormatSpecifier;
- }
- if (format.Length == 1)
+ if (format!.Length == 1)
{
switch (format[0] | 0x20)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 5714ebc15d33c2..329270849f3149 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
@@ -455,6 +455,11 @@ public override int GetHashCode()
///
public static TimeOnly Parse(ReadOnlySpan s, IFormatProvider? provider = default, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind result = TryParseInternal(s, provider, style, out TimeOnly timeOnly);
if (result != ParseFailureKind.None)
{
@@ -478,6 +483,11 @@ public static TimeOnly Parse(ReadOnlySpan s, IFormatProvider? provider = d
/// An object that is equivalent to the time contained in s, as specified by format, provider, and style.
public static TimeOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider = default, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind result = TryParseExactInternal(s, format, provider, style, out TimeOnly timeOnly);
if (result != ParseFailureKind.None)
{
@@ -507,6 +517,25 @@ public static TimeOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSynt
/// An object that is equivalent to the time contained in s, as specified by format, provider, and style.
public static TimeOnly ParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] string[] formats, IFormatProvider? provider, DateTimeStyles style = DateTimeStyles.None)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ if (formats == null)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ // Validate formats array for null/empty entries upfront
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (string.IsNullOrEmpty(formats[i]))
+ {
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
+ }
+ }
+
ParseFailureKind result = TryParseExactInternal(s, formats, provider, style, out TimeOnly timeOnly);
if (result != ParseFailureKind.None)
{
@@ -604,25 +633,16 @@ public static TimeOnly ParseExact(string s, [StringSyntax(StringSyntaxAttribute.
///
public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
- if (failureKind != ParseFailureKind.None)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles)
- {
- ThrowOnError(failureKind, s);
- }
- return false;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
- return true;
+
+ ParseFailureKind failureKind = TryParseInternal(s, provider, style, out result);
+ return failureKind == ParseFailureKind.None;
}
private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
DateTimeResult dtResult = default;
dtResult.Init(s);
@@ -665,12 +685,17 @@ private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatPr
/// true if s was converted successfully; otherwise, false.
public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
ParseFailureKind failureKind = TryParseExactInternal(s, format, provider, style, out result);
if (failureKind != ParseFailureKind.None)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ if (failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
{
- ThrowOnError(failureKind, s);
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
return false;
}
@@ -679,12 +704,6 @@ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSynta
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
if (format.Length == 1)
{
switch (format[0] | 0x20)
@@ -742,39 +761,38 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, Read
/// true if the s parameter was converted successfully; otherwise, false.
public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- ParseFailureKind failureKind = TryParseExactInternal(s, formats, provider, style, out result);
- if (failureKind != ParseFailureKind.None)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ if (formats == null)
{
- if (failureKind == ParseFailureKind.Argument_InvalidDateStyles || failureKind == ParseFailureKind.Argument_BadFormatSpecifier)
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
+ }
+
+ // Validate formats array for null/empty entries upfront
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (string.IsNullOrEmpty(formats[i]))
{
- ThrowOnError(failureKind, s);
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
- return false;
}
- return true;
+
+ return TryParseExactInternal(s, formats, provider, style, out result) == ParseFailureKind.None;
}
private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0 || formats == null)
- {
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
- }
-
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
- for (int i = 0; i < formats.Length; i++)
+ for (int i = 0; i < formats!.Length; i++)
{
DateTimeFormatInfo dtfiToUse = dtfi;
string? format = formats[i];
- if (string.IsNullOrEmpty(format))
- {
- result = default;
- return ParseFailureKind.Argument_BadFormatSpecifier;
- }
- if (format.Length == 1)
+ if (format!.Length == 1)
{
switch (format[0] | 0x20)
{