The new datatypes of DateOnly and TimeOnly are a good addition to C# but it feels as if they were released too soon without proper integration with other features. An example of this is their integration with API controllers, when added as a datatype to an input or output DTO the controller and other related services like Swagger expect them to be provided as an object rather than as a string.

In order to allow the API to accept a date in the expected format “2022-08-24” and for Swagger to display the examples correctly it’s necessary to first add a converter.

public sealed class DateOnlyJsonConverter : JsonConverter<DateOnly>
{
    public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateOnly.FromDateTime(reader.GetDateTime());
    }

    public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
    {
        var isoDate = value.ToString("O");
        writer.WriteStringValue(isoDate);
    }
}
public sealed class NullableDateOnlyJsonConverter : JsonConverter<DateOnly?>
{
    public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TryGetDateTime(out DateTime dateTime))
            return DateOnly.FromDateTime(dateTime);
        else
            return null;
    }

    public override void Write(Utf8JsonWriter writer, DateOnly? value, JsonSerializerOptions options)
    {
        if (value == null)
            writer.WriteNullValue();
        else
            writer.WriteStringValue(((DateOnly)value).ToString("O"));
    }
}

These then need to be added to your service collection in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddJsonOptions(x =>
    {
        // serialize DateOnly as strings
        x.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
        x.JsonSerializerOptions.Converters.Add(new NullableDateOnlyJsonConverter());
    });
}

This will allow your API controllers to work properly with DateOnly datatypes now but if you want Swagger to work as expected you’ll need to update the mappings there as well.

services.AddSwaggerGen(c =>
{
    c.MapType<DateOnly>(() => new OpenApiSchema
        {
            Type = "string",
            Format = "date"
        });
});

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published.