Search code examples
asp.net-web-apijson.netutc

Asp.net WebApi deserializes UTC time string to local time


I have this url

http://example.com/api/record/getall?startdate=1994-11-05T17:15:30Z

and this webapi endpoint

[ActionName("GetAll")]
public object GetAll(DateTime startDate)
{
     ...
}

The problem I face is that the startDate received the deserialized string as a local time, "11/5/1994 9:15:30 AM" instead of stay in UTC time which what I wanted "11/5/1994 5:15:30 PM".

I'm using VS2012 update2, latest Json.net nuget package. However, if I use json.net in a separate console app to test, the same string "1994-11-05T17:15:30Z" is able to deserialize correctly into "11/5/1994 5:15:30 PM".

Anyone know what is wrong here?


Solution

  • Although you have already found a solution for your question, I thought I would take a shot at explaining why it did not work as you expected.

    WebApi uses content type negotiation to determine what parser to use when reading data. That means it will look at the Content-Type header of the request to make the determination. If the Content-Type header is set to application/json then it will use Json.Net to parse to content and feed it to your method.

    An HTTP GET request, such as the one you are making here, does not have a content type set. The "content" in this case is really just the query string from the URL. WebApi does not expect to find JSON data here, so it is not going to try to use a JSON parser to make sense of it. Even if it did, the string you are passing to your GetAll method isn't even valid JSON. (It would need to be quoted to be valid.)

    Now, if you were to change your method to accept a POST request, and you set the content type header to application/json and passed the date as a JSON string in the body, then WebApi will use Json.Net to parse it, and it will work like you expect.

    For example, say your method looked like this:

    [HttpPost]
    public object GetAll([FromBody]DateTime startDate)
    {
        try
        {
            return new
            {
                StartDate = startDate.ToString("yyyy-MM-dd HH:mm:ss"),
                StartDateKind = startDate.Kind.ToString(),
            };
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
    }
    

    And you made a request like this (note the POST):

    POST http://localhost:57524/api/values/GetAll HTTP/1.1
    Content-Type: application/json
    Content-Length: 22
    Host: localhost:57524
    
    "1994-11-05T17:15:30Z"
    

    The response would look like this:

    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Pragma: no-cache
    Content-Type: application/json; charset=utf-8
    Expires: -1
    Server: Microsoft-IIS/8.0
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 31 May 2013 01:25:48 GMT
    Content-Length: 57
    
    {"StartDate":"1994-11-05 17:15:30","StartDateKind":"Utc"}
    

    As you can see, it does correctly recognize the date to be UTC in this scenario.