Formatting DateTime in ASP.NET Core 3.0 using System.Text.Json

43,263

Solution 1

Solved with a custom formatter. Thank you Panagiotis for the suggestion.

public class DateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        Debug.Assert(typeToConvert == typeof(DateTime));
        return DateTime.Parse(reader.GetString());
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
    }
}


// in the ConfigureServices()
services.AddControllers()
    .AddJsonOptions(options =>
     {
         options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
     });

Solution 2

Migrating to Core 3 I had to replace System.Text.Json to use Newtonsoft again by :

services.AddControllers().AddNewtonsoftJson();

But I was having same issue with UTC dates in an Angular app and I had to add this to get dates in UTC:

services.AddControllers().AddNewtonsoftJson(
       options => options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc);

In your case you should be able to do this:

services.AddControllers().AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
        options.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
    });

It works and I hope it helps...

Solution 3

This dumpster fire of asp.net core date serialization/deserialization is maybe easier to understand when you see the dumpster fire of Date.Parse() and Date.ParseExact(). We're passing dates to and from javascript, so we don't want to be formatting. We just want to transparently serialize and deserialize between DateTime and ISO 8601 in UTC.

That this is not the default, and that there's no easy configuration option, and that the solution is so funky and fragile, is credibility-destroying. This is currently what's working for me, based on D.English's answer for writing, and the linked answer for reading, and using this answer to access the JsonDocument correctly...

Update this is for the dumptser fire of model binding. For the dumpster fire of query string parsing, it's over here

// in Startup.cs ConfigureServices()

services.AddMvc().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new UtcDateTimeConverter());
});


public class BobbyUtcDateTimeConverter : JsonConverter<DateTime>
{
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using (var jsonDoc = JsonDocument.ParseValue(ref reader))
        {
            var stringValue = jsonDoc.RootElement.GetRawText().Trim('"').Trim('\'');
            var value = DateTime.Parse(stringValue, null, System.Globalization.DateTimeStyles.RoundtripKind);
            return value;
        }
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture));
    }
}
Share:
43,263
Admin
Author by

Admin

Updated on July 31, 2022

Comments

  • Admin
    Admin almost 2 years

    I am migrating a web API from .NET Core 2.2 to 3.0 and want to use the new System.Text.Json. When using Newtonsoft I was able to format DateTime using the code below. How can I accomplish the same?

    .AddJsonOptions(options =>
        {
            options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
            options.SerializerSettings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ";
        });
    
  • Carlos Muñoz
    Carlos Muñoz over 3 years
    There are two problems with this code. 1) NewtonSoft.Json doesn't always call .ToUniversalTime() on value. It depends on its DateTimeKind. The provided format string strips down the 7 decimal precision that NewtonSoft.Json mantains. I can update your answer with the correct code if you are ok with it. Otherwise i can create a new answer with the correct code.
  • mkb
    mkb over 3 years
    the thing is question is about System.Text.Json not NewtonSoft.Json
  • mkb
    mkb over 3 years
    Since I'am already using UTC in client code too I exluded ToUniversalTime(), well all I needed was the Z at the end of the date string and this accoplished that, this is the correct approach I guess?!. also apparantly reader has a method reader.GetDateTime() which can be used in Read method
  • Anton Shakalo
    Anton Shakalo almost 3 years
    question was not about newtonsoft.json
  • Necip Sunmaz
    Necip Sunmaz over 2 years
    This is a good solution for problem. Thanks mate you saved my day.
  • AntonIva
    AntonIva over 2 years
    I will accept this answer, kk. Thanks
  • bbsimonbb
    bbsimonbb over 2 years
    15 lines of code, a fragile modification in Startup, just to have a complete unambiguous date in my API output. This is credibility-affecting.
  • bbsimonbb
    bbsimonbb over 2 years
    Beware the timezone! The above code will assume your C# DateTime is in local time, and will convert it to UTC before serializing as UTC. If, to stay sane, all your dates are already UTC, an unwanted timezone delta will sneak in at this step. (To fix this, just delete ToUniversalTime() in the Write() method.)
  • Jonathan Allen
    Jonathan Allen about 2 years
    DateTime.SpecifyKind doesn't work. If you round-trip a date, it will be off by your local time zone.