Modifying Date Format for JSON Serialization in C#

By FoxLearn 2/5/2025 7:42:22 AM   317
When serializing a DateTime object using the System.Text.Json library in C#, the default date format used is the ISO-8601 standard (e.g., 2022-01-31T13:15:05.2151663-05:00).

This format is controlled internally by the DateTimeConverter class, and there is no built-in way to customize the date format directly.

To change the date format, you need to implement a custom JSON converter and register it with the serialization options.

using System.Text.Json;

var options = new JsonSerializerOptions() { WriteIndented = true };
options.Converters.Add(new CustomDateTimeConverter("yyyy-MM-dd"));

var person = new Person() { BirthDate = new DateTime(1856, 7, 10) };

var json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);

This code snippet will output the following JSON:

{
  "BirthDate": "1856-07-10"
}

In the above example, we define a custom converter CustomDateTimeConverter to format the DateTime object as a string in a custom format:

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);
    }
}

This converter serializes the DateTime object as a string in the specified format and deserializes it back into a DateTime object from the string.

If you're also working with DateTimeOffset, a separate converter will be needed for that type. You can extend the approach using a JsonConverterFactory to create converters for both DateTime and DateTimeOffset.

Using Newtonsoft to Change the Date Format

When using the Newtonsoft JSON library (Json.NET), changing the date format is much easier. You can adjust the format by setting the DateFormatString property:

using Newtonsoft.Json;

var settings = new JsonSerializerSettings() { DateFormatString = "yyyy-MM-dd" };

var person = new Person() { BirthDate = new DateTime(1856, 7, 10) };

var json = JsonConvert.SerializeObject(person, Formatting.Indented, settings);
Console.WriteLine(json);

This will produce the following JSON output:

{
  "BirthDate": "1856-07-10"
}

Handling DateOnly and TimeOnly in .NET 7

Starting with .NET 7, System.Text.Json natively supports DateOnly and TimeOnly types. Here's an example demonstrating serialization and deserialization of these types:

using System.Text.Json;

var activity = new Activity()
{
    Date = new DateOnly(2022, 1, 31),
    Time = new TimeOnly(14, 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}");

This will output:

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

Handling DateOnly and TimeOnly with Custom Converters

Before .NET 7, System.Text.Json didn’t support DateOnly and TimeOnly. To handle these types, custom converters were necessary. Here’s an example of how to create custom converters:

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(2022, 1, 31),
    Time = new TimeOnly(14, 39)
};

var json = JsonSerializer.Serialize(activity, options);
Console.WriteLine(json);

The output will be:

{
  "Date": "2022-01-31",
  "Time": "14:39"
}

Here are the custom converters for DateOnly and TimeOnly:

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 time, JsonSerializerOptions options)
    {
        writer.WriteStringValue(time.ToString(Format));
    }

    public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return TimeOnly.ParseExact(reader.GetString(), Format);
    }
}

Using Newtonsoft with DateOnly and TimeOnly

Starting with version 13.0.2, Newtonsoft supports DateOnly and TimeOnly.

using Newtonsoft.Json;

var activity = new Activity()
{
    Date = new DateOnly(2022, 1, 31),
    Time = new TimeOnly(14, 39)
};

var json = JsonConvert.SerializeObject(activity, Formatting.Indented);
Console.WriteLine("Serialized DateOnly/TimeOnly properly");
Console.WriteLine(json);

var newActivity = JsonConvert.DeserializeObject<Activity>(json);
Console.WriteLine("Deserialized DateOnly/TimeOnly");
Console.WriteLine($"DateOnly={newActivity.Date} TimeOnly={newActivity.Time}");

This produces the following output:

Serialized DateOnly/TimeOnly properly
{
  "Date": "2022-01-31",
  "Time": "14:39"
}
Deserialized DateOnly/TimeOnly
DateOnly=1/31/2022 TimeOnly=2:39 PM

Before v13.0.2: In earlier versions, Newtonsoft didn’t handle DateOnly and TimeOnly well, often serializing DateOnly as a complex object instead of a date string.

For example:

{
  "BirthDate": {
    "Year": 1856,
    "Month": 7,
    "Day": 10,
    "DayOfWeek": 4,
    "DayOfYear": 192,
    "DayNumber": 677715
  }
}

This wasn’t a desirable format for serialization and deserialization, and custom converters were required to handle it correctly.