skip to Main Content

I have binding issue between NodaTime and aspnet when using LocalDateTime

Here is a sample razor pages app :

Project.csproj

<ItemGroup>
  <PackageReference Include="NodaTime" Version="3.1.12" />
  <PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.2.0" />
</ItemGroup>

Program.cs

builder.Services.AddRazorPages()
    .AddJsonOptions(o =>
        o.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb));

Index.cs

public class IndexModel : PageModel
{
    [BindProperty]
    public LocalDateTime Local { get; set; }
    
    public void OnPost()
    {
            
    }
}

Index.cs.cshtml

<form method="post">
    <input asp-for="Local" type="datetime-local" />
    <button type="submit">Send</button>
</form>

The default format the browser is using to send data back to server is yyyy-MM-ddTHH:mm which is not supported by default by NodaTime.

NodaTime is expecting yyyy-MM-ddTHH:mm:ss to work.

What would be the best way to have NodaTime work with the input type datetime-local

2

Answers


  1. Instead of just calling ConfigureForNodaTime(this JsonSerializerOptions options, DateTimeZoneProvider zoneProvider), as of version 1.2.0 of NodaTime.Serialization.SystemTextJson, you can call ConfigureForNodaTime(this JsonSerializerOptions options, NodaJsonSettings nodaJsonSettings). That lets you perform much more configuration. Ideally you’d use LocalDateTimePattern.DateHourMinuteIso, but that’s not yet in a NodaTime release – fortunately you can effectively create it yourself:

    var pattern = LocalDateTimePattern.CreateWithInvariantCulture("uuuu-MM-dd'T'HH:mm");
    var nodaSettings = new NodaJsonSettings
    {
        LocalDateTimeConverter = new NodaPatternConverter<LocalTime>(pattern)
    };
    builder.Services.AddRazorPages()
        .AddJsonOptions(o =>
            o.JsonSerializerOptions.ConfigureForNodaTime(nodaSettings));
    

    This code is untested, but I’d expect it to work.

    Note that this will always parse and format values using HH:mm. If you actually want to format to HH:mm:ss when seconds are present, that would require a bit more work, using CompositePatternBuilder<>.

    Login or Signup to reply.
  2. In an ASP.Net Core way you could try custom Model Binder.

    ModelBinder

    public class TimeModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
    
            var valueAsString = value.FirstValue;
    
            if (LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-ddTHH:mm").Parse(valueAsString).TryGetValue(LocalDateTime.MinIsoValue, out var localDateTime))
            {
                bindingContext.Result = ModelBindingResult.Success(localDateTime);
            }
            else
            {
                //...
            }
    
            return Task.CompletedTask;
        }
    }
    

    ModelBinderProvider

    public class TimeModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            var modelType = context.Metadata.ModelType;
    
            if (modelType == typeof(LocalDateTime) || modelType == typeof(LocalDateTime?))
            {
                return new BinderTypeModelBinder(typeof(TimeModelBinder));
            }
    
            return null;
        }
    }
    

    Register service

    builder.Services.AddRazorPages()
        .AddJsonOptions(o =>
            o.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb))
        .AddMvcOptions(options =>
        {
            options.ModelBinderProviders.Insert(0, new TimeModelBinderProvider());
        });
    

    enter image description here

    This way the NodaTime can work with the input type datetime-local, however since it doesn’t deal with seconds, it is always 00.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search