Home > Back-end >  Is NodaTime too strict to be used in requst DTOs for web APIs?
Is NodaTime too strict to be used in requst DTOs for web APIs?

Time:01-17

So, I'm developing an API that takes an object which includes datetime properties(ISO 8601).

However, how forgiving should you be when parsing something as ISO 8601? Ie. an input of '2023-01-01T00:00Z' will parse OK with DateTimeOffset, but not when using OffsetDateTime from NodaTime, as it is missing seconds.

I've looked into the OffsetDateTimePattern class, and cannot find any information on this topic. I also tried (and failed) to define a custom pattern that has optional seconds, but I'm not sure if this is idiomatic nodatime usage anyway. I couldn't find any description on how to do this in the documentation.

So, should I use BCL DateTimeOffset for any exposed data type (request/response DTOs), or can I tweak the NodaTime types to be more forgiving?

CodePudding user response:

So, should I use BCL DateTimeOffset for any exposed data type (request/response DTOs), or can I tweak the NodaTime types to be more forgiving?

You can absolutely create patterns which will allow this. We fairly recently created a change for LocalDateTimePattern to expose such a pattern directly. We haven't (yet) done the same for OffsetDateTimePattern, but you can create your own pattern that does this easily enough, with very similar code. I'll admit it's slightly fiddly, but it should do the job.

using NodaTime;
using NodaTime.Text;

IPattern<OffsetDateTime> pattern = new CompositePatternBuilder<OffsetDateTime>
{
    { OffsetDateTimePattern.ExtendedIso, value => true },
    { OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mmo<G>"),
        value => value.Second == 0 && value.NanosecondOfSecond == 0 },
    { OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HHo<G>"),
        value => value.Minute == 0 && value.Second == 0 && value.NanosecondOfSecond == 0 },
}.Build();

string[] examples =
{
    "2023-01-01T01Z",
    "2023-01-01T01:23Z",
    "2023-01-01T01:23:45Z",
    "2023-01-01T01:23:45.678Z",
    "2023-01-01T01:23:45.678901Z",
    "2023-01-01T01:23:45.678901234Z",
};

foreach (var example in examples)
{
    Console.WriteLine($"{example}: {pattern.Parse(example).Success}");
}

Once you've created an appropriate IPattern<OffsetDateTime>, you may need to configure serialization to use that instead of the default configured pattern. It's hard to provide sample code for that without knowing exactly what's doing the parsing, but this code in NodaTime.Serialization.JsonNet and the equivalent for NodaTime.Serialization.SystemTextJson may be useful.

This will all be a little easier after this PR is finished and merged...

  • Related