bindingasp.net-core-mvc

Binding to a TimeSpan property in ASP.NET Core MVC


I have a TimeSpan property on an object which binds to a time value in a SQL Server database using EF Core. I would like to have an input on a form in an ASP.NET Core 2.2 MVC project where a user can enter a time value such as "9:00 AM" and bind it to this TimeSpan property. The built in property binding in ASP.NET Core MVC does not handle this well.

Could someone provide sample code to add a way to setup binding to TimeSpan properties without having to manually receive and convert the property in the post controller method?


Solution

  • Generally speaking, there's the DisplayFormat attribute that can be applied to your property:

    [DisplayFormat(DataFormatString = "{0:hh:mm tt}", ApplyFormatInEditMode = true)]
    public TimeSpan MyTimeSpan { get; set; }
    

    The ApplyFormatInEditMode property of the attribute determines whether this format will be used for parsing the value as well.

    However, you've also got to contend with some HTML5-specific stuff here. By default, a TimeSpan property will be rendered as an input of type "time". Which modern browsers will replace with a time control. It will be localized to the user, allowing entry of 12 hour and AM/PM where appropriate. However, the value of this input must be in ISO format: hh:mm:ss (24-hour time, no AM/PM). If you set the value of the input to something like 9:00 AM instead of 09:00:00, then it will be treated by the browser as the same as null, and the control will be rendered unset with any value.

    Additionally, relying on a specific binding format leaves no room for user variances in input. If one user enters the time in 24-hour format, the binding will fail when it hits the server, because it won't be in the expected format. You'd need to have custom client-side validation to ensure that the user sticks to the appropriate format.

    Your best bet is to simply send and receive ISO (hh:mm:ss). Client-side then, you can either rely on the browser control and/or progressively enhance the input with some JS library for time entry. Under the hood, you'll be dealing only with ISO times, but then you can present a localized user-focused format for display/entry.