214

I am developing an API to expose some data using ASP.NET Web API.

In one of the API, the client wants us to expose the date in yyyy-MM-dd format. I don't want to change the global settings (e.g. GlobalConfiguration.Configuration.Formatters.JsonFormatter) for that since it is very specific to this client. And I do developing that in a solution for multiple clients.

One of the solution that I could think of is to create a custom JsonConverter and then put that to the property I need to do the custom formatting

e.g.

class ReturnObjectA 
{
    [JsonConverter(typeof(CustomDateTimeConverter))]
    public DateTime ReturnDate { get;set;}
}

Just wondering if there is some other easy way of doing that.

3
  • 25
    For what it's worth, APIs are for computer readability, not user readability, so it's better to stick to a single specified date format, such as ISO 8601. If the client is directly displaying the API result to the user, or writing their own date parsing code for the API, then they're doing it wrong. Formatting a date for display should be left to the topmost UI layer. Commented Oct 15, 2013 at 18:14
  • Create web API by using Visual Studio 2019, fixed by Formatting DateTime in ASP.NET Core 3.0 using System.Text.Json Commented Feb 29, 2020 at 0:26
  • I steer clear of sprinkling Json.NET specifics into my DTOs. Instead, I have DTO date properties you mention as strings that are formatted with a shared const string value. Commented Oct 21, 2021 at 10:14

9 Answers 9

227

You are on the right track. Since you said you can't modify the global settings, then the next best thing is to apply the JsonConverter attribute on an as-needed basis, as you suggested. It turns out Json.Net already has a built-in IsoDateTimeConverter that lets you specify the date format. Unfortunately, you can't set the format via the JsonConverter attribute, since the attribute's sole argument is a type. However, there is a simple solution: subclass the IsoDateTimeConverter, then specify the date format in the constructor of the subclass. Apply the JsonConverter attribute where needed, specifying your custom converter, and you're ready to go. Here is the entirety of the code needed:

class CustomDateTimeConverter : IsoDateTimeConverter
{
    public CustomDateTimeConverter()
    {
        base.DateTimeFormat = "yyyy'-'MM'-'dd";
    }
}

If you don't mind having the time in there also, you don't even need to subclass the IsoDateTimeConverter. Its default date format is yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK (as seen in the source code).

Sign up to request clarification or add additional context in comments.

3 Comments

@Koen Zomers - The single quotes you removed from my date formats technically ARE correct, although they are not strictly necessary here. See Literal String Delimiters in the documentation for Custom Date and Time Format Strings. However, the format I quoted as the default format for the IsonDateTimeConverter was taken directly from the Json.Net source code; therefore I am reverting your edit on that.
it didn't work here with the quotes and it did without them, but if you say it should, I probably did something wrong. Sorry for the edit.
I know this is a 10-years old post, but I still want to mention it clearly. The single quotes is strict necessary. The date splitter is base on System Culture, in western nations, it should be -. Therefore, you won't find dirrerence between yyyy-MM-dd and yyyy'-'MM'-'dd. But in Asia, the date splitter could be /. In this case, the outcome of yyyy-MM-dd will be 2024/08/05, but the outcome of yyyy'-'MM'-'dd is still 2024-08-05.
179

You could use this approach:

public class DateFormatConverter : IsoDateTimeConverter
{
    public DateFormatConverter(string format)
    {
        DateTimeFormat = format;
    }
}

And use it this way:

class ReturnObjectA 
{
    [JsonConverter(typeof(DateFormatConverter), "yyyy-MM-dd")]
    public DateTime ReturnDate { get;set;}
}

The DateTimeFormat string uses the .NET format string syntax described here: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings

5 Comments

This doesn't work for me - I get 'JsonConverterAttribute' does not contain a constructor that takes 2 arguments
This is the most flexible solution. If you get the following error : 'JsonConverterAttribute' does not contain a constructor that takes 2 arguments, it means your version of json.net is too old. You need to update to the latest json.net version.
Works for me. Any idea how I can remove the time? So only return 2020-02-12 for example with the T00:00:00
@Enrico Simple: [JsonConverter(typeof(DateFormatConverter), "yyyy-MM-dd\"T:00:00:00\"")]
@TamCoton one of the reasons seeing this error might be that you are referencing "System.Text.Json` instead of ."Newtonsoft.Json" in your file. At least that was the issue in my case.
74

