0

I have this method within a controller on a ASP.NET MVC application:

public PartialViewResult UpdateCalendar(long id, List<List<CalendarDay>> newCalendar, int selectedMonth) {
    //
}

CalendarDay contains some DateTime objects. When I send them from Client, they are deserialized as Kind Local, and not Utc.

Example of Date received from client: 13/01/2022 00:00:00Z (note the final Z; I would expect Utc as deserialization).

How can I setup (only for this controller/method; can't impact the whole app in the global config) the deserialization as Utc for the input object's DateTime?

13
  • Is this classic asp.net or core / 3 / 5 / 6 ? Commented Oct 5, 2021 at 6:56
  • A PartialViewResult usually sends data TO a client. Yet your question says from a client. If they are coming FROM a client, then a client might need to create them as utc and not as GMT+X. Otherwise your could simply iterate over your CalendarDay objects and call .ToUniversalTime(). Commented Oct 5, 2021 at 6:59
  • @Marco classic asp.net. The data from client is correct (not +X, as for this question stackoverflow.com/questions/69437340/…). The solution to iterate the date is a "workaround", and I don't like it so much. Any other way? Commented Oct 5, 2021 at 7:07
  • Do you want to convert your local Datetime to UTC Date time while perse from JSON to Data Model? Commented Oct 5, 2021 at 11:17
  • @Parvez no I need that the deserialization process save them in DateTime object with kind Utc (as they are), and not Local. By default it seems deserialization set Kind as Local Commented Oct 5, 2021 at 11:19

1 Answer 1

1

You have to change the default deserialization for your action parameters by either adding a custom ModelBinder (that changes deserialization everywhere the model is used) or for just that Controller Action with a custom ActionFilterAttribute.

So let's say we have this simple class

public class CalendarDay { public DateTime Date { get; set; } }

and your example date from above (13/01/2022 00:00:00Z)

Then add your custom implementation of the ActionFilter (could be just overwriting some values of already parsed ActionParameters or custom json deserialization, or anything else you like):

public class UtcDateTimeFilter : ActionFilterAttribute
{
  public string Parameter    { get; set; }
  public Type   JsonDataType { get; set; }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
    {
      string inputContent;
      filterContext.HttpContext.Request.InputStream.Seek(0, SeekOrigin.Begin);
      using (var sr = new StreamReader(filterContext.HttpContext.Request.InputStream))
      {
        inputContent = sr.ReadToEnd();
      }

      var result = JsonConvert.DeserializeObject(inputContent, JsonDataType, new CustomUtcDateTimeConverter());
      filterContext.ActionParameters[Parameter] = result;
    }
  }
}

Hint: you can add some conditions for the JsonDataType to only add the CustomJsonConverter for certain types, to prevent UTC-converting every date you use the ActionFilter with. That way it's reusable for different cases.

As I've went with the custom json deserialization I can add a custom JsonConverter that automatically processes each occurence of my defined object (in this case the DateTime):

public class CustomUtcDateTimeConverter : JsonConverter<DateTime>
{
  public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
  {
    writer.WriteValue(value.ToString());
  }

  public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
  {
    var date = DateTime.Parse((string)reader.Value);
    return date.ToUniversalTime();
  }
}

And finally you can define your Controller Action like this:

[AcceptVerbs(HttpVerbs.Post)]
[UtcDateTimeFilter(Parameter = "myUtcDates", JsonDataType = typeof(CalendarDay[]))]
public ActionResult TestUtcDateTimeDeserialization(CalendarDay[] myUtcDates)
{
    var date1 = myUtcDates[0];
    var date2 = myUtcDates[1];
    return PartialView("SomePartialView");
}

Resulting in this DateTime object:

DateTime Screenshot

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.