Skip to content

Validate the entire formats[] array up front in ParseExact/TryParseExact#123

Merged
OlegRa merged 2 commits into
masterfrom
fix/118-parseexact-formats-array-validation
Jul 11, 2026
Merged

Validate the entire formats[] array up front in ParseExact/TryParseExact#123
OlegRa merged 2 commits into
masterfrom
fix/118-parseexact-formats-array-validation

Conversation

@OlegRa

@OlegRa OlegRa commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #118.

DateOnly/TimeOnly ParseExact and TryParseExact(string/span, string[]) 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 method returned successfully and the bad entry was never inspected. Upstream dotnet/runtime's DateOnly/TimeOnly validate the whole array up front and throw FormatException immediately for any null/empty entry, regardless of position.

Tests

  • Updated two existing ArgumentValidationTests assertions ([], [string.Empty] with AllowWhiteSpaces) that expected false — they now expect FormatException, since a non-null single-entry-bad array is exactly what up-front validation catches.
  • Added the regression case that actually demonstrates the gap: 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 throw FormatException for both ParseExact and TryParseExact, on both DateOnly and TimeOnly.

Test plan

  • dotnet build -c Debug — succeeds, 0 warnings/errors
  • dotnet test — 169/169 passed

@OlegRa OlegRa requested a review from Copilot July 11, 2026 08:50
@OlegRa OlegRa self-assigned this Jul 11, 2026
@OlegRa OlegRa added the bug Something isn't working label Jul 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 in DateOnly and TimeOnly.
  • Updated TryParseExact (formats-array overload) to call the new helper before attempting parsing.
  • Updated/added regression tests to assert FormatException is 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.

Comment thread DateTimeOnly/TimeOnly.cs
Comment thread DateTimeOnly/DateOnly.cs
Comment thread DateTimeOnly/TimeOnly.cs
Comment thread DateTimeOnly/DateOnly.cs
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
@OlegRa OlegRa force-pushed the fix/118-parseexact-formats-array-validation branch from ee54308 to d1d6f10 Compare July 11, 2026 09:17
@OlegRa

OlegRa commented Jul 11, 2026

Copy link
Copy Markdown
Owner Author

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: ThrowOnBadFormatSpecifier(formats) ran unconditionally as the first statement in ParseExact/TryParseExact, before any DateTimeStyles validation. If a caller passed both an invalid style and a malformed formats array at the same time, the method threw FormatException (about the bad format) instead of ArgumentException (about the bad style) — the wrong exception type, since upstream validates style first, then formats == null, then per-entry format validity.

Fix:

  • TryParseExact (both types): now that Throw ArgumentException for invalid DateTimeStyles in TryParse/TryParseExact #122 added an explicit, unconditional style-throw at the top of the method, resolving the rebase conflict by putting that check before ThrowOnBadFormatSpecifier fixes the ordering with no extra logic needed.
  • ParseExact (both types): there's no separate style-throw block here (style validation lives only inside TryParseExactInternal), so ThrowOnBadFormatSpecifier is now gated on (style & ~DateTimeStyles.AllowWhiteSpaces) == 0 — skipped when style is already invalid, letting TryParseExactInternal retain sole ownership of "what happens on invalid style".

Added two regression tests (one per type) that combine an invalid style with a bad formats[] entry and assert ArgumentException (not FormatException) — mirroring the equivalent coverage that already existed for TryParseExact via #122's tests, which is what caught this while resolving the conflict.

Rebuilt and re-ran the full suite: 169/169 passed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread DateTimeOnly.Tests/ArgumentValidationTests.cs
Comment thread DateTimeOnly.Tests/ArgumentValidationTests.cs
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).
@OlegRa OlegRa merged commit 860c18c into master Jul 11, 2026
6 checks passed
@OlegRa OlegRa deleted the fix/118-parseexact-formats-array-validation branch July 11, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ParseExact/TryParseExact(formats[]) should validate the whole array up front

2 participants