C# – Changing the JSON serialization date format

When you serialize a date with System.Text.Json, it uses the standard ISO-8601 date format (ex: “2022-01-31T13:15:05.2151663-05:00”). Internally, it uses the DateTimeConverter class for handling DateTime, which doesn’t give you a way to change the date format.

To change the date format, you have to create a custom converter and pass it in:

using System.Text.Json; var options = new JsonSerializerOptions() { WriteIndented = true }; options.Converters.Add(new CustomDateTimeConverter("yyyy-MM-dd")); var nikolaTesla = new Person() { BirthDate = new DateTime(year: 1856, month: 7, day: 10) }; var json = JsonSerializer.Serialize(nikolaTesla, options); Console.WriteLine(json);
Code language: C# (cs)

Here’s the custom converter class:

using System.Text.Json; using System.Text.Json.Serialization; public class CustomDateTimeConverter : JsonConverter<DateTime> { private readonly string Format; public CustomDateTimeConverter(string format) { Format = format; } public override void Write(Utf8JsonWriter writer, DateTime date, JsonSerializerOptions options) { writer.WriteStringValue(date.ToString(Format)); } public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return DateTime.ParseExact(reader.GetString(), Format, null); } }
Code language: C# (cs)

Note: If you need to deal with DateTimeOffset as well, you’ll need another custom converter. Consider using JsonConverterFactory in that scenario.

Running the code above generates the following JSON with the custom date format:

{ "BirthDate": "1856-07-10" }
Code language: JSON / JSON with Comments (json)

Newtonsoft – Change date format through settings

It’s much simpler to change the date format when you’re using Newtonsoft. By default, it uses the ISO-8601 date format, but you can change it by setting the DateFormatString setting:

using Newtonsoft.Json; var settings = new JsonSerializerSettings() { DateFormatString = "yyyy-MM-dd" }; var nikolaTesla = new Person() { BirthDate = new DateTime(year: 1856, month: 7, day: 10) }; var json = JsonConvert.SerializeObject(nikolaTesla, Formatting.Indented, settings); Console.WriteLine(json);
Code language: C# (cs)

This outputs the following JSON:

{ "BirthDate": "1856-07-10" }
Code language: JSON / JSON with Comments (json)

Handling DateOnly and TimeOnly

Starting in .NET 7, System.Text.Json supports (de)serialization of DateOnly/TimeOnly properties. Here’s an example:

using System.Text.Json; var activity = new Activity() { Date = new DateOnly(year: 2022, month: 1, day: 31), Time = new TimeOnly(hour: 14, minute: 39) }; var json = JsonSerializer.Serialize(activity, new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine($"Serialized DateOnly/TimeOnly={json}"); var newActivity = JsonSerializer.Deserialize<Activity>(json); Console.WriteLine($"Deserialized DateOnly={newActivity.Date} TimeOnly={newActivity.Time}");
Code language: C# (cs)

This outputs the following, showing that it properly handles (de)serializing DateOnly/TimeOnly properties:

Serialized DateOnly/TimeOnly={ "Date": "2022-01-31", "Time": "14:39:00" } Deserialized DateOnly=1/31/2022 TimeOnly=2:39 PM
Code language: plaintext (plaintext)

Before .NET 7 – Handling DateOnly/TimeOnly with System.Text.Json custom converters

The DateOnly/TimeOnly types were not initially supported by System.Text.Json when they were introduced in .NET 6. When you tried to use these, you’d get an exception:

System.NotSupportedException: Serialization and deserialization of ‘System.DateOnly’ instances are not supported.

To be able to handle DateOnly and TimeOnly, you’d have to create and use custom converters, like this:

using System.Text.Json; var options = new JsonSerializerOptions() { WriteIndented = true }; options.Converters.Add(new CustomDateOnlyConverter("yyyy-MM-dd")); options.Converters.Add(new CustomTimeOnlyConverter("HH:mm")); var activity = new Activity() { Date = new DateOnly(year: 2022, month: 1, day: 31), Time = new TimeOnly(hour: 14, minute: 39) }; var json = JsonSerializer.Serialize(activity, options); Console.WriteLine(json);
Code language: C# (cs)

This outputs the following JSON:

{ "Date": "2022-01-31", "Time": "14:39" }
Code language: JSON / JSON with Comments (json)

Here are the DateOnly and TimeOnly custom converter classes:

using System.Text.Json; using System.Text.Json.Serialization; public class CustomDateOnlyConverter : JsonConverter<DateOnly> { private readonly string Format; public CustomDateOnlyConverter(string format) { Format = format; } public override void Write(Utf8JsonWriter writer, DateOnly date, JsonSerializerOptions options) { writer.WriteStringValue(date.ToString(Format)); } public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return DateOnly.ParseExact(reader.GetString(), Format); } } public class CustomTimeOnlyConverter : JsonConverter<TimeOnly> { private readonly string Format; public CustomTimeOnlyConverter(string format) { Format = format; } public override void Write(Utf8JsonWriter writer, TimeOnly date, JsonSerializerOptions options) { writer.WriteStringValue(date.ToString(Format)); } public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return TimeOnly.ParseExact(reader.GetString(), Format); } }
Code language: C# (cs)

Newtonsoft and DateOnly/TimeOnly properties

Starting with v13.0.2, Newtonsoft handles (de)serialization of DateOnly/TimeOnly properly. Here’s an example:

using Newtonsoft.Json; var activity = new Activity() { Date = new DateOnly(year: 2022, month: 1, day: 31), Time = new TimeOnly(hour: 14, minute: 39) }; var json = JsonConvert.SerializeObject(activity, Formatting.Indented); Console.WriteLine("Newtonsoft serializes DateOnly/TimeOnly properly now"); Console.WriteLine(json); var newActivity = JsonConvert.DeserializeObject<Activity>(json); Console.WriteLine("Newtonsoft can deserialize DateOnly/TimeOnly"); Console.WriteLine($"DateOnly={newActivity.Date} TimeOnly={newActivity.Time}");
Code language: C# (cs)

This outputs the following, showing that Newtonsoft handles TimeOnly/DateOnly as expected:

Newtonsoft serializes DateOnly/TimeOnly properly now { "Date": "2022-01-31", "Time": "14:39" } Newtonsoft can deserialize DateOnly/TimeOnly DateOnly=1/31/2022 TimeOnly=2:39 PM
Code language: plaintext (plaintext)

Before v13.0.2 – Newtonsoft doesn’t handle DateOnly / TimeOnly well

When DateOnly/TimeOnly were first introduced in .NET 6, Newtonsoft didn’t handle them properly. Unlike System.Text.Json, Newtonsoft attempted to handle DateOnly/TimeOnly without throwing an exception, but the results were undesirable. Here’s an example to see what I mean:

using Newtonsoft.Json; var nikolaTesla = new Person() { BirthDate = new DateOnly(year: 1856, month: 7, day: 10) }; var json = JsonConvert.SerializeObject(nikolaTesla, Formatting.Indented); Console.WriteLine(json);
Code language: C# (cs)

This outputs the following JSON:

{ "BirthDate": { "Year": 1856, "Month": 7, "Day": 10, "DayOfWeek": 4, "DayOfYear": 192, "DayNumber": 677715 } }
Code language: JSON / JSON with Comments (json)

It’s really just serializing all of the public DateOnly properties. I don’t think anyone would want to receive a date like this. Furthermore, it wasn’t able to deserialize this back into a DateOnly property (because none of the properties have public setters). Before the latest version that supports DateOnly/TimeOnly, you’d need to write a custom converter (same approach as System.Text.Json) to handle DateOnly / TimeOnly properly.

Leave a Comment