In regular .net, If we have a time that has DateTimeKind.Unspecified If we convert ToLocal -- it assumes the input date is UTC when converting. If we convert ToUniversal -- it assumes the input date is local when converting
However, in JSON.Net, if our string date in JSON.Net is unspecified, it doesn't seem to have this logic? Look at my test cases below - am I doing some thing wrong? Or is this by design? or a bug 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);
}
JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).
Specifies the settings on a JsonSerializer object. Newtonsoft.Json.
The System. Text. Json namespace provides functionality for serializing to and deserializing from JavaScript Object Notation (JSON). Serialization is the process of converting the state of an object, that is, the values of its properties, into a form that can be stored or transmitted.
There is a common step when writing an API that returns a JSON formatted file, which is the deserialization of the data content which can cause slowness.
I am not 100% sure what you are looking for here, but I think it is not safe to assume that JSON.Net will meet all of your needs without a little help. As Mr. Newton says:
Dates in JSON are hard.
The first thing is to determine whether or you want to support accepting unspecified dates or whether you are going to assume that all incoming dates are universal, even if they are missing the trailing Z.
If you assume that all incoming dates are universal, you can just see if they have a trailing 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 does require the dates you are testing for to be modified to be Utc.
If you do not want to assume that incoming dates are universal, but instead treat them as unspecified, you need to change the way that you are converting the incoming JSON by 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 unspecified date that matches the dateString exactly. Here is where your helping hand comes into play:
if (dateSerialized.Kind == DateTimeKind.Unspecified)
{
dateSerialized = dateSerialized.ToUniversalTime();
}
This means that the complete, revised first test will look like the following 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With