Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ _NCrunch_*
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
**/.idea/**/modules.xml
.idea/.idea.Exceptionless.DateTimeExtensions/.idea/
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cSpell.words": [
"millis",
"timespan"
]
}
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,101 @@ bool isDay = day.IsBusinessDay(date);

### DateTime Ranges

Quickly work with date ranges. . Check out our [unit tests](https://github.com/exceptionless/Exceptionless.DateTimeExtensions/blob/main/tests/Exceptionless.DateTimeExtensions.Tests/DateTimeRangeTests.cs) for more usage samples.
Quickly work with date ranges with support for Elasticsearch-style date math expressions and bracket notation. Check out our [unit tests](https://github.com/exceptionless/Exceptionless.DateTimeExtensions/blob/main/tests/Exceptionless.DateTimeExtensions.Tests/DateTimeRangeTests.cs) for more usage samples.

```csharp
// Basic range parsing
var range = DateTimeRange.Parse("yesterday", DateTime.Now);
if (range.Contains(DateTime.Now.Subtract(TimeSpan.FromHours(6)))) {
//...
}

// Elasticsearch Date Math support with proper timezone handling
var elasticRange = DateTimeRange.Parse("2025-01-01T01:25:35Z||+3d/d", DateTime.Now);
// Supports timezone-aware operations: Z (UTC), +05:00, -08:00

// Bracket notation support [start TO end]
var bracketRange = DateTimeRange.Parse("[2023-01-01 TO 2023-12-31]", DateTime.Now);
Comment on lines +50 to +51
Copy link

Copilot AI Sep 22, 2025

Choose a reason for hiding this comment

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

Documentation shows only square bracket usage but the tests and parser also accept curly braces; the README does not clarify (a) whether {} are supported, or (b) inclusive vs exclusive semantics. Either document that {} are currently treated the same as [] (temporary limitation) or explain exclusivity once implemented to avoid developer confusion.

Suggested change
// Bracket notation support [start TO end]
var bracketRange = DateTimeRange.Parse("[2023-01-01 TO 2023-12-31]", DateTime.Now);
// Bracket notation support [start TO end] or {start TO end}
// Both square brackets [ ] and curly braces { } are accepted for range expressions.
// Currently, both are treated the same (inclusive bounds). Exclusive semantics for { } may be added in the future.
var bracketRange = DateTimeRange.Parse("[2023-01-01 TO 2023-12-31]", DateTime.Now);
var curlyBracketRange = DateTimeRange.Parse("{2023-01-01 TO 2023-12-31}", DateTime.Now); // Currently same as above

Copilot uses AI. Check for mistakes.


// Wildcard support for open-ended ranges
var wildcardRange = DateTimeRange.Parse("[2023-01-01 TO *]", DateTime.Now); // From date to infinity
```

#### Date Math Features

Supports full Elasticsearch date math syntax following [official specifications](https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#date-math):

- **Anchors**: `now`, explicit dates with `||` separator
- **Operations**: `+1d` (add), `-1h` (subtract), `/d` (round down)
- **Units**: `y` (years), `M` (months), `w` (weeks), `d` (days), `h`/`H` (hours), `m` (minutes), `s` (seconds)
- **Timezone Support**: Preserves explicit timezones (`Z`, `+05:00`, `-08:00`) or uses system timezone as fallback

Examples:

- `now+1h` - One hour from now
- `now-1d/d` - Start of yesterday
- `2025-01-01T01:25:35Z||+3d/d` - January 4th, 2025 (start of day) in UTC
- `2023-06-15T14:30:00+05:00||+1M-2d` - One month minus 2 days from the specified date/time in +05:00 timezone

### DateMath Utility

For applications that need standalone date math parsing without the range functionality, the `DateMath` utility class provides direct access to Elasticsearch date math expression parsing. Check out our [unit tests](https://github.com/exceptionless/Exceptionless.DateTimeExtensions/blob/main/tests/Exceptionless.DateTimeExtensions.Tests/DateMathTests.cs) for more usage samples.

```csharp
using Exceptionless.DateTimeExtensions;

// Parse date math expressions with standard .NET conventions
var baseTime = DateTimeOffset.Now;

// Parse method - throws ArgumentException on invalid input
var result = DateMath.Parse("now+1h", baseTime);
var rounded = DateMath.Parse("now-1d/d", baseTime, isUpperLimit: false); // Start of yesterday

// TryParse method - returns bool for success/failure
if (DateMath.TryParse("2023.06.15||+1M/d", baseTime, false, out var parsed)) {
// Successfully parsed: June 15, 2023 + 1 month, rounded to start of day
Console.WriteLine($"Parsed: {parsed:O}");
}

// Upper limit behavior affects rounding
var startOfDay = DateMath.Parse("now/d", baseTime, isUpperLimit: false); // 00:00:00
var endOfDay = DateMath.Parse("now/d", baseTime, isUpperLimit: true); // 23:59:59.999

// Explicit dates with timezone preservation
var utcResult = DateMath.Parse("2025-01-01T01:25:35Z||+3d/d", baseTime);
var offsetResult = DateMath.Parse("2023-06-15T14:30:00+05:00||+1M", baseTime);
```

#### TimeZone-Aware DateMath

The `DateMath` utility also provides overloads that work directly with `TimeZoneInfo` for better timezone handling:

```csharp
using Exceptionless.DateTimeExtensions;

// Parse expressions using a specific timezone
var utcTimeZone = TimeZoneInfo.Utc;
var easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US/Eastern");

// "now" will use current time in the specified timezone
var utcResult = DateMath.Parse("now+1h", utcTimeZone);
var easternResult = DateMath.Parse("now/d", easternTimeZone, isUpperLimit: false);

// TryParse with timezone
if (DateMath.TryParse("now+2d-3h", easternTimeZone, false, out var result)) {
Console.WriteLine($"Eastern time result: {result:O}");
}

// Dates without explicit timezone use the provided TimeZoneInfo
var localDate = DateMath.Parse("2023-06-15T14:30:00||+1M", easternTimeZone);

// Dates with explicit timezone are preserved regardless of TimeZoneInfo parameter
var preservedTz = DateMath.Parse("2023-06-15T14:30:00+05:00||+1M", easternTimeZone);
// Result will still have +05:00 offset, not Eastern time offset
```

The `DateMath` utility supports the same comprehensive syntax as `DateTimeRange` but provides a simpler API for direct parsing operations.

### TimeUnit

Quickly work with time units. . Check out our [unit tests](https://github.com/exceptionless/Exceptionless.DateTimeExtensions/blob/main/tests/Exceptionless.DateTimeExtensions.Tests/TimeUnitTests.cs) for more usage samples.
Expand Down
Loading