Conversation
Previously, TwoPartFormatParser always passed isUpperLimit=false for the
start side and isUpperLimit=true for the end side of a range, regardless
of bracket type. This is only correct for inclusive [...] brackets.
Per Elasticsearch conventions, rounding behavior must change based on
whether each boundary is inclusive or exclusive:
- Inclusive min ([): rounds down (start of period) — gte semantics
- Exclusive min ({): rounds up (end of period) — gt semantics
- Inclusive max (]): rounds up (end of period) — lte semantics
- Exclusive max (}): rounds down (start of period) — lt semantics
This means [now/d TO now/d] correctly produces the entire day (start to
end), while {now/d TO now/d} collapses, and mixed brackets like
[now/d TO now/d} produce start-of-day to start-of-day.
Changes:
- Pre-scan closing bracket before parsing parts so isUpperLimit is known
upfront (uses a simple backwards char scan, no extra regex)
- Pass bracket-aware isUpperLimit to part parsers; wildcard parsers
still receive positional isUpperLimit (false=min, true=max) since they
use it for position semantics rather than rounding
- Allow mixed bracket pairs ([..}, {..]) per Elasticsearch Lucene syntax
- Add comprehensive tests for all four bracket combinations with /d, /M,
/h rounding and mixed date math operations
- Document rounding behavior in README with reference table and common
date range patterns
Ref: FoundatioFx/Foundatio.Lucene@a8426ab
Co-authored-by: Cursor <cursoragent@cursor.com>
|
|
src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/TwoPartFormatParser.cs
Fixed
Show fixed
Hide fixed
src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/TwoPartFormatParser.cs
Fixed
Show fixed
Hide fixed
There was a problem hiding this comment.
Pull request overview
Adjusts date range parsing to make date-math rounding bracket-aware (inclusive/exclusive) and align behavior with Elasticsearch range-query conventions, while also allowing mixed bracket pairs.
Changes:
- Updates
TwoPartFormatParserto determine rounding direction based on inclusive ([ ]) vs exclusive ({ }) boundaries, and allows mixed bracket pairs. - Adds/updates tests covering exclusive + mixed bracket combinations and
/d,/M,/hrounding behaviors. - Expands README documentation with a bracket-aware rounding reference section and common patterns.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/Exceptionless.DateTimeExtensions.Tests/FormatParsers/TwoPartFormatParserTests.cs | Adds coverage for exclusive and mixed bracket pairs (and updates wildcard expectations). |
| tests/Exceptionless.DateTimeExtensions.Tests/DateTimeRangeTests.cs | Adds end-to-end parsing tests for bracket-aware rounding across units and mixed brackets. |
| src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/TwoPartFormatParser.cs | Implements bracket-aware rounding direction + mixed bracket acceptance; changes closing-bracket detection. |
| README.md | Documents inclusive/exclusive rounding rules and provides example patterns. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/TwoPartFormatParser.cs
Show resolved
Hide resolved
tests/Exceptionless.DateTimeExtensions.Tests/DateTimeRangeTests.cs
Outdated
Show resolved
Hide resolved
- Simplify boolean ternary expressions per CodeQL: replace `A ? false : B` with `A is not X && B` and `A ? true : B` with `A is X || B` for the wildcard type checks - Collapse inverted ranges from exclusive brackets instead of letting DateTimeRange reorder bounds and unintentionally expand the range - Update test comment to reflect the explicit collapse behavior Co-authored-by: Cursor <cursoragent@cursor.com>
ejsmith
previously approved these changes
Feb 16, 2026
Corrects the logic for determining inclusive and exclusive date ranges based on brackets. Improves the bracket parsing logic to handle null or empty content and bracket characters, ensuring accurate date range calculations. Also fixes the IsValidBracketPair function to improve bracket validation.
randylsu
approved these changes
Feb 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TwoPartFormatParser: Previously, rounding (/d,/M,/h, etc.) always rounded down for the start side and up for the end side regardless of bracket type. Now follows Elasticsearch conventions where inclusive brackets ([,]) maximize the matched range and exclusive brackets ({,}) minimize it.[start TO end}and{start TO end]are now valid, matching Elasticsearch Lucene query syntax. Previously these were rejected as mismatched.isUpperLimitsince they use it for min/max semantics, not rounding.Rounding Rules
[inclusivegte{exclusivegt]inclusivelte}exclusiveltExamples
[now/d TO now/d]— entire current day (start-of-day to end-of-day)[now/d TO now/d}— start-of-day to start-of-day (empty/collapsed){now/d TO now/d]— end-of-day to end-of-day (empty/collapsed)[now-1d/d TO now/d}— start of yesterday to start of today (ltsemantics on end)Ref: FoundatioFx/Foundatio.Lucene@a8426ab
Test plan
TwoPartFormatParserTests: exclusive brackets ({jan TO feb},{2012 TO 2013}), mixed brackets ([2012 TO 2013},{2012 TO 2013]), wildcard with exclusive brackets ({2012 TO *})DateTimeRangeTests: all four bracket combos with/drounding,/Mrounding,/hrounding, mixed brackets with date math operationsdotnet formatpasses with no changes needed