Asp.net WebApi deserializes UTC time string to local time

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.


If you want to modify the way Asp WebApi parses uri parameters of your GET requests, you can write custom IModelBinder like this:

public class UtcDateTimeModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName)?.RawValue as string;
        DateTime parsedDate;
        if (!DateTime.TryParse(stringValue, null, DateTimeStyles.AdjustToUniversal, out parsedDate))
        {
            return false;
        }

        bindingContext.Model = parsedDate;
        return true;
    }
}

And then override default binder:

GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder()););

Parameter Binding in ASP.NET Web API