Validate the entire formats[] array up front in ParseExact/TryParseExact#123
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aligns DateOnly/TimeOnly ParseExact and TryParseExact(..., string[] formats, ...) behavior with upstream .NET by validating the entire formats[] array up front, ensuring null/empty entries are detected even when an earlier format would otherwise succeed.
Changes:
- Added an up-front
ThrowOnBadFormatSpecifier(string?[]? formats)validation pass inDateOnlyandTimeOnly. - Updated
TryParseExact(formats-array overload) to call the new helper before attempting parsing. - Updated/added regression tests to assert
FormatExceptionis thrown for null/empty entries anywhere in the array.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| DateTimeOnly/TimeOnly.cs | Adds up-front formats-array validation for ParseExact/TryParseExact (formats overload). |
| DateTimeOnly/DateOnly.cs | Adds up-front formats-array validation for ParseExact/TryParseExact (formats overload). |
| DateTimeOnly.Tests/ArgumentValidationTests.cs | Updates expectations and adds regression tests for trailing bad format entries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
DateOnly/TimeOnly.ParseExact and TryParseExact(string, string[]) overloads validated each formats[] entry lazily inside the parse loop, so a null/empty trailing entry was only detected if the loop actually reached it. If an earlier entry matched the input first, the bad entry was silently never inspected and no exception was thrown - diverging from upstream dotnet/runtime, which validates the whole array up front. Add a ThrowOnBadFormatSpecifier helper that validates every entry before attempting to parse, called from both ParseExact and TryParseExact for each type. Fixes #118
ee54308 to
d1d6f10
Compare
|
Rebased onto master (now includes #122) and addressed Copilot's 4 review comments — they were all valid, describing the same root issue at 4 call sites. The bug: Fix:
Added two regression tests (one per type) that combine an invalid style with a bad Rebuilt and re-ran the full suite: 169/169 passed. |
TimeOnlyValue.ToString("HH:mm:ss") formatted using the current
thread culture, while the immediately following parse used
CultureInfo.InvariantCulture explicitly. ":" in a custom time format
string is the special time-separator token (culture-sensitive, not
a literal), so under a culture whose TimeSeparator isn't ":" the
formatted string and the InvariantCulture parse would disagree for
a reason unrelated to what the test claims to cover, making it
silently not exercise the eager formats-array validation it's meant
to pin down.
Format with CultureInfo.InvariantCulture explicitly, matching the
parse-side culture and the convention already used elsewhere in this
test suite (BasicFormatParseTest).
Summary
Closes #118.
DateOnly/TimeOnlyParseExactandTryParseExact(string/span, string[])validated eachformats[]entry lazily, inside the parse loop. So a null/empty trailing entry was only detected if the loop actually reached it — if an earlier entry matched the input first, the method returned successfully and the bad entry was never inspected. Upstreamdotnet/runtime'sDateOnly/TimeOnlyvalidate the whole array up front and throwFormatExceptionimmediately for any null/empty entry, regardless of position.ThrowOnBadFormatSpecifier(string?[]? formats)helper (mirrors upstream's added validation pass) to both types, called from:ParseExact(ReadOnlySpan<char>, string[], IFormatProvider?, DateTimeStyles)TryParseExact(ReadOnlySpan<char>, string?[]?, IFormatProvider?, DateTimeStyles, out T)formats == nullis left to the existingTryParseExactInternalcheck (unchanged) so the current null-formats behavior —ArgumentExceptionfromParseExact,falsefromTryParseExact— is preserved exactly.string-based overloads delegate into these span-based ones, so they inherit the fix automatically.DateTimeStylesinTry*) is a separate, already-open PR (Throw ArgumentException for invalid DateTimeStyles in TryParse/TryParseExact #122) and isn't touched here; this branch is cut frommaster, not from that branch, so the two can merge independently.Tests
ArgumentValidationTestsassertions ([], [string.Empty]withAllowWhiteSpaces) that expectedfalse— they now expectFormatException, since a non-null single-entry-bad array is exactly what up-front validation catches.formats = ["yyyy-MM-dd", ""]/["HH:mm:ss", ""]against an input the first format would have matched — before this fix these silently returned a parsed value, ignoring the bad trailing entry; now they throwFormatExceptionfor bothParseExactandTryParseExact, on bothDateOnlyandTimeOnly.Test plan
dotnet build -c Debug— succeeds, 0 warnings/errorsdotnet test— 169/169 passed