Declined
Last Updated: 11 Aug 2025 13:12 by ADMIN
Garrett
Created on: 23 Dec 2024 14:09
Category: Grid
Type: Bug Report
1
DateOnly field cannot be filtered in a Grid configured for OData binding

### Bug report

When the Grid is set up for OData-v4 binding, the columns that bind to DateOnly fields fail to filter. The date value in the filter expression contains the time portion and the following error is thrown:

"The binary operator GreaterThan is not defined for the types 'System.Nullable`1[System.DateOnly]' and 'System.Nullable`1[System.DateTimeOffset]'."

### Reproduction of the problem

1) Create a Grid that uses OData-v4 binding.

2) Bind a specified column to a DateOnly field.

3) Filter the column through the default column filter menu and open the browser DevTools to review the response of the request.

//Model
public DateOnly LastProdUpdate { get; set; }

//View
@(Html.Kendo().Grid<ProductViewModel>()
    .Name("grid")
     .Columns(columns =>
        {
            columns.Bound(p => p.LastProdUpdate).Format("{0:dd/MM/yyyy}");
        })
        ...
        .Filterable()
        .DataSource(dataSource => dataSource
        .Custom()
        .Type("odata-v4")
        .Transport(t =>
        {
            t.Read(read => read.Url("/odata/Products").Data("function() {return {'$expand': 'Employee'} }"));
        })
        .PageSize(10)
        .ServerPaging(true)
        .ServerFiltering(true)
        .ServerSorting(true)
     )
)

### Expected/desired behavior

The DateOnly fields must be filtered successfully as the DateTime fields.

### Environment

* **Kendo UI version: 2024.4.1112
* **Browser: [all]

3 comments
ADMIN
Mihaela
Posted on: 29 Jul 2025 12:38

Hello Garret,

Thank you for your feedback. It is great that you found a solution that works for you.

Regarding the synthetic property, your assumption is correct - the filtering and sorting on this property will perform after the data is retrieved from the database, which can lead to larger result sets being loaded into memory.

    Regards,
    Mihaela
    Progress Telerik

    Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

    Garrett
    Posted on: 26 Jul 2025 05:50

    Hi Mihaela,

    Thanks for the interesting suggestion.  I didn't know one could ignore and remap properties in the OData configuration.  However, I presume that when filtering or sorting on the synthetic property that these will not be "passed through" to the database, meaning many more rows than necessary may be retrieved from the DB in order to perform sorting/filtering on the web server.

    You wrote "Until OData natively parses $filter=CreatedAt eq 2025-07-22 as Edm.Date, this mismatch will remain a problem."  Actually, this URL worked for me, where LastProdUpdate is a DateOnly column.

    https://localhost:44315/odata/VisitTypeVersionDetails?%24format=json&%24top=10&%24filter=LastProdUpdate%20eq%202024-12-10&virtual=true&%24count=true

    But this is merely academic at this point.  I've long ago converted my database columns to the datetime datatype to get around this issue.

    Thanks, Garrett

    ADMIN
    Mihaela
    Posted on: 25 Jul 2025 05:22

    According to public evidence, DateOnly has no direct match in the EDM system. Meaning that it still uses Edm.DateTimeOffset as a predominant EDT Date type. OData does not recognize 2025-07-22 as a DateOnly — it parses it as DateTimeOffset. This can be verified by the filter parameter within the constructed URL:



    Until OData natively parses $filter=CreatedAt eq 2025-07-22 as Edm.Date, this mismatch will remain a problem.

    There are other reports that imply that there are DateOnly implications that might be interconnected with the reported problem as well:

    As well as other issues that are categorized as "investigative":

    Workaround

    The most straightforward workaround would be to introduce a synthetic property that will be serialized as a DateTimeOffset construct underneath:

    For example:

    • Model
            public DateOnly CreatedAt
            {
                get;
                set;
            }
    
            [NotMapped]
            public DateTime CreatedAtDateTime => CreatedAt.ToDateTime(TimeOnly.MinValue);

    EDM Mapping (Typically in the minimal hosting model):

    builder.Services.AddControllers()
                    .AddOData(options =>
                    {
                        options.AddRouteComponents("odata", GetEdmModel(), defaultBatchHandler);
                        options.Select()
                               .Filter()
                               .Count()
                               .OrderBy()
                               .Expand()
                               .Select()
                               .SetMaxTop(null);
                    });
    ...
    
    app.UseODataBatching();
    ...
    
    
    static IEdmModel GetEdmModel()
    {
        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        var products = builder.EntitySet<ProductViewModel>("Products");
        var productType = products.EntityType;
    
        productType.Ignore(p => p.CreatedAt); // Ignore the DateOnly field
        productType.Property(p => p.CreatedAtDateTime).Name = "CreatedAt"; // Transpose the "DateOnly" field to the synthetic property.
    
        return builder.GetEdmModel();
    }

    Although it will be parsed as a DateTimeOffSet construct:

    <Property Name="CreatedAt" Type="Edm.DateTimeOffset" Nullable="false"/>

    The Grid will permit utilizing the DateOnly field as follows:

         .Columns(columns =>
         {
                columns.Bound(p => p.CreatedAt);
         })

    Other alternatives

    Other alternatives would inevitably require a surgical intervention within the depths of the Telerik UI for ASP.NET Core library that. More in particular, with a dedicated parsing/serialization logic aimed to convert DataSourceRequest and DataSourceResult abstractions in a more OData-friendly manner.

    This is a mechanism that the Telerik UI for Blazor library is currently using.

    Regards,
    Mihaela
    Progress Telerik

    Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.