Skip to content

Handle range inclusivity, escaping, and short forms#130

Merged
niemyjski merged 1 commit intomainfrom
es-compat
Feb 16, 2026
Merged

Handle range inclusivity, escaping, and short forms#130
niemyjski merged 1 commit intomainfrom
es-compat

Conversation

@ejsmith
Copy link
Member

@ejsmith ejsmith commented Feb 16, 2026

Enhance date math parsing to support escaped slashes and short-form comparison operators, ensuring consistent behavior for inclusive and exclusive boundaries. Update regex patterns and validation logic to accommodate these changes.

Copy link

Copilot AI left a comment

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 enhances the date math parsing functionality to fully support Elasticsearch-compatible range semantics with proper handling of bracket inclusivity, short-form comparison operators, and escaped slashes. The changes ensure that boundary inclusivity (indicated by bracket type or comparison operator) correctly controls rounding direction in date math expressions.

Changes:

  • Added support for all four bracket combinations ([], [}, {], {}) in range syntax, where bracket type controls whether boundaries round to start or end of period
  • Implemented short-form comparison operators (>, >=, <, <=) as syntactic sugar for one-sided ranges
  • Added support for escaped forward slashes (\/) in date math expressions to accommodate contexts where / must be escaped (e.g., Lucene query syntax)

Reviewed changes

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

Show a summary per file
File Description
TwoPartFormatParser.cs Enhanced to pre-detect closing brackets and set appropriate rounding direction (isUpperLimit) for both start and end expressions based on bracket type; updated validation to allow mixed bracket pairs
ComparisonFormatParser.cs New parser implementing short-form comparison operators with correct inclusivity-based rounding
DateMath.cs Updated regex patterns to support escaped forward slashes (\/) in operations; normalized escaped slashes during processing
TwoPartFormatParserTests.cs Added comprehensive tests for all four bracket combinations with various rounding scenarios; removed tests for previously-invalid mixed brackets that are now valid per Elasticsearch
DateTimeRangeTests.cs Added tests for comparison operators with and without rounding, covering all four operator types
DateMathTests.cs Added tests verifying escaped and unescaped slashes produce identical results
README.md Updated documentation with rounding behavior table, real-world patterns, and examples of the new features

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +360 to +366
/// The parser must set inclusivity correctly so that downstream rounding
/// doesn't need separate handling.
///
/// >value → exclusive lower bound (MinInclusive = false) → isUpperLimit = true
/// >=value → inclusive lower bound (MinInclusive = true) → isUpperLimit = false
/// &lt;value → exclusive upper bound (MaxInclusive = false) → isUpperLimit = false
/// &lt;=value → inclusive upper bound (MaxInclusive = true) → isUpperLimit = true
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The documentation references MinInclusive and MaxInclusive properties that don't exist in the DateTimeRange class. While these are used conceptually to explain the operator semantics, the actual implementation doesn't track inclusivity explicitly - instead, it's encoded in the rounding behavior (isUpperLimit parameter). Consider clarifying that these are conceptual properties used for explanation, not actual class members, or rephrase the documentation to avoid suggesting they are properties of DateTimeRange.

Suggested change
/// The parser must set inclusivity correctly so that downstream rounding
/// doesn't need separate handling.
///
/// >value → exclusive lower bound (MinInclusive = false) → isUpperLimit = true
/// >=value → inclusive lower bound (MinInclusive = true) → isUpperLimit = false
/// &lt;value → exclusive upper bound (MaxInclusive = false) → isUpperLimit = false
/// &lt;=value → inclusive upper bound (MaxInclusive = true) → isUpperLimit = true
/// The parser must interpret the operator's inclusive/exclusive semantics
/// correctly so that downstream rounding (driven by the isUpperLimit flag)
/// produces the expected boundaries.
///
/// >value → exclusive lower bound → treated as an upper-limit rounding case (isUpperLimit = true)
/// >=value → inclusive lower bound → treated as a lower-limit rounding case (isUpperLimit = false)
/// &lt;value → exclusive upper bound → treated as a lower-limit rounding case (isUpperLimit = false)
/// &lt;=value → inclusive upper bound → treated as an upper-limit rounding case (isUpperLimit = true)

Copilot uses AI. Check for mistakes.
README.md Outdated
// [now/d TO now/d] → start of today through end of today (full day, inclusive both ends)
// [now/d TO now/d} → start of today through start of today (exclusive upper)
// {now/d TO now/d] → end of today through end of today (exclusive lower)
// {now/d TO now/d} → end of today through start of today (exclusive both ends)
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The documentation for {now/d TO now/d} is misleading. The comment states "end of today through start of today", but the DateTimeRange constructor automatically swaps start and end if start > end (see DateTimeRange.cs lines 25-31). This means {now/d TO now/d} would actually produce the same result as [now/d TO now/d] (start of today through end of today) rather than an inverted or empty range. Consider either documenting this swapping behavior or using an example with different time periods that doesn't trigger the swap (like the test cases do with now-7d vs now).

Suggested change
// {now/d TO now/d} → end of today through start of today (exclusive both ends)
// {now/d TO now/d} → logically "end of today through start of today" (exclusive both ends), but DateTimeRange normalizes start/end so this evaluates the same as [now/d TO now/d]

Copilot uses AI. Check for mistakes.
/// &lt;=now/d → range from MinValue to end of today
/// </summary>
[Priority(24)]
public class ComparisonFormatParser : IFormatParser
Copy link
Member

Choose a reason for hiding this comment

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

Is this used?

Assert.Equal(baseTime.StartOfDay(), range.End);
}

#region Short-form comparison operators (>, >=, <, <=)
Copy link
Member

Choose a reason for hiding this comment

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

remove regions

AGENTS.md Outdated
# AGENTS.md

You are an expert .NET engineer working on Exceptionless.DateTimeExtensions, a focused utility library providing DateTimeRange, Business Day/Week calculations, Elasticsearch-compatible date math parsing, and extension methods for DateTime, DateTimeOffset, and TimeSpan. Your changes must maintain backward compatibility, correctness across edge cases (especially timezone handling), and parsing reliability. Approach each task methodically: research existing patterns, make surgical changes, and validate thoroughly.
This file provides guidance to WARP (warp.dev) when working with code in this repository.
Copy link
Member

Choose a reason for hiding this comment

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

revert

Copy link

Copilot AI left a comment

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 8 out of 8 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@niemyjski niemyjski left a comment

Choose a reason for hiding this comment

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

Looks good minus the agents file

@niemyjski niemyjski merged commit 3552051 into main Feb 16, 2026
4 checks passed
@niemyjski niemyjski deleted the es-compat branch February 16, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants