Proper Way to Convert JSON Date to .NET DateTime During Deserialization

Asked
Active3 hr before
Viewed126 times

7 Answers

properconvert
90%

I created a DTO with the exact same fields as the domain object, except that I made the date fields strings so they would deserialize. Now that I can deserialize it, I'll work on getting the dates into a valid format so I can create domain objects from my DTOs.,JavaScript (well, EcmaScript) defines its DateTime string interchange format based on a simplification of the ISO-8601 standard.,I've been doing some googling, and the above code is my latest attempt to make this work (using the TimeSpanJsonConverter from here). Other approaches show sending only a date to the server, but I have a list of objects that have dates as some properties.,XML Schema defines its DateTime string interchange format based on ISO-8601 also.

Old:

var specs = @Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(ViewBag.JobSpecEquipment))

New:

var specs = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.JobSpecEquipment));
load more v
88%

Utf8JsonReader parses DateTime and DateTimeOffset data:,The lower level Utf8JsonWriter writes DateTime and DateTimeOffset data:,DateTime and DateTimeOffset data can be serialized with JsonSerializer:,Used to format a DateTime or DateTimeOffset without fractional seconds but with a local offset.

DateTime and DateTimeOffset data can be serialized with JsonSerializer:

using System;
using System.Text.Json;

public class Example {
   private class Product {
      public string Name {
         get;
         set;
      }
      public DateTime ExpiryDate {
         get;
         set;
      }
   }

   public static void Main(string[] args) {
      Product p = new Product();
      p.Name = "Banana";
      p.ExpiryDate = new DateTime(2019, 7, 26);

      string json = JsonSerializer.Serialize(p);
      Console.WriteLine(json);
   }
}

// The example displays the following output:
// {"Name":"Banana","ExpiryDate":"2019-07-26T00:00:00"}
load more v
72%

/Date(1347992529530)/ is not a valid value for DateTime.,That exception happens when I call Deserialize() (third line in method below):,Dates look like this: Date(1348017917565),I took @Bob Horn answer but it wasn't working for me. My REST service is using Javascritpt dates. I adapted the referred answer to an extension method.

I have a javascript function that calls an MVC controller with JSON data:

var specsAsJson = JSON.stringify(specs);
$.post('/Home/Save', {
   jsonData: specsAsJson
});

That exception happens when I call Deserialize() (third line in method below):

    public ActionResult Save(string jsonData)
    {
        var serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new[] { new TimeSpanJsonConverter() });
        var specs = serializer.Deserialize<List<EquipmentSpecWithParameterlessConstructor>>(jsonData);

        return View("Index", _allTrackerJobs);
    }

I created a DTO with the exact same fields as the domain object, except that I made the date fields strings so they would deserialize. Now that I can deserialize it, I'll work on getting the dates into a valid format so I can create domain objects from my DTOs.

public class EquipmentSpecDto {
   public string StartTime {
      get;
      set;
   }
   public string EndTime {
      get;
      set;
   }
   // more properties here
}

And I simply just used the DTO for the deserialization:

var specs = serializer.Deserialize<List<EquipmentSpecDto>>(jsonData);

For completeness, and in the hopes that I save someone else an hour, this is how I was able to convert the javascript dates:

    foreach(EquipmentSpecDto specDto in specDtos) {
       // JavaScript uses the unix epoch of 1/1/1970. Note, it's important to call ToLocalTime()
       // after doing the time conversion, otherwise we'd have to deal with daylight savings hooey.
       DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
       Double startMilliseconds = Convert.ToDouble(specDto.StartTime.Substring(6, 13));
       Double endMilliseconds = Convert.ToDouble(specDto.EndTime.Substring(6, 13));
       DateTime startTime = unixEpoch.AddMilliseconds(startMilliseconds).ToLocalTime();
       DateTime endTime = unixEpoch.AddMilliseconds(endMilliseconds).ToLocalTime();
       EquipmentSpec spec = new EquipmentSpec(startTime, endTime, specDto.Equipment);

       specs.Add(spec);
    }
load more v
65%

The major problem with this Unix Epoch representation is that, it doesn't provide a way to store or represent the timezone offset for the date., So moment offers a perfect & simple way to handle the TimeZone offsets and convert it into a proper UTC date format.,This way of representation of JSON date was widely used before proper ISO 8601 format was formalized and endorsed by W3C. The major advantage of Unix Epoch format is that it can be stored efficiently as an Integer datatype, easily compared against other values and retrieved faster. so most languages has the default parser (constructor) that can parse and convert it into a date object.,In case, you need a complex date manipulations, Timezone parsing & conversions, precise display etc., then it would be best to depend on a well tested DateTime library like moment.js.

For e.g., In JavaScript following Date constructor straight away converts the milliseconds since 1970 to Date as follows

var myDate = new Date(1530144000000);
load more v
75%

The problem comes from the JSON spec itself: there is no literal syntax for dates in JSON. The spec has objects, arrays, strings, integers, and floats, but it defines no standard for what a date looks like.,With no standard for dates in JSON, the number of possible different formats when interoping with other systems is endless. Fortunately Json.NET has a solution to deal with reading and writing custom dates: JsonConverters. A JsonConverter is used to override how a type is serialized.,Technically this is invalid JSON according to the spec, but all browsers and some JSON frameworks, including Json.NET, support it.,From Json.NET 4.5 and onwards dates are written using the ISO 8601 format by default, and using this converter is unnecessary.

