diff --git a/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs b/src/libraries/System.Private.CoreLib/src/System/DateOnly.cs
index f24aa06296c2a4..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)
{
@@ -433,16 +462,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;
-
- private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
+ 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)
+ {
DateTimeResult dtResult = default;
dtResult.Init(s);
@@ -482,16 +514,26 @@ 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;
- private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ 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_BadFormatSpecifier)
+ {
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
+ }
+ return false;
+ }
+ return true;
+ }
+ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ {
if (format.Length == 1)
{
switch (format[0] | 0x20)
@@ -514,7 +556,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)
@@ -546,30 +590,40 @@ 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;
-
- private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
+ public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out DateOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0 || formats == null)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
- DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+ 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++)
{
- DateTimeFormatInfo dtfiToUse = dtfi;
- string? format = formats[i];
- if (string.IsNullOrEmpty(format))
+ if (string.IsNullOrEmpty(formats[i]))
{
- result = default;
- return ParseFailureKind.Argument_BadFormatSpecifier;
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
+ }
+
+ 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)
+ {
+ DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+
+ for (int i = 0; i < formats!.Length; i++)
+ {
+ DateTimeFormatInfo dtfiToUse = dtfi;
+ string? format = formats[i];
- if (format.Length == 1)
+ if (format!.Length == 1)
{
switch (format[0] | 0x20)
{
@@ -594,6 +648,12 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
result = new DateOnly(DayNumberFromDateTime(dtResult.parsedDate));
return ParseFailureKind.None;
}
+
+ if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ result = default;
+ return ParseFailureKind.Argument_BadFormatSpecifier;
+ }
}
result = default;
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs b/src/libraries/System.Private.CoreLib/src/System/TimeOnly.cs
index 72e15d2faada6a..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)
{
@@ -602,16 +631,18 @@ 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;
- private static ParseFailureKind TryParseInternal(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
+ 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)
+ {
DateTimeResult dtResult = default;
dtResult.Init(s);
@@ -652,17 +683,27 @@ 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;
-
- private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ public static bool TryParseExact(ReadOnlySpan s, [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ 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_BadFormatSpecifier)
+ {
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
+ }
+ return false;
}
+ return true;
+ }
+ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, ReadOnlySpan format, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ {
if (format.Length == 1)
{
switch (format[0] | 0x20)
@@ -685,7 +726,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)
@@ -716,30 +759,40 @@ 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;
-
- private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
+ public static bool TryParseExact(ReadOnlySpan s, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] string?[]? formats, IFormatProvider? provider, DateTimeStyles style, out TimeOnly result)
{
- if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0 || formats == null)
+ if ((style & ~DateTimeStyles.AllowWhiteSpaces) != 0)
{
- result = default;
- return ParseFailureKind.Argument_InvalidDateStyles;
+ throw new ArgumentException(SR.Argument_InvalidDateStyles, nameof(style));
}
- DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+ 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++)
{
- DateTimeFormatInfo dtfiToUse = dtfi;
- string? format = formats[i];
- if (string.IsNullOrEmpty(format))
+ if (string.IsNullOrEmpty(formats[i]))
{
- result = default;
- return ParseFailureKind.Argument_BadFormatSpecifier;
+ throw new FormatException(SR.Argument_BadFormatSpecifier);
}
+ }
+
+ 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)
+ {
+ DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(provider);
+
+ for (int i = 0; i < formats!.Length; i++)
+ {
+ DateTimeFormatInfo dtfiToUse = dtfi;
+ string? format = formats[i];
- if (format.Length == 1)
+ if (format!.Length == 1)
{
switch (format[0] | 0x20)
{
@@ -764,6 +817,12 @@ private static ParseFailureKind TryParseExactInternal(ReadOnlySpan s, stri
result = FromDateTime(dtResult.parsedDate);
return ParseFailureKind.None;
}
+
+ if (dtResult.failure == ParseFailureKind.Argument_BadFormatSpecifier)
+ {
+ result = default;
+ return ParseFailureKind.Argument_BadFormatSpecifier;
+ }
}
result = default;
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..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
@@ -349,14 +349,14 @@ 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.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal));
- Assert.False(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.Parse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal));
- Assert.False(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);
@@ -554,5 +554,57 @@ 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
+ 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
+ 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" };
+ 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
+ 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));
+ }
+
+ [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 75f14b5ab4f6bd..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
@@ -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 + " ";
@@ -620,5 +620,57 @@ 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));
+ }
+
+ [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));
+ }
}
}