It can also be done with an IsoDateTimeConverter instance, without changing global formatting settings:

string json = JsonConvert.SerializeObject(yourObject,
    new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" });

This uses the JsonConvert.SerializeObject overload that takes a params JsonConverter[] argument.

1 Comment

If you are serializing same class object in many places, then accepted answer is better than this
26

Also available using one of the serializer settings overloads:

var json = JsonConvert.SerializeObject(someObject, new JsonSerializerSettings() { DateFormatString = "yyyy-MM-ddThh:mm:ssZ" });

Or

var json = JsonConvert.SerializeObject(someObject, Formatting.Indented, new JsonSerializerSettings() { DateFormatString = "yyyy-MM-ddThh:mm:ssZ" });

Overloads taking a Type are also available.

1 Comment

FYI I think you mean yyyy-MM-ddTHH:mm:ssZ .. 24 hour clock on the hours.
14

There is another solution I've been using. Just create a string property and use it for json. This property wil return date properly formatted.

class JSonModel {
    ...

    [JsonIgnore]
    public DateTime MyDate { get; set; }

    [JsonProperty("date")]
    public string CustomDate {
        get { return MyDate.ToString("ddMMyyyy"); }
        // set { MyDate = DateTime.Parse(value); }
        set { MyDate = DateTime.ParseExact(value, "ddMMyyyy", null); }
    }

    ...
}

This way you don't have to create extra classes. Also, it allows you to create diferent data formats. e.g, you can easily create another Property for Hour using the same DateTime.

Comments

11

With below converter

public class CustomDateTimeConverter : IsoDateTimeConverter
    {
        public CustomDateTimeConverter()
        {
            DateTimeFormat = "yyyy-MM-dd";
        }

        public CustomDateTimeConverter(string format)
        {
            DateTimeFormat = format;
        }
    }

Can use it with a default custom format

class ReturnObjectA 
{
    [JsonConverter(typeof(CustomDateTimeConverter))]
    public DateTime ReturnDate { get;set;}
}

Or any specified format for a property

class ReturnObjectB 
{
    [JsonConverter(typeof(CustomDateTimeConverter), "dd MMM yy")]
    public DateTime ReturnDate { get;set;}
}

Comments

5
public static JsonSerializerSettings JsonSerializer { get; set; } = new JsonSerializerSettings()
        {
            DateFormatString= "yyyy-MM-dd HH:mm:ss",
            NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = new LowercaseContractResolver()
        };

Hello,

I'm using this property when I need set JsonSerializerSettings

Comments

0

Some times decorating the json convert attribute will not work ,it will through exception saying that "2010-10-01" is valid date. To avoid this types i removed json convert attribute on the property and mentioned in the deserilizedObject method like below.

var addresss = JsonConvert.DeserializeObject<AddressHistory>(address, new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" });

Comments

0

If you are using Json.Net, you can create this converter class:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Api.Converters;


public class RequestDateTimeConverter : JsonConverter<DateTimeOffset>
{
    public string DateTimeFormat { get; set; } = "yyyy-MM-ddTHH:mm:ss";

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTimeOffset.ParseExact(reader.GetString()!, DateTimeFormat, null);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(DateTimeFormat));
    }
}

And use it like this in your Dto :

using System.Text.Json;
using System.Text.Json.Serialization;
using Api.Converters;


namespace Api.Dto;

public class MyRequestDto
{

    [JsonPropertyName("requestId")]
    public string RequestId { get;set;} = "1";


    [JsonConverter(typeof(RequestDateTimeConverter))]
    [JsonPropertyName("requestDateTime")]
    public DateTimeOffset RequestDateTime {get;set; } = DateTimeOffset.Now;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.