Search code examples
asp.net-corenodatime

aspnet razor pages form local-datetime not working with NodaTime LocalDateTime


I have a form with an input of type local-datetime that is expecting a Date and a Time with the following format : yyyy-MM-ddTHH:mm

By default NodaTime LocalDateTime format is yyyy-MM-ddTHH:mm:ss and I need to strip the seconds from it.

Here is a sample razor pages app :

Project.csproj

<ItemGroup>
  <PackageReference Include="JetBrains.Annotations" Version="2024.2.0" />
  <PackageReference Include="NodaTime" Version="3.1.12" />
</ItemGroup>

CustomLocalDateTimeConverter.cs

// TypeConverterBase is coming from
// https://github.com/nodatime/nodatime/blob/main/src/NodaTime/Text/TypeConverterBase.cs
public class CustomLocalDateTimeConverter : TypeConverterBase<LocalDateTime>
{
    public CustomLocalDateTimeConverter()
      : base(LocalDateTimePattern.CreateWithInvariantCulture("uuuu-MM-dd'T'HH:mm")) { }
}

Program.cs

TypeDescriptor.AddAttributes(typeof(LocalDateTime),
    new TypeConverterAttribute(typeof(CustomLocalDateTimeConverter)));

Index.cs

public class IndexModel : PageModel
{
    [BindProperty]
    public LocalDateTime Local { get; set; } = new LocalDateTime(2024, 09, 11, 12, 12);
    
    public void OnPost()
    {
            
    }
}

Index.cs.cshtml

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

With this in place, if you select a value from the local-datetime input and send the form back to the server, the LocalDateTime will be parsed with the CustomLocalDateTimeConverter.

But when you load the page, the asp-for tag helper is rendering the date like this instead of showing the default date from Index.cshtml.cs:

LocalDateTime input

And the generated html code is

<form method="post">
    <input type="text"
           data-val="true"
           data-val-required="The Local field is required."
           id="Local"
           name="Local"
           value="11/09/2024 12:12:00">
    <button type="submit">Send</button>
    <input name="__RequestVerificationToken" type="hidden" value="...">
</form>

The value attribute is not respecting the format, it looks like ToString() was used instead of our CustomLocalDateTimeConverter


Solution

  • This may or may not be the best way of doing it, but it's fairly simple to specify the format used in an <input> element, using the Format property:

    <input asp-for="Local" type="datetime-local" asp-format="{0:uuuu-MM-dd'T'HH:mm}" />
    

    Note that I expect this will still use "the current culture" instead of an invariant culture pattern, which is definitely not ideal, but is unlikely to make any difference with the above pattern. (The LocalDateTime "knows" its own calendar system, so it's not going to affect that, for example.)

    You may well still need the answers from the earlier version of the question (e.g. specifying a [TypeConverter] attribute on the property) in order to make sure that the value is then parsed correctly on submission.