3

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);
}
Søren
  • 6,517
  • 6
  • 43
  • 47
diegosasw
  • 13,734
  • 16
  • 95
  • 159

2 Answers2

6

This issue is still there even with ASP.NET-Core 3.1 when using UTC date-times in query. For instance with [FromQuery].

In this github-issue is a also a very well working model-binder provided.

Basically you can copy & pase it and just register it

For Web-API controllers only:

services.AddController(options => options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());

For full-fledged MVC pipeline

services.AddMvc(options => options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
alsami
  • 8,996
  • 3
  • 25
  • 36
4

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

diegosasw
  • 13,734
  • 16
  • 95
  • 159