Search code examples
c#datetimeasp.net-core-mvcutciso8601

Http Query Parameters in UTC in AspNet Core


I'm having an issue that I cannot figure out.

I try to deal ALWAYS with UTC DateTime at server side.

I have an AspNetCore Mvc application with an endpoint that accepts queries that may include DateTimes. I want Mvc to understand that these dates are already in UTC and not to transform them "again".

My system is in Spain, (UTC +2)

If I send an http request to my localhost server like this:

http://localhost:50004/api/Resources?appliesOn=2018-06-30T18:00:00.000Z

I want to have the deserialized datetime as UTC representing the same date as:

DateTime.SpecifyKind(new DateTime(2018, 6, 30, 18, 0, 0), DateTimeKind.Utc)

But I can see that Mvc always transforms the date 2018-06-30T18:00:00.000Z into two hours later: 2018-06-30 20:00:00

I have tried to tell Mvc to use UTC json serializer/deserializer but nothing changes:

services
  .AddMvc()
  .AddJsonOptions(options =>
  {
      options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
  });

Is there a way to send query parameters in an http GET request already as a representation of UTC datetime? I understood a Date in ISO 8601, if it has a suffix Z it means "zero-offset" which should be interpreted already as UTC date time. Why Mvc is then transforming this and adding 2 hours offset?

Any clarification would be much appreciated!

PS: This is my endpoint, nothing special as you can see:

[HttpGet("")]
public IActionResult GetResources()
{
    var displayUri = Request.GetDisplayUrl();
    var requestUri = new Uri(displayUri);
    var filter = _filteredRequestFactory.Create(requestUri);
    var resources = _myProjection.GetResourcers(filter);
    return Ok(resources);
}

Solution

  • Thanks to another question in StackOverflow I found out that the reason this happens is because AspNetCore Mvc deserializer does not even use Json.Net deserializer in a GET http request to deserialize the query parameters.

    Therefore, the following request: http://localhost:50004/api/DateTests?date=2018-06-15T18:00:00.000Z

    would be captured by my endpoint:

    [HttpGet("")]
    public IActionResult GetDate(DateTime date)
    {
        return Ok(date.ToString("o"));
    }
    

    and return the date in ISO 8601 format as follows: 2018-06-15T20:00:00.0000000+02:00

    It deserializes the query parameter as if it was a local time date, and it applies the UTC + 2 (because the app is in Spain).

    I needed a way to tell AspNet Core Mvc deserializer to understand that the query parameter that looks like a date, should be treated already as an UTC date and not modified it when deserializing.

    The answer was to create a custom model binder and apply it either to that endpoint or globally.

    I found a good implementation and after adding that model binder provider and model binder,

    my endpoint now returns for the request with ?date=2018-06-15T18:00:00.000Z :

    2018-06-15T18:00:00.0000000 as a DateTime of UTC kind.

    If I pass a ?date=2018-06-11T18:00:00+0100 it will be retrieved as a local time kind and the result would be: 2018-06-11T19:00:00.0000000+02:00

    as desired