Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell ASP.Net MVC that all incoming dates deserialized from JSon should be UTC?

I'm sending dates from my web app in UTC format, but when I recieve them on the server side, the JSon serializer (which is probably used by setting up your model) makes this in a local date & time with DateTimeKind.Local relative to the server's time zone.

When I do a DateTime.ToUniversalTime() I'm getting the proper UTC date, so this isn't a problem. Conversion works correctly and dates are sent the way they should... but.... I don't like to make a call to 'ToUniversalTime()' on every date on my model before I store it into a database... This is prone to errors and easy to forget when you have a big app.

So here's the question: Is there a way to tell MVC that incoming dates should always be expressed in UTC format?

like image 879
Jeroen Landheer Avatar asked Mar 02 '12 20:03

Jeroen Landheer


1 Answers

After digging a bit more, I've found a way to make this work.

The problem wasn't so much the serializer, only the problem that the model's dates aren't expressed in UTC but in local time. ASP.Net allows you to create custom model binders and I think this is the key to change dates to UTC once they are deserialized.

I've used the following code to make this work, there might be a few bugs to iron out, but you will get the idea:

public class UtcModelBinder : DefaultModelBinder
{
  protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
  {
    HttpRequestBase request = controllerContext.HttpContext.Request;

    // Detect if this is a JSON request
    if (request.IsAjaxRequest() &&
      request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
    {
      // See if the value is a DateTime
      if (value is DateTime)
      {
        // This is double casting, but since it's a value type there's not much other things we can do
        DateTime dateValue = (DateTime)value;

        if (dateValue.Kind == DateTimeKind.Local)
        {
          // Change it
          DateTime utcDate = dateValue.ToUniversalTime();
          if (!propertyDescriptor.IsReadOnly && propertyDescriptor.PropertyType == typeof(DateTime))
            propertyDescriptor.SetValue(bindingContext.Model, utcDate);

          return;
        }
      }
    }

    // Fall back to the default behavior
    base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);

  }
}
like image 149
Jeroen Landheer Avatar answered Oct 27 '22 00:10

Jeroen Landheer