38

I'm using JSON.NET to serialize DateTime values directly from object instances (not using DateTime.ToString() with formatter).

Is there a way to force JSON.NET to include milliseconds in the serialization even if the millisecond component of the DateTime is zero?

Background: I have a very slow web service consumer for this JSON endpoint. Conditional logic is expensive for the consumer, so I'd like to provide the same data formatting every time.

Alex
  • 75,813
  • 86
  • 255
  • 348
  • 1
    it might be a good idea to explain the set of circumstances that lead you to wanting to do this – spender Aug 12 '13 at 17:45

3 Answers3

62

We ran into this same issue on my current project. We are using Web API (and hence JSON.Net) to implement a REST API. We discovered that, when serializing DateTime objects, JSON.Net omits the trailing zeros from the milliseconds, or omits the milliseconds from the date entirely if it is zero. Our clients were expecting a fixed-length date-time string, with exactly 3 digits for the milliseconds. We fixed it by doing the following in Application_Start():

JsonSerializerSettings settings = HttpConfiguration.Formatters.JsonFormatter.SerializerSettings;
IsoDateTimeConverter dateConverter = new IsoDateTimeConverter 
{ 
    DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff'Z'" 
};
settings.Converters.Add(dateConverter);

If you're not using Web API, you can do the same thing by creating a new instance of JsonSerializerSettings, adding the IsoDateTimeConverter to it as shown above, then passing the serializer settings to JsonConvert.SerializeObject().

Note: If you're serializing a DateTimeOffset or a local DateTime and you want to include the timezone offset, replace the quoted 'Z' in the above format with an unquoted K. See Custom Date and Time Format Strings in the documentation for more info.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • 1
    Your approach with my performance code actually sits at about a 153ms to parse. It seems that performance is pretty important, that's why I bring it up. They say that a person notices a delay after about 200ms, so you only end up having about 43ms left to account for network usage, database access, etc. – Kelly Elton Aug 12 '13 at 20:50
  • 3
    My answer simply attempts to address the OP's question about how to get JSON.Net to always include the milliseconds when formatting dates. It does not make a performance claim of any kind. – Brian Rogers Aug 13 '13 at 03:13
  • Yeah that's fine, I'm just adding information that might help the OP or someone along the way later on. – Kelly Elton Aug 13 '13 at 07:22
  • This helped, thanks. I'm not sure why there are single quotes in the format string, though. I used `"yyyy-MM-ddTHH:mm:ssZ"`. – jrummell Oct 15 '15 at 14:21
  • 3
    @jrummell The single quotes denote the start and end of a literal character within the format, i.e. those that are not intended to be replaced with a part of the date. Here, it doesn't really matter because none of the literal characters `-`, `T`, `:`, and `Z` can be confused with formatting specifier characters. On the other hand, if you wanted to put a literal `M`, for example, in the format somewhere you would definitely need to quote it. See [Custom Date and Time Format Strings](https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx) in the documentation for more info. – Brian Rogers Oct 15 '15 at 14:36
  • 4
    @jrummel: The literal delimiters could matter even in this case. For example, if your current culture was *"it-IT"* then your time portion would be converted to something like *"12.31.42"* if you didn't treat the *":"* separator as a literal by quoting it. – LukeH Dec 12 '15 at 00:32
  • This was helpful. Fix the issue I was having with angular uib-datepicker. Thanks @BrianRogers – John Gathogo May 09 '16 at 13:01
  • @BrianRogers This resolved my issue with formatting `DateTime` properties on GETs, but now if I attempt to POST a viewmodel with a DateTime property and the value I'm submitting is eg. `2017-03-01`, it will fail to bind the model completely and the viewmodel will be null. Any way around this? – mellis481 Mar 22 '17 at 13:07
  • @im1dermike - You should [post a new question](http://stackoverflow.com/questions/ask) detailing the problem you are having, including code if possible. The comments section isn't the appropriate place for Q&A. In the new question you can link back to this one one if it helps to provide context. – Brian Rogers Mar 22 '17 at 13:58
  • Thanks. I added your code into WebApiConfig.Register(HttpConfiguration config) method as a single line like this: config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fff" }); – Oncel Umut TURER Jun 30 '17 at 11:43
0

For any Java people finding this answer and wanting to parse such .NET default-formatted timestamps, I wanted to note a Java 8 (java.time.*) approach for parsing the format:

public static final DateTimeFormatter FORMATTER =
    new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss")
                                  .appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
                                  .toFormatter();
...
LocalDateTime ldt = LocalDateTime.parse(dateString, FORMATTER);

This says to expect 0-3 digits of fractional milliseconds, with true for an expected decimal point. It correctly interprets, say, ".47" as 470 milliseconds.

dbreaux
  • 4,982
  • 1
  • 25
  • 64
0

I encountered this issue when working with JsonWriter and needed to resolve it because we observed that Javascript date libraries such as Day.js prefer milliseconds to have 3 digits. This question led me to the resolution, so I thought to share it in case others have a use-case like mine:

StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (JsonWriter writer = new JsonTextWriter(sw)) {
    writer.Formatting = Newtonsoft.Json.Formatting.None;
    writer.DateFormatString="yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff''";
    writer.WriteStartObject();

    DateTime? date = new DateTime(2021,12,30,23,59,40,250);
    writer.WritePropertyName("Date1");
    writer.WriteValue(date);

    date = new DateTime(2021, 12, 30, 23, 59, 40, 555);
    writer.WritePropertyName("Date2");
    writer.WriteValue(date);

    date = new DateTime(2021, 12, 30, 23, 59, 40, 0);
    writer.WritePropertyName("Date3");
    writer.WriteValue(date);

    date = null;
    writer.WritePropertyName("DateNULL");
    writer.WriteValue(date);
    writer.WriteEndObject();
}
Console.WriteLine(sb.ToString());

This produces:

{
    "Date1": "2021-12-30T23:59:40.250",
    "Date2": "2021-12-30T23:59:40.555",
    "Date3": "2021-12-30T23:59:40.000",
    "DateNULL": null
}

The important line I needed to add was:

writer.DateFormatString="yyyy'-'MM'-'dd'T'HH':'mm':'ss.fff''";
Elliveny
  • 2,159
  • 1
  • 20
  • 28