Proper Way to Convert JSON Date to .NET DateTime D

2019-01-14 16:28发布

问题:

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

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

On the server side, within the controller, I can't seem to get past this error:

/Date(1347992529530)/ is not a valid value for DateTime.

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'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.

Is there an elegant, generally-accepted approach to solving this, or do we still need some kind of ugly work-around? What's the right way to resolve this?

=================== End of original question ===================


Edit - SOLVED by serializing using JsonConvert

See my answer below (not the crappy work-around in this question).


Edit - Crappy work-around

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);

Edit 2 - Converting JavaScript Dates to .NET

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);
    }

回答1:

I found a simple answer. In my javascript, I was serializing the data using the JavaScriptSerializer. After much googling, I found this article that shows how to serialize using JsonConvert that causes a more .NET-friendly DateTime to be used.

Old:

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

Dates look like this: Date(1348017917565)

New:

var specs = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(ViewBag.JobSpecEquipment));

Dates look like this: 2012-09-18T21:27:31.1285861-04:00

So the problem was really how I was serializing in the first place. Once I used JsonConvert, deserialization on the back end simply worked.



回答2:

I found this piece of code on the internet. It worked like a charm for me...

function customJSONstringify(obj) {
    return JSON.stringify(obj).replace(/\/Date/g, "\\\/Date").replace(/\)\//g, "\)\\\/")
}


回答3:

One thing that catches people out quite often with converting between Javascript dates and various server-side languages is that although both sides may be able to understand a unix-style timestamp value, JS uses microsecond-precision timestamp, whereas in most other languages the default timestamp precision is to the second.

In other words, 1347993132851 in Javascript needs to be divided by 1000 in order to be recognised as a unix timestamp in other languages.

Alternatively, if your platform can accept formatted date strings, use the Javascript Date() object to convert a timestamp value into a formatted date to send to the server. Or even better, use a helper library such as Date.js or Moment.js.



回答4:

JavaScript (well, EcmaScript) defines its DateTime string interchange format based on a simplification of the ISO-8601 standard.

XML Schema defines its DateTime string interchange format based on ISO-8601 also.

I have found it handy to use the .NET class System.Runtime.Remoting.Metadata.W3cXsd2001.SoapDateTime to handle conversion from .NET DateTime values to XML formats and back.

Since JavaScript is based on the same ISO-8601 standard, perhaps it will work for your JSON case as well.



回答5:

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.


using System;

namespace Mediatel.Framework
{
    public static class JsonDate
    {
        public static DateTime ConvertToDateTime(this string jsonDate)
        {
            // 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 milliseconds = Convert.ToDouble(jsonDate);
            DateTime dateTime = unixEpoch.AddMilliseconds(milliseconds).ToLocalTime();

            return dateTime;
        }
    }
}


回答6:

After receving the error

/Date(1347992529530)/ is not a valid value for DateTime.

using this replace worked for me.

var data = ko.toJSON({ objext: obj});
$.ajax({
    url: "/API/API.asmx/SaveObject",
    type: "POST",
    dataType: "json",
    contentType: "application/json; char-utf8;",
    data: data.replace(/\/Date/g, "\\\/Date").replace(/\)\//g, "\)\\\/"),
    success: function (r) {},
    error: function (e) {},
    complete: function () {}
});