public class LogEntry {
   public string Details {
      get;
      set;
   }
   public DateTime LogDate {
      get;
      set;
   }
}

[Test]
public void WriteJsonDates() {
   LogEntry entry = new LogEntry {
      LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc),
         Details = "Application started."
   };

   // default as of Json.NET 4.5
   string isoJson = JsonConvert.SerializeObject(entry);
   // {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}

   JsonSerializerSettings microsoftDateFormatSettings = new JsonSerializerSettings {
      DateFormatHandling = DateFormatHandling.MicrosoftDateFormat
   };
   string microsoftJson = JsonConvert.SerializeObject(entry, microsoftDateFormatSettings);
   // {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"}

   string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter());
   // {"Details":"Application started.","LogDate":new Date(1234656000000)}
}
40%

Check this example. Now please take its reference and correct your code.,You have to set JsonSerializerSettings.DateFormatString to your desired format.,Serialize DateFormatString,Serializing Dates in JSON

HTML

<table class="tblEmployees">
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>BirthDate</th>
        </tr>
    </thead>
    <tbody>
    </tbody>
</table>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
    $(function () {
        $.ajax({
            type: "POST",
            url: "Default.aspx/GetEmployees",
            data: '{}',
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (response) {
                var employees = JSON.parse(response.d)
                var rows = '';
                for (var i = 0; i < employees.length; i++) {
                    var id = employees[i].EmployeeId;
                    var name = employees[i].FirstName;
                    var dob = employees[i].DOB;
                    rows += "<tr><td>" + id + "</td><td>" + name + ' ' + "</td><td>" + dob + "</td></tr>";
                }
                $('.tblEmployees tbody').append(rows);
            }
        });
    });
</script>
load more v
22%

Using this date parser you can now add that to the call to JSON.parse():,Because the setting is page or scope global, you can also reset the original parser by using:,To encode a date with JSON you can just use the standard JSON serializer’s stringify() method:,If you can avoid parsing dates on the client, avoid it and format on the server.

To encode a date with JSON you can just use the standard JSON serializer’s stringify() method:

var date = new Date();
console.log(date); // Wed Jan 01 2014 13:28:56 GMT-1000 (Hawaiian Standard Time) 

var json = JSON.stringify(date);
console.log(json); // "2014-01-01T23:28:56.782Z"

If you now want to deserialize that JSON date back into a date you’ll find that JSON.parse() doesn’t do the job. If you try:

// JSON encoded date
var json = "\"2014-01-01T23:28:56.782Z\"";

var dateStr = JSON.parse(json);
console.log(dateStr); // 2014-01-01T23:28:56.782Z

The following turns the ISO date back into a ‘real’ date:

// JSON encoded date
var json = "\"2014-01-01T23:28:56.782Z\"";

var dateStr = JSON.parse(json);
console.log(dateStr); // 2014-01-01T23:28:56.782Z

var date = new Date(dateStr);
console.log(date); // Wed Jan 01 2014 13:28:56 GMT-1000 (Hawaiian Standard Time)

It’s only a short bit of code:

if (window.JSON && !window.JSON.dateParser) {
   var reISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
   var reMsAjax = /^\/Date\((d|-|.*)\)[\/|\\]$/;

   JSON.dateParser = function(key, value) {
      if (typeof value === 'string') {
         var a = reISO.exec(value);
         if (a)
            return new Date(value);
         a = reMsAjax.exec(value);
         if (a) {
            var b = a[1].split(/[-+,.]/);
            return new Date(b[0] ? +b[0] : 0 - +b[1]);
         }
      }
      return value;
   };

}

Using this date parser you can now add that to the call to JSON.parse():

var date = JSON.parse(json, JSON.dateParser);
console.log(date);

This also works on complex objects so if you have and object like this:

{
   "id": "312saAs1",
   "name": "Jimmy Roe",
   "entered": "2014-01-01T23:28:56.782Z",
   "updated": "2014-01-01T23:28:56.782Z"
}

To use this you would call JSON.useDateParser() somewhere at the top of your script hierarchy:

<script src="jquery.min.js"></script>
<script src="JsonDateExtensions.js"></script>
<script>
   // use date parser for all JSON.parse() requests
   // make sure to call before any JSON conversions
   JSON.useDateParser();
</script>
<script>
   // other libs
   // page script code etc.
</script>

Let’s look at an example. Assume we have a JSON object we’re requesting from the server that has a couple of date properties – update and entered.

{
   "id": "312saAs1",
   "name": "Jimmy Roe",
   "entered": "2014-01-01T13:13:34.441Z",
   "updated": "2014-01-03T23:28:56.782Z"
}

Now we want to access this JSON data with jQuery’s $.getJSON() like this:

$.getJSON("JsonWithDate.json")
   .done(function(data) {
      console.log("result.entered: " + data.entered +
         "  result.updated: " + data.updated);
   });
load more v

Other "proper-convert" queries related to "Proper Way to Convert JSON Date to .NET DateTime During Deserialization"