Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Json.Net deserialising DateTimes inconsistently

I am having trouble deserialising datetimes with Json.Net 6.0.3 (I can replicate the issue on 6.0.6). The code is run in .Net 4.5 on Windows 8.1, and the culture is en-GB.

This demonstrates the problem:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

var d1 = new DateTimeOffset(2014, 12, 15, 18, 0, 0, TimeSpan.FromHours(1));
var obj = new {
    time = d1
};

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

var jo = JObject.Parse(json);

Console.WriteLine(jo.Value<string>("time") + " // jo.Value<string>(\"time\")");
Console.WriteLine(jo["time"] + " // jo[\"time\"]");

the output:

{
    "time": "2014-12-15T18:00:00+01:00"
}
12/15/2014 17:00:00 // jo.Value<string>("time")
15/12/2014 17:00:00 // jo["time"]

The date times are different depending on how the JObject is accessed - one is MM/DD/YYYY DD/MM/YYYY. Why is this?

I don't need them to be in a specific format: the problem is that the format changes. I have a lot of legacy code that parses the datetime string sourced from Json.Net. The code will also run on different computers around the world, maybe with different cultures.

Is there a way to make Json.Net always return datetimes in the same format?

like image 340
rikkit Avatar asked Dec 17 '14 15:12

rikkit


1 Answers

The issue is that the two lines do different things:

jo["time"]

Is (ultimately) writing an actual DateTime value to the console. As @JonSkeet points out, you're actually writing a JValue to the console--JValue.ToString just calls the wrapped value's ToString method though, which in your case is DateTime.ToString().

In other words:

  • Calling Console.WriteLine and passing an instance of JValue uses the overload of Console.WriteLine that takes an object.
  • That overload calls .ToString() on the JValue
  • Which then calls .ToString() on the underlying type (in your case a DateTime)

So you're going to get whatever format is the default for your current culture. More specifically, you're going to get the DateTime formatted with the "G" specifier in your current culture.

The more interesting bit is the jo.Value<string>("time") line. Under the hood, JSON.NET is converting the underlying DateTime to a string using the following:

Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture);

This, of course, produces an entirely different string, since it's explicitly using CultureInfo.InvariantCulture.

The takeaway from this is that your best bet is probably to retrieve the date as a DateTime, and then format it the exact way you want it, to avoid any ambiguity:

DateTime dt = jo.Value<DateTime>("time");
string dateTimeSTring = dt.ToString(/* whatever format you always want */);
like image 143
Andrew Whitaker Avatar answered Oct 14 '22 10:10

Andrew Whitaker