@@ -68,8 +68,9 @@ public void CanParseNamedRanges(string input, DateTime start, DateTime end)
68
68
Assert . Equal ( end , range . End ) ;
69
69
}
70
70
71
- public static IEnumerable < object [ ] > Inputs => new [ ] {
72
- new object [ ] { "today" , _now . StartOfDay ( ) , _now . EndOfDay ( ) } ,
71
+ public static IEnumerable < object [ ] > Inputs =>
72
+ [
73
+ [ "today" , _now . StartOfDay ( ) , _now . EndOfDay ( ) ] ,
73
74
[ "yesterday" , _now . SubtractDays ( 1 ) . StartOfDay ( ) , _now . SubtractDays ( 1 ) . EndOfDay ( ) ] ,
74
75
[ "tomorrow" , _now . AddDays ( 1 ) . StartOfDay ( ) , _now . AddDays ( 1 ) . EndOfDay ( ) ] ,
75
76
[ "last 5 minutes" , _now . SubtractMinutes ( 5 ) . StartOfMinute ( ) , _now ] ,
@@ -83,6 +84,272 @@ public void CanParseNamedRanges(string input, DateTime start, DateTime end)
83
84
[ "next nov" , _now . AddYears ( 1 ) . StartOfMonth ( ) , _now . AddYears ( 1 ) . EndOfMonth ( ) ] ,
84
85
[ "next jan" , _now . AddYears ( 1 ) . ChangeMonth ( 1 ) . StartOfMonth ( ) , _now . AddYears ( 1 ) . ChangeMonth ( 1 ) . EndOfMonth ( ) ] ,
85
86
[ "jan-feb" , _now . ChangeMonth ( 1 ) . StartOfMonth ( ) , _now . ChangeMonth ( 2 ) . EndOfMonth ( ) ] ,
86
- [ "now-this feb" , _now , _now . AddYears ( 1 ) . ChangeMonth ( 2 ) . EndOfMonth ( ) ]
87
- } ;
87
+ [ "now-this feb" , _now , _now . AddYears ( 1 ) . ChangeMonth ( 2 ) . EndOfMonth ( ) ] ,
88
+
89
+ // Date math expressions without brackets (testing if they work)
90
+ [ "now-6h TO now" , _now . AddHours ( - 6 ) , _now ] ,
91
+ [ "now-1d TO now" , _now . AddDays ( - 1 ) , _now ] ,
92
+
93
+ // Bracket notation with date math (currently failing - documenting expected behavior)
94
+ [ "[now-6h TO now]" , _now . AddHours ( - 6 ) , _now ] ,
95
+ [ "[now-1d TO now]" , _now . AddDays ( - 1 ) , _now ] ,
96
+ [ "[now-30m TO now]" , _now . AddMinutes ( - 30 ) , _now ] ,
97
+ [ "[now TO now+2h]" , _now , _now . AddHours ( 2 ) ] ,
98
+ [ "[now-1h TO now+1h]" , _now . AddHours ( - 1 ) , _now . AddHours ( 1 ) ] ,
99
+
100
+ // Curly brace notation with date math (currently failing - documenting expected behavior)
101
+ [ "{now-6h TO now}" , _now . AddHours ( - 6 ) , _now ] ,
102
+ [ "{now-1d TO now}" , _now . AddDays ( - 1 ) , _now ] ,
103
+
104
+ // Mixed expressions with brackets (currently failing - documenting expected behavior)
105
+ [ "[yesterday TO now]" , _now . SubtractDays ( 1 ) . StartOfDay ( ) , _now ] ,
106
+ [ "[now-1w TO today]" , _now . AddDays ( - 7 ) , _now . EndOfDay ( ) ]
107
+ ] ;
108
+
109
+ [ Fact ]
110
+ public void Parse_Yesterday_ReturnsFullDayRange ( )
111
+ {
112
+ // Arrange
113
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
114
+ var expected = baseTime . AddDays ( - 1 ) . StartOfDay ( ) ;
115
+
116
+ // Act
117
+ var range = DateTimeRange . Parse ( "yesterday" , baseTime ) ;
118
+
119
+ // Assert
120
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
121
+ Assert . Equal ( expected , range . Start ) ;
122
+ Assert . Equal ( expected . EndOfDay ( ) , range . End ) ;
123
+ }
124
+
125
+ [ Fact ]
126
+ public void Parse_Today_ReturnsFullDayRange ( )
127
+ {
128
+ // Arrange
129
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
130
+ var expected = baseTime . StartOfDay ( ) ;
131
+
132
+ // Act
133
+ var range = DateTimeRange . Parse ( "today" , baseTime ) ;
134
+
135
+ // Assert
136
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
137
+ Assert . Equal ( expected , range . Start ) ;
138
+ Assert . Equal ( expected . EndOfDay ( ) , range . End ) ;
139
+ }
140
+
141
+ [ Fact ]
142
+ public void Parse_Tomorrow_ReturnsFullDayRange ( )
143
+ {
144
+ // Arrange
145
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
146
+ var expected = baseTime . AddDays ( 1 ) . StartOfDay ( ) ;
147
+
148
+ // Act
149
+ var range = DateTimeRange . Parse ( "tomorrow" , baseTime ) ;
150
+
151
+ // Assert
152
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
153
+ Assert . Equal ( expected , range . Start ) ;
154
+ Assert . Equal ( expected . EndOfDay ( ) , range . End ) ;
155
+ }
156
+
157
+ [ Fact ]
158
+ public void Parse_LastFiveMinutes_ReturnsPastTimeRange ( )
159
+ {
160
+ // Arrange
161
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
162
+ var expectedStart = baseTime . AddMinutes ( - 5 ) . StartOfMinute ( ) ;
163
+
164
+ // Act
165
+ var range = DateTimeRange . Parse ( "last 5 minutes" , baseTime ) ;
166
+
167
+ // Assert
168
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
169
+ Assert . Equal ( expectedStart , range . Start ) ;
170
+ Assert . Equal ( baseTime , range . End ) ;
171
+ }
172
+
173
+ [ Fact ]
174
+ public void Parse_NextTwoHours_ReturnsFutureTimeRange ( )
175
+ {
176
+ // Arrange
177
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
178
+ var expectedEnd = baseTime . AddHours ( 2 ) . EndOfHour ( ) ;
179
+
180
+ // Act
181
+ var range = DateTimeRange . Parse ( "next 2 hours" , baseTime ) ;
182
+
183
+ // Assert
184
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
185
+ Assert . Equal ( baseTime , range . Start ) ;
186
+ Assert . Equal ( expectedEnd , range . End ) ;
187
+ }
188
+
189
+ [ Fact ]
190
+ public void Parse_BracketNotationWithDateMath_ParsesCorrectly ( )
191
+ {
192
+ // Arrange
193
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
194
+
195
+ // Act
196
+ var range = DateTimeRange . Parse ( "[now-6h TO now]" , baseTime ) ;
197
+
198
+ // Assert
199
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
200
+ Assert . NotEqual ( DateTime . MinValue , range . Start ) ;
201
+ Assert . NotEqual ( DateTime . MinValue , range . End ) ;
202
+ Assert . True ( range . Start < range . End ) ;
203
+
204
+ // Verify 'now' resolves to base time
205
+ var tolerance = TimeSpan . FromMinutes ( 1 ) ;
206
+ Assert . True ( Math . Abs ( ( range . End - baseTime ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
207
+
208
+ // Verify the 6-hour span
209
+ var expectedStart = baseTime . AddHours ( - 6 ) ;
210
+ Assert . True ( Math . Abs ( ( range . Start - expectedStart ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
211
+ }
212
+
213
+ [ Fact ]
214
+ public void Parse_CurlyBraceNotationWithDateMath_ParsesCorrectly ( )
215
+ {
216
+ // Arrange
217
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
218
+
219
+ // Act
220
+ var range = DateTimeRange . Parse ( "{now-1d TO now}" , baseTime ) ;
221
+
222
+ // Assert
223
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
224
+ Assert . NotEqual ( DateTime . MinValue , range . Start ) ;
225
+ Assert . NotEqual ( DateTime . MinValue , range . End ) ;
226
+ Assert . True ( range . Start < range . End ) ;
227
+
228
+ // Verify 'now' resolves to base time
229
+ var tolerance = TimeSpan . FromMinutes ( 1 ) ;
230
+ Assert . True ( Math . Abs ( ( range . End - baseTime ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
231
+
232
+ // Verify the 1-day span
233
+ var expectedStart = baseTime . AddDays ( - 1 ) ;
234
+ Assert . True ( Math . Abs ( ( range . Start - expectedStart ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
235
+ }
236
+
237
+ [ Fact ]
238
+ public void Parse_DateMathWithoutBrackets_ParsesCorrectly ( )
239
+ {
240
+ // Arrange
241
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
242
+
243
+ // Act
244
+ var range = DateTimeRange . Parse ( "now-6h TO now" , baseTime ) ;
245
+
246
+ // Assert
247
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
248
+ Assert . NotEqual ( DateTime . MinValue , range . Start ) ;
249
+ Assert . NotEqual ( DateTime . MinValue , range . End ) ;
250
+ Assert . True ( range . Start < range . End ) ;
251
+
252
+ // Verify 'now' resolves to base time
253
+ var tolerance = TimeSpan . FromMinutes ( 1 ) ;
254
+ Assert . True ( Math . Abs ( ( range . End - baseTime ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
255
+ }
256
+
257
+ [ Fact ]
258
+ public void Parse_MixedParsersInBracketNotation_ParsesCorrectly ( )
259
+ {
260
+ // Arrange
261
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
262
+
263
+ // Act
264
+ var range = DateTimeRange . Parse ( "[yesterday TO today]" , baseTime ) ;
265
+
266
+ // Assert
267
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
268
+ Assert . NotEqual ( DateTime . MinValue , range . Start ) ;
269
+ Assert . NotEqual ( DateTime . MinValue , range . End ) ;
270
+ Assert . True ( range . Start < range . End ) ;
271
+
272
+ // Verify yesterday and today are parsed correctly
273
+ var expectedStart = baseTime . AddDays ( - 1 ) . StartOfDay ( ) ;
274
+ var expectedEnd = baseTime . StartOfDay ( ) . EndOfDay ( ) ;
275
+ Assert . Equal ( expectedStart , range . Start ) ;
276
+ Assert . Equal ( expectedEnd , range . End ) ;
277
+ }
278
+
279
+ [ Fact ]
280
+ public void Parse_DateMathStartAndEnd_ParsesCorrectly ( )
281
+ {
282
+ // Arrange
283
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
284
+
285
+ // Act
286
+ var range = DateTimeRange . Parse ( "now-1h TO now+1h" , baseTime ) ;
287
+
288
+ // Assert
289
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
290
+ Assert . NotEqual ( DateTime . MinValue , range . Start ) ;
291
+ Assert . NotEqual ( DateTime . MinValue , range . End ) ;
292
+ Assert . True ( range . Start < range . End ) ;
293
+
294
+ // Verify the 2-hour span centered on base time
295
+ var tolerance = TimeSpan . FromMinutes ( 1 ) ;
296
+ var expectedStart = baseTime . AddHours ( - 1 ) ;
297
+ var expectedEnd = baseTime . AddHours ( 1 ) ;
298
+ Assert . True ( Math . Abs ( ( range . Start - expectedStart ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
299
+ Assert . True ( Math . Abs ( ( range . End - expectedEnd ) . TotalMinutes ) < tolerance . TotalMinutes ) ;
300
+ }
301
+
302
+ [ Theory ]
303
+ [ InlineData ( "now-6h TO now+6h" , 12 , "DateMath expressions should create 12-hour range" ) ]
304
+ [ InlineData ( "now-1d TO now" , 24 , "DateMath expressions should create 24-hour range" ) ]
305
+ [ InlineData ( "now TO now+30m" , 0.5 , "DateMath expressions should create 30-minute range" ) ]
306
+ public void Parse_DateMathExpressions_CreatesCorrectTimeSpans ( string input , double expectedHours , string reason )
307
+ {
308
+ // Arrange
309
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
310
+
311
+ // Act
312
+ var range = DateTimeRange . Parse ( input , baseTime ) ;
313
+
314
+ // Assert
315
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
316
+ var actualHours = ( range . End - range . Start ) . TotalHours ;
317
+ Assert . True ( Math . Abs ( actualHours - expectedHours ) < 0.1 ,
318
+ $ "{ reason } . Expected { expectedHours } hours, but got { actualHours } hours") ;
319
+ }
320
+
321
+ [ Fact ]
322
+ public void Parse_BracketNotationWithDateMath_PreservesTimeZoneInformation ( )
323
+ {
324
+ // Arrange
325
+ var baseTime = new DateTimeOffset ( 2023 , 12 , 25 , 12 , 0 , 0 , TimeSpan . FromHours ( - 5 ) ) ;
326
+ const string input = "[now-6h TO now]" ;
327
+
328
+ // Act
329
+ var range = DateTimeRange . Parse ( input , baseTime ) ;
330
+
331
+ // Assert
332
+ Assert . NotEqual ( DateTimeRange . Empty , range ) ;
333
+ Assert . Equal ( baseTime . AddHours ( - 6 ) . DateTime , range . Start ) ;
334
+ Assert . Equal ( baseTime . DateTime , range . End ) ;
335
+ }
336
+
337
+ [ Theory ]
338
+ [ InlineData ( "now+1INVALID" , "Invalid unit should not parse" ) ]
339
+ [ InlineData ( "now+" , "Incomplete expression should not parse" ) ]
340
+ [ InlineData ( "now++1d" , "Double operators should not parse" ) ]
341
+ [ InlineData ( "[now+ TO now]" , "Invalid left side in bracket notation should not parse" ) ]
342
+ public void Parse_InvalidDateMathExpressions_ReturnsEmptyRange ( string input , string reason )
343
+ {
344
+ // Arrange
345
+ var baseTime = new DateTime ( 2023 , 12 , 25 , 12 , 0 , 0 ) ;
346
+
347
+ // Act
348
+ var range = DateTimeRange . Parse ( input , baseTime ) ;
349
+
350
+ // Assert - Invalid expressions should result in empty range or fallback parsing
351
+ // The behavior may vary based on parser priority and fallback mechanisms
352
+ Assert . True ( range == DateTimeRange . Empty || range . Start != DateTime . MinValue ,
353
+ $ "{ reason } . Input '{ input } ' should either return empty range or valid fallback parsing") ;
354
+ }
88
355
}
0 commit comments