Skip to content

Commit 88e0fdd

Browse files
committed
fix: add DateOnly support to filtering and searching
1 parent 3b78cad commit 88e0fdd

File tree

6 files changed

+76
-4
lines changed

6 files changed

+76
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# 6.1.1
22
- Fix `AddUrlHelper` to create a more full ActionContext when operating outside an MVC action.
3+
- Fix errors thrown when filtering and searching on `System.DateOnly` properties.
34

45
# 6.1.0
56
- Added support for .NET 10

src/IntelliTect.Coalesce.Tests/Tests/Api/DataSources/StandardDataSourceTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,33 @@ public void ApplyListPropertyFilter_WhenPropIsDateTime_FiltersProp(
192192
Assert.Equal(shouldMatch ? 1 : 0, query.Count());
193193
}
194194

195+
public static IEnumerable<object[]> Filter_MatchesDateOnlyData = new[]
196+
{
197+
new object[] { true, "2017-08-02", new DateOnly(2017, 08, 02) },
198+
new object[] { false, "2017-08-02", new DateOnly(2017, 08, 03) },
199+
new object[] { false, "2017-08-02", new DateOnly(2017, 08, 01) },
200+
201+
new object[] { true, "2017-08-02T12:34:56", new DateOnly(2017, 08, 02) },
202+
new object[] { false, "2017-08-02T12:34:56", new DateOnly(2017, 08, 03) },
203+
204+
new object[] { false, "can't parse", new DateOnly(2017, 08, 02) },
205+
206+
// Null or empty inputs always do nothing - these will always match.
207+
new object[] { true, "", new DateOnly(2017, 08, 02) },
208+
new object[] { true, null, new DateOnly(2017, 08, 02) },
209+
};
210+
211+
[Theory]
212+
[MemberData(nameof(Filter_MatchesDateOnlyData))]
213+
public void ApplyListPropertyFilter_WhenPropIsDateOnly_FiltersProp(
214+
bool shouldMatch, string inputValue, DateOnly fieldValue)
215+
{
216+
var (prop, query) = PropertyFiltersTestHelper<ComplexModel, DateOnly>(
217+
m => m.SystemDateOnly, fieldValue, inputValue);
218+
219+
Assert.Equal(shouldMatch ? 1 : 0, query.Count());
220+
}
221+
195222

196223
public static IEnumerable<object[]> Filter_MatchesDateTimeOffsetsData = new[]
197224
{

src/IntelliTect.Coalesce.Tests/Tests/Api/SearchTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,34 @@ public void Search_DateTime_IsTimeZoneAgnostic(bool expectedMatch, string search
9999
TimeZoneInfo.CreateCustomTimeZone("test", TimeSpan.FromHours(new Random().Next(-11, 12)), "test", "test"));
100100
}
101101

102+
public static IEnumerable<object[]> Search_MatchesDateOnlyData = new[]
103+
{
104+
new object[] { true, "2017", new DateOnly(2017, 08, 15) },
105+
new object[] { false, "2018", new DateOnly(2017, 08, 15) },
106+
new object[] { true, "2017-08", new DateOnly(2017, 08, 01) },
107+
new object[] { true, "2017-08", new DateOnly(2017, 08, 15) },
108+
new object[] { false, "2017-08", new DateOnly(2017, 07, 31) },
109+
new object[] { false, "2017-08", new DateOnly(2017, 09, 01) },
110+
new object[] { false, "2018-08", new DateOnly(2017, 08, 15) },
111+
112+
new object[] { true, "Nov 6 17", new DateOnly(2017, 11, 06) },
113+
new object[] { true, "Nov 6, 2017", new DateOnly(2017, 11, 06) },
114+
new object[] { true, "6 November 2017", new DateOnly(2017, 11, 06) },
115+
new object[] { true, "6 Nov 2017", new DateOnly(2017, 11, 06) },
116+
new object[] { true, "6 Nov 17", new DateOnly(2017, 11, 06) },
117+
};
118+
119+
[Theory]
120+
[MemberData(nameof(Search_MatchesDateOnlyData))]
121+
public void Search_DateOnly_SearchesCorrectly(bool expectedMatch, string searchTerm, DateOnly searchCandidate)
122+
{
123+
SearchHelper(
124+
(ComplexModel t) => t.SystemDateOnly,
125+
"systemdateonly:" + searchTerm,
126+
searchCandidate,
127+
expectedMatch);
128+
}
129+
102130
[Theory]
103131
[InlineData(true, "FAFAB015-FFA4-41F8-B4DD-C15EB0CE40B6", "FAFAB015-FFA4-41F8-B4DD-C15EB0CE40B6")]
104132
[InlineData(true, "FAFAB015-FFA4-41F8-B4DD-C15EB0CE40B6", "fafab015-ffa4-41f8-b4dd-c15eb0ce40b6")]

src/IntelliTect.Coalesce/Api/DataSources/QueryableDataSourceBase`1.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ protected virtual IQueryable<T> ApplyListPropertyFilter(
112112
return query.Where(_ => false);
113113
}
114114

115+
// Handle DateOnly separately since it can't cast from DateTime
116+
if (propType.NullableValueUnderlyingType.IsA<DateOnly>() && DateOnly.TryParse(value, out DateOnly parsedDateOnly))
117+
{
118+
var dateParam = parsedDateOnly.AsQueryParam(propType);
119+
return query.WhereExpression(it =>
120+
Expression.Equal(it.Prop(prop), dateParam)
121+
);
122+
}
123+
115124
// See if they just passed in a date or a date and a time
116125
if (DateTime.TryParse(value, out DateTime parsedValue))
117126
{

src/IntelliTect.Coalesce/Helpers/Search/SearchableValueProperty.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ public SearchableValueProperty(PropertyViewModel prop) : base(prop)
1919
public enum ParseFlags
2020
{
2121
None = 0,
22-
HaveYear = 1 << 0,
23-
HaveMonth = 1 << 1,
24-
HaveDay = 1 << 2,
25-
HaveHour = 1 << 3,
22+
HaveYear = 1 << 0,
23+
HaveMonth = 1 << 1,
24+
HaveDay = 1 << 2,
25+
HaveHour = 1 << 3,
2626
HaveMinute = 1 << 4,
2727
HaveSecond = 1 << 5,
2828

@@ -144,6 +144,12 @@ out dt
144144
min = offset.AsQueryParam(propType);
145145
max = offset.Add(range.Value).AsQueryParam(propType);
146146
}
147+
else if (propType.NullableValueUnderlyingType.IsA<DateOnly>())
148+
{
149+
var dateOnly = new DateOnly(dt.Year, dt.Month, dt.Day);
150+
min = dateOnly.AsQueryParam(propType);
151+
max = dateOnly.AddDays((int)range.Value.TotalDays).AsQueryParam(propType);
152+
}
147153
else
148154
{
149155
min = dt.AsQueryParam(propType);

src/IntelliTect.Coalesce/TypeDefinition/PropertyViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ propName is null
224224
/// <summary>
225225
/// True if the property has the DateType(DateOnly) Attribute.
226226
/// </summary>
227+
[Obsolete("Documentation comment does not match behavior. Use `DateType` directly.")]
227228
public bool IsDateOnly => DateType == DateTypeAttribute.DateTypes.DateOnly;
228229

229230
/// <summary>

0 commit comments

Comments
 (0)