Serialize / Deserialize json.net datetime 'unspecified'

In regular .net, If we have time with DateTimeKind.Unspecified If we convert ToLocal - it is assumed that the input date is UTC when converting. If we convert ToUniversal - it is assumed that the input date is local when converting

However, in JSON.Net, if our string date is not specified in JSON.Net, does it seem to have no such logic? Look at my test cases below - am I doing something wrong? Or is it design? or an error in JSON.Net? Thanks!

// TODO: This Fails with output // date string: "2014-06-02T21:00:00.0000000" // date serialized: 2014-06-02T21:00:00.0000000Z // Expected date and time to be <2014-06-03 04:00:00>, but found <2014-06-02 21:00:00>. [TestMethod] public void NEW_Should_deserialize_unspecified_datestring_to_utc_date() { string dateString = "\"2014-06-02T21:00:00.0000000\""; DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); Console.WriteLine("date string: " + dateString); Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); dateSerialized.Kind.Should().Be(DateTimeKind.Utc); dateSerialized.Should().Be(dateRaw.ToUniversalTime()); dateSerialized.Should().Be(dateRawAsUtc); } // TODO: This Fails with output // date string: "2014-06-02T21:00:00.0000000" // date serialized: 2014-06-02T21:00:00.0000000-07:00 // Expected date and time to be <2014-06-02 14:00:00>, but found <2014-06-02 21:00:00>. [TestMethod] public void NEW_Should_deserialize_unspecified_datestring_to_local_date() { string dateString = "\"2014-06-02T21:00:00.0000000\""; DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); DateTime dateRawAsLocal = new DateTime(2014, 6, 2, 14, 0, 0, 0, DateTimeKind.Local); dateRawAsLocal.Should().Be(dateRaw.ToLocalTime()); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Local; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); Console.WriteLine("date string: " + dateString); Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); dateSerialized.Kind.Should().Be(DateTimeKind.Local); dateSerialized.Should().Be(dateRaw.ToLocalTime()); dateSerialized.Should().Be(dateRawAsLocal); } [TestMethod] public void NEW_Should_deserialize_unspecified_datestring_to_unspecified_date() { string dateString = "\"2014-06-02T21:00:00.0000000\""; // unspecified, does not have the 'Z' DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); Console.WriteLine("date string: " + dateString); Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); dateSerialized.Kind.Should().Be(DateTimeKind.Unspecified); dateSerialized.Should().Be(dateRaw); } 
+6
source share
1 answer

I am not 100% sure what you are looking for here, but I think it is unsafe to assume that JSON.Net will satisfy all your needs without a little help. Like Mr. Newton says :

Dates in JSON are difficult.

Firstly, you need to determine whether you want to support receiving unspecified dates or whether you think that all incoming dates are universal, even if they do not have a trailing Z.

If you assume that all incoming dates are universal, you can just see if they have a final Z and, if not, add it (not exactly production code, but you get the idea):

 if (!dateString.EndsWith("Z\"", StringComparison.InvariantCultureIgnoreCase)) { dateString = dateString.Substring(0, dateString.LastIndexOf("\"", StringComparison.InvariantCultureIgnoreCase)) + "Z\""; } 

This change in assumption requires that the dates you test for modification be Utc.

If you do not want to assume that the incoming dates are universal, but instead consider them unspecified, you need to change the way the incoming JSON is converted, replacing:

 JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; settings.DateFormatHandling = DateFormatHandling.IsoDateFormat; DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, settings); 

with:

 var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); 

This will result in an undefined date that exactly matches dateString. This is where your hand helps:

 if (dateSerialized.Kind == DateTimeKind.Unspecified) { dateSerialized = dateSerialized.ToUniversalTime(); } 

This means that the full, revised first test will look like this and it will pass:

  string dateString = "\"2014-06-02T21:00:00.0000000\""; DateTime dateRaw = new DateTime(2014, 6, 2, 21, 0, 0, 0, DateTimeKind.Unspecified); DateTime dateRawAsUtc = new DateTime(2014, 6, 3, 4, 0, 0, 0, DateTimeKind.Utc); dateRawAsUtc.Should().Be(dateRaw.ToUniversalTime()); var oConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter(); DateTime dateSerialized = JsonConvert.DeserializeObject<DateTime>(dateString, oConverter); if (dateSerialized.Kind == DateTimeKind.Unspecified) { dateSerialized = dateSerialized.ToUniversalTime(); } Console.WriteLine("date string: " + dateString); Console.WriteLine("date serialized: " + dateSerialized.ToString("o")); dateSerialized.Kind.Should().Be(DateTimeKind.Utc); dateSerialized.Should().Be(dateRaw.ToUniversalTime()); dateSerialized.Should().Be(dateRawAsUtc); 
+2
source

Source: https://habr.com/ru/post/978806/


All Articles