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:

.ToUniversalTime().