Skip to content

Commit c831438

Browse files
committed
fix: date and time handling
1 parent db93a1a commit c831438

File tree

5 files changed

+273
-23
lines changed

5 files changed

+273
-23
lines changed

QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,117 @@ public async Task can_filter_by_string()
3838
people[0].Id.Should().Be(fakePersonOne.Id);
3939
}
4040

41+
[Fact]
42+
public async Task can_filter_by_datetime_with_milliseconds()
43+
{
44+
// Arrange
45+
var testingServiceScope = new TestingServiceScope();
46+
var dateUtcNow = DateTime.UtcNow;
47+
var dateInMilliPast = dateUtcNow.AddMilliseconds(-100);
48+
var fakePersonOne = new FakeTestingPersonBuilder()
49+
.WithSpecificDateTime(dateUtcNow)
50+
.Build();
51+
var fakePersonTwo = new FakeTestingPersonBuilder()
52+
.WithSpecificDateTime(dateInMilliPast)
53+
.Build();
54+
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);
55+
56+
var input = $"""SpecificDateTime == "{fakePersonOne.SpecificDateTime:yyyy-MM-ddTHH:mm:ss.ffffff}Z" """;
57+
58+
// Act
59+
var queryablePeople = testingServiceScope.DbContext().People;
60+
61+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
62+
var people = await appliedQueryable.ToListAsync();
63+
64+
// Assert
65+
people.Count.Should().Be(1);
66+
people[0].Id.Should().Be(fakePersonOne.Id);
67+
}
68+
69+
[Fact]
70+
public async Task can_filter_by_dateonly()
71+
{
72+
// Arrange
73+
var testingServiceScope = new TestingServiceScope();
74+
var baseDate = new DateTime(1678, 01, 01);
75+
var dateToday = DateOnly.FromDateTime(baseDate);
76+
var fakePersonOne = new FakeTestingPersonBuilder()
77+
.WithDate(dateToday)
78+
.Build();
79+
var fakePersonTwo = new FakeTestingPersonBuilder()
80+
.WithDate(DateOnly.FromDateTime(baseDate.AddDays(-1)))
81+
.Build();
82+
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);
83+
84+
var input = $"""Date == "{fakePersonOne.Date:yyyy-MM-dd}" """;
85+
86+
// Act
87+
var queryablePeople = testingServiceScope.DbContext().People;
88+
89+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
90+
var people = await appliedQueryable.ToListAsync();
91+
92+
// Assert
93+
people.Count.Should().Be(1);
94+
people[0].Id.Should().Be(fakePersonOne.Id);
95+
}
96+
97+
[Fact]
98+
public async Task can_filter_by_timeonly_with_micros()
99+
{
100+
// Arrange
101+
var testingServiceScope = new TestingServiceScope();
102+
var timeNow = TimeOnly.FromDateTime(DateTime.UtcNow);
103+
var fakePersonOne = new FakeTestingPersonBuilder()
104+
.WithTime(timeNow)
105+
.Build();
106+
var fakePersonTwo = new FakeTestingPersonBuilder()
107+
.WithTime(TimeOnly.FromDateTime(DateTime.UtcNow.AddHours(-1)))
108+
.Build();
109+
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);
110+
111+
var input = $"""Time == "{fakePersonOne.Time:HH:mm:ss.ffffff}" """;
112+
113+
// Act
114+
var queryablePeople = testingServiceScope.DbContext().People;
115+
116+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
117+
var people = await appliedQueryable.ToListAsync();
118+
119+
// Assert
120+
people.Count.Should().Be(1);
121+
people[0].Id.Should().Be(fakePersonOne.Id);
122+
}
123+
124+
[Fact]
125+
public async Task can_filter_by_timeonly_without_micros()
126+
{
127+
// Arrange
128+
var testingServiceScope = new TestingServiceScope();
129+
var timeNow = TimeOnly.FromDateTime(DateTime.UtcNow);
130+
var fakePersonOne = new FakeTestingPersonBuilder()
131+
.WithTime(timeNow)
132+
.Build();
133+
var fakePersonTwo = new FakeTestingPersonBuilder()
134+
.WithTime(TimeOnly.FromDateTime(DateTime.UtcNow.AddHours(-1)))
135+
.Build();
136+
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);
137+
138+
var input = $"""Time >= "{fakePersonOne.Time:HH:mm:ss}" """;
139+
140+
// Act
141+
var queryablePeople = testingServiceScope.DbContext().People;
142+
143+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
144+
var people = await appliedQueryable.ToListAsync();
145+
146+
// Assert
147+
people.Count.Should().Be(1);
148+
people[0].Id.Should().Be(fakePersonOne.Id);
149+
}
150+
151+
41152
[Fact]
42153
public async Task can_filter_by_guid()
43154
{

QueryKit.UnitTests/FilterParserTests.cs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public void complex_with_lots_of_types()
3636

3737
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
3838
filterExpression.ToString().Should()
39-
.Be(""""x => (((((((x.Title.ToLower().Contains("waffle & chicken".ToLower()) AndAlso (x.Age > 30)) OrElse (x.Id == Parse("aa648248-cb69-4217-ac95-d7484795afb2"))) OrElse (x.Title == "lamb")) OrElse (x.Title == null)) AndAlso ((x.Age < 18) OrElse ((x.BirthMonth == "January") AndAlso x.Title.StartsWith("ally")))) OrElse (x.Rating > 3.5)) OrElse ((x.SpecificDate == 7/1/2022 12:00:03 AM +00:00) AndAlso ((x.Date == 7/1/2022) OrElse (x.Time == 12:00 AM))))"""");
39+
.Be(""""x => (((((((x.Title.ToLower().Contains("waffle & chicken".ToLower()) AndAlso (x.Age > 30)) OrElse (x.Id == Parse("aa648248-cb69-4217-ac95-d7484795afb2"))) OrElse (x.Title == "lamb")) OrElse (x.Title == null)) AndAlso ((x.Age < 18) OrElse ((x.BirthMonth == "January") AndAlso x.Title.StartsWith("ally")))) OrElse (x.Rating > 3.5)) OrElse ((x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 00:00:00))) AndAlso ((x.Date == new Nullable`1(new DateOnly(2022, 7, 1))) OrElse (x.Time == new Nullable`1(new TimeOnly(0, 0, 3, 0, 0))))))"""");
4040
}
4141

4242
[Fact]
@@ -253,31 +253,31 @@ public void can_handle_date_only()
253253
{
254254
var input = """Date == 2022-07-01""";
255255
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
256-
filterExpression.ToString().Should().Be(""""x => (x.Date == 7/1/2022)"""");
256+
filterExpression.ToString().Should().Be(""""x => (x.Date == new Nullable`1(new DateOnly(2022, 7, 1)))"""");
257257
}
258258

259259
[Fact]
260260
public void can_handle_date_time_offset()
261261
{
262262
var input = """SpecificDate == 2022-07-01T00:00:03Z""";
263263
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
264-
filterExpression.ToString().Should().Be(""""x => (x.SpecificDate == 7/1/2022 12:00:03 AM +00:00)"""");
264+
filterExpression.ToString().Should().Be(""""x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 00:00:00)))"""");
265265
}
266266

267267
[Fact]
268268
public void can_handle_datetime()
269269
{
270270
var input = """SpecificDateTime == 2022-07-01T00:00:03""";
271271
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
272-
filterExpression.ToString().Should().Be(""""x => (x.SpecificDateTime == 7/1/2022 12:00:03 AM)"""");
272+
filterExpression.ToString().Should().Be(""""x => (x.SpecificDateTime == new DateTime(637922304030000000, Local))"""");
273273
}
274274

275275
[Fact]
276276
public void can_handle_datetime_comparison_with_timezone()
277277
{
278278
var input = """SpecificDate == 2022-07-01T00:00:03+01:00""";
279279
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
280-
filterExpression.ToString().Should().Be("x => (x.SpecificDate == 7/1/2022 12:00:03 AM +01:00)");
280+
filterExpression.ToString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 01:00:00)))");
281281
}
282282

283283
[Theory]
@@ -288,31 +288,63 @@ public void can_handle_date_time_offset_another(string format)
288288
var dateTimeOffset = DateTimeOffset.Parse("2022-07-01T00:00:03Z").ToString(format);
289289
var input = $"""SpecificDate == "{dateTimeOffset}" """;
290290
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
291-
filterExpression.ToString().Should().Be(""""x => (x.SpecificDate == 7/1/2022 12:00:03 AM +00:00)"""");
291+
filterExpression.ToString().Should().Be(""""x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 00:00:00)))"""");
292292
}
293293

294294
[Fact]
295295
public void can_handle_datetime_another()
296296
{
297297
var input = """SpecificDateTime == "2022-07-01T00:00:03" """;
298298
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
299-
filterExpression.ToString().Should().Be(""""x => (x.SpecificDateTime == 7/1/2022 12:00:03 AM)"""");
299+
filterExpression.ToString().Should().Be(""""x => (x.SpecificDateTime == new DateTime(637922304030000000, Local))"""");
300+
}
301+
302+
[Fact]
303+
public void can_handle_datetime_utc()
304+
{
305+
var input = """SpecificDateTime == "2022-07-01T00:00:03Z" """;
306+
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
307+
filterExpression.ToString().Should().Be(""""x => (x.SpecificDateTime == new DateTime(637922304030000000, Utc))"""");
300308
}
301309

302310
[Fact]
303311
public void can_handle_datetime_comparison_with_timezone_another()
304312
{
305313
var input = """SpecificDate == "2022-07-01T00:00:03+01:00" """;
306314
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
307-
filterExpression.ToString().Should().Be("x => (x.SpecificDate == 7/1/2022 12:00:03 AM +01:00)");
315+
filterExpression.ToString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 01:00:00)))");
308316
}
309317

310318
[Fact]
311319
public void can_handle_time_only()
312320
{
313321
var input = "Time == 12:30:00";
314322
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
315-
filterExpression.ToString().Should().Be("x => (x.Time == 12:30 PM)");
323+
filterExpression.ToString().Should().Be("x => (x.Time == new Nullable`1(new TimeOnly(12, 30, 0, 0, 0)))");
324+
}
325+
326+
[Fact]
327+
public void can_handle_datetime_comparison_with_negative_timezone()
328+
{
329+
var input = """SpecificDate == 2022-07-01T00:00:03-02:00""";
330+
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
331+
filterExpression.ToString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, -02:00:00)))");
332+
}
333+
334+
[Fact]
335+
public void can_handle_datetime_comparison_with_timezone_no_minutes()
336+
{
337+
var input = """SpecificDate == 2022-07-01T00:00:03+02""";
338+
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
339+
filterExpression.ToString().Should().Be("x => (x.SpecificDate == new Nullable`1(new DateTimeOffset(637922304030000000, 02:00:00)))");
340+
}
341+
342+
[Fact]
343+
public void can_handle_datetime_with_milliseconds()
344+
{
345+
var input = """SpecificDateTime == 2022-07-01T00:00:03.123""";
346+
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
347+
filterExpression.ToString().Should().Be("x => (x.SpecificDateTime == new DateTime(637922304031230000, Local))");
316348
}
317349

318350
[Fact]
@@ -336,23 +368,23 @@ public void can_handle_time_comparison()
336368
{
337369
var input = """Time == 00:00:03""";
338370
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
339-
filterExpression.ToString().Should().Be("x => (x.Time == 12:00 AM)");
371+
filterExpression.ToString().Should().Be("x => (x.Time == new Nullable`1(new TimeOnly(0, 0, 3, 0, 0)))");
340372
}
341373

342374
[Fact]
343375
public void multiple_properties_and_operators()
344376
{
345377
var input = """Title _= "lamb" && Age >= 25 && Rating < 4.5 && SpecificDate <= 2022-07-01T00:00:03Z && Time == 00:00:03""";
346378
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
347-
filterExpression.ToString().Should().Be("x => ((((x.Title.StartsWith(\"lamb\") AndAlso (x.Age >= 25)) AndAlso (x.Rating < 4.5)) AndAlso (x.SpecificDate <= 7/1/2022 12:00:03 AM +00:00)) AndAlso (x.Time == 12:00 AM))");
379+
filterExpression.ToString().Should().Be("x => ((((x.Title.StartsWith(\"lamb\") AndAlso (x.Age >= 25)) AndAlso (x.Rating < 4.5)) AndAlso (x.SpecificDate <= new Nullable`1(new DateTimeOffset(637922304030000000, 00:00:00)))) AndAlso (x.Time == new Nullable`1(new TimeOnly(0, 0, 3, 0, 0))))");
348380
}
349381

350382
[Fact]
351383
public void complex_filter_with_nested_parentheses()
352384
{
353385
var input = """(Title == "lamb" && ((Age >= 25 && Rating < 4.5) || (SpecificDate <= 2022-07-01T00:00:03Z && Time == 00:00:03)) && (Favorite == true || Email.Value _= "example"))""";
354386
var filterExpression = FilterParser.ParseFilter<TestingPerson>(input);
355-
filterExpression.ToString().Should().Be("x => (((x.Title == \"lamb\") AndAlso (((x.Age >= 25) AndAlso (x.Rating < 4.5)) OrElse ((x.SpecificDate <= 7/1/2022 12:00:03 AM +00:00) AndAlso (x.Time == 12:00 AM)))) AndAlso ((x.Favorite == True) OrElse x.Email.Value.StartsWith(\"example\")))");
387+
filterExpression.ToString().Should().Be("""x => (((x.Title == "lamb") AndAlso (((x.Age >= 25) AndAlso (x.Rating < 4.5)) OrElse ((x.SpecificDate <= new Nullable`1(new DateTimeOffset(637922304030000000, 00:00:00))) AndAlso (x.Time == new Nullable`1(new TimeOnly(0, 0, 3, 0, 0)))))) AndAlso ((x.Favorite == True) OrElse x.Email.Value.StartsWith("example")))""");
356388
}
357389

358390
[Fact]

0 commit comments

Comments
 (0)