Skip to content

Commit 904ea19

Browse files
committed
Adds wildcard support to date range parsing
Extends the date range parsing functionality to support wildcard characters, allowing for open-ended date ranges. Implements a new `WildcardPartParser` to handle "*" characters, representing either the minimum or maximum DateTimeOffset value based on the context. Updates the `TwoPartFormatParser` to correctly handle bracketed date ranges and wildcard characters.
1 parent 1d2765d commit 904ea19

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Text.RegularExpressions;
3+
4+
namespace Exceptionless.DateTimeExtensions.FormatParsers.PartParsers;
5+
6+
[Priority(1)]
7+
public class WildcardPartParser : IPartParser
8+
{
9+
private static readonly Regex _wildCardRegex = new(@"\G\s*\*(?=\s|\]|\}|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
10+
11+
public Regex Regex => _wildCardRegex;
12+
13+
public DateTimeOffset? Parse(Match match, DateTimeOffset relativeBaseTime, bool isUpperLimit)
14+
{
15+
if (!match.Success)
16+
return null;
17+
18+
return isUpperLimit ? DateTimeOffset.MaxValue : DateTimeOffset.MinValue;
19+
}
20+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.RegularExpressions;
4+
using Exceptionless.DateTimeExtensions.FormatParsers.PartParsers;
5+
using Microsoft.Extensions.Logging;
6+
using Xunit;
7+
using Xunit.Abstractions;
8+
9+
namespace Exceptionless.DateTimeExtensions.Tests.FormatParsers.PartParsers;
10+
11+
public class WildcardPartParserTests : PartParserTestsBase
12+
{
13+
public WildcardPartParserTests(ITestOutputHelper output) : base(output) { }
14+
15+
[Theory]
16+
[MemberData(nameof(ParseInputs))]
17+
public void ParseInput(string input, bool isUpperLimit, DateTimeOffset? expected)
18+
{
19+
var parser = new WildcardPartParser();
20+
_logger.LogInformation("Testing input: '{Input}', IsUpperLimit: {IsUpperLimit}, Expected: {Expected}", input, isUpperLimit, expected);
21+
22+
var match = parser.Regex.Match(input);
23+
_logger.LogInformation("Regex match success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);
24+
25+
var result = parser.Parse(match, _now, isUpperLimit);
26+
_logger.LogInformation("Parse result: {Result}", result);
27+
28+
if (expected == null)
29+
{
30+
Assert.Null(result);
31+
}
32+
else
33+
{
34+
Assert.NotNull(result);
35+
Assert.Equal(expected.Value.DateTime, result.Value.DateTime);
36+
}
37+
}
38+
39+
public static IEnumerable<object[]> ParseInputs
40+
{
41+
get
42+
{
43+
return new[]
44+
{
45+
// Valid wildcard inputs
46+
new object[] { "*", false, DateTimeOffset.MinValue },
47+
new object[] { "*", true, DateTimeOffset.MaxValue },
48+
new object[] { " * ", false, DateTimeOffset.MinValue },
49+
new object[] { " * ", true, DateTimeOffset.MaxValue },
50+
new object[] { " * ", false, DateTimeOffset.MinValue },
51+
new object[] { " * ", true, DateTimeOffset.MaxValue },
52+
53+
// Invalid inputs (patterns that should not match a complete wildcard)
54+
new object[] { "blah", false, null },
55+
new object[] { "blah", true, null },
56+
new object[] { "2012", false, null },
57+
new object[] { "2012", true, null },
58+
new object[] { "**", false, null },
59+
60+
// This should match the first * in a two-part context like "* *"
61+
new object[] { "* *", false, DateTimeOffset.MinValue },
62+
};
63+
}
64+
}
65+
66+
[Fact]
67+
public void RegexPatternTest()
68+
{
69+
var parser = new WildcardPartParser();
70+
var regex = parser.Regex;
71+
72+
_logger.LogInformation("Regex pattern: {Pattern}", regex);
73+
74+
// Test various inputs
75+
var testInputs = new[] { "*", " * ", " * ", "blah", "2012", "**", "* *", "" };
76+
77+
foreach (var input in testInputs)
78+
{
79+
var match = regex.Match(input);
80+
_logger.LogInformation("Input: '{Input}' -> Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", input, match.Success, match.Value, match.Index, match.Length);
81+
}
82+
}
83+
84+
[Fact]
85+
public void TestInTwoPartContext()
86+
{
87+
var parser = new WildcardPartParser();
88+
89+
// Test how it behaves in a two-part parsing context
90+
var inputs = new[] { "* TO 2013", "2012 TO *", "[* TO 2013]", "{2012 TO *}" };
91+
92+
foreach (var input in inputs)
93+
{
94+
_logger.LogInformation("Testing two-part context for: '{Input}'", input);
95+
96+
// Test parsing at the beginning
97+
var match = parser.Regex.Match(input, 0);
98+
_logger.LogInformation(" At position 0: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);
99+
100+
// Test parsing after bracket
101+
if (input.StartsWith("[") || input.StartsWith("{"))
102+
{
103+
match = parser.Regex.Match(input, 1);
104+
_logger.LogInformation(" At position 1: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);
105+
}
106+
107+
// Find TO and test parsing after it
108+
var toIndex = input.IndexOf(" TO ", StringComparison.OrdinalIgnoreCase);
109+
if (toIndex >= 0)
110+
{
111+
var afterTo = toIndex + 4;
112+
match = parser.Regex.Match(input, afterTo);
113+
_logger.LogInformation(" After TO at position {Position}: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", afterTo, match.Success, match.Value, match.Index, match.Length);
114+
}
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)