如何使ASP.Net MVC模型绑定器将传入日期视为UTC?

C.J*_*.J. 20 javascript asp.net-mvc json model-binding

我正在向MVC控制器发布一个对象.该对象包含一个名为StartDt的字段,在客户端上它是本地时间的javascript Date对象.

当我在对象上调用JSON.stringify并使用jQuery的ajax方法将其POST到服务器时,我可以在Firebug中看到发送到服务器的是一个ISO字符串,如"1900-12-31T13:00:00.000Z",我相信应该是UTC格式的本地时间.

当我查看控制器中的DateTime字段时,它看起来像是回到本地时间而不是UTC.我怎样才能解决这个问题?

我想存储来自客户端的日期的UTC版本.

Dav*_*ike 19

我在Google上找到了一个关于符合ISO 8601标准的DateTime Model Binder代码的要点,然后将其修改为:

public class DateTimeBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var name = bindingContext.ModelName;
        var value = bindingContext.ValueProvider.GetValue(name);
        if (value == null) 
            return null;

        DateTime date;
        if (DateTime.TryParse(value.AttemptedValue, null, DateTimeStyles.RoundtripKind, out date))
            return date;
        else
            return base.BindModel(controllerContext, bindingContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

我相信gist代码限制太多 - 它在秒上需要6位小数或者它不接受时间戳.这使用TryParse而不是TryParseExact,因此它在技术上会接受大量的时间戳类型.重要的是它使用DateTimeStyles.RoundtripKind来尊重Z所暗示的时区.所以这在技术上不再是ISO 8601特定的实现.

然后,您可以使用模型绑定器属性或使用App_Start中的此代码段将其挂接到MVC管道中:

var dateTimeBinder = new DateTimeBinder();

ModelBinders.Binders.Add(typeof(DateTime), dateTimeBinder);
ModelBinders.Binders.Add(typeof(DateTime?), dateTimeBinder);
Run Code Online (Sandbox Code Playgroud)

  • 您无需解析日期时间值本身.正如问题中所述,问题不是解析日期(它的解析就好了)它的默认模型绑定器的行为,将解析的utc日期转换为服务器本地日期时间.我同意模型绑定器可能比动作过滤器更合适 - 但是它需要的是从base.BindModel调用获取值,检查值是否为Kind = Local然后将其转换为Utc并返回. (5认同)

Mat*_*int 15

ASP.NET Core 2.0中仍存在此问题.以下代码将解决它,支持ISO 8601基本和扩展格式,正确保留值和DateTimeKind正确设置.这与JSON.Net的解析的默认行为一致,因此它使模型绑定行为与系统的其余部分保持一致.

首先,添加以下模型绑定器:

public class DateTimeModelBinder : IModelBinder
{
    private static readonly string[] DateTimeFormats = { "yyyyMMdd'T'HHmmss.FFFFFFFK", "yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK" };

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).FirstValue;

        if (bindingContext.ModelType == typeof(DateTime?) && string.IsNullOrEmpty(stringValue))
        {
            bindingContext.Result = ModelBindingResult.Success(null);
            return Task.CompletedTask;
        }

        bindingContext.Result = DateTime.TryParseExact(stringValue, DateTimeFormats,
            CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result)
            ? ModelBindingResult.Success(result)
            : ModelBindingResult.Failed();

        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后添加以下模型绑定提供程序:

public class DateTimeModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
            throw new ArgumentNullException(nameof(context));

        if (context.Metadata.ModelType != typeof(DateTime) &&
            context.Metadata.ModelType != typeof(DateTime?))
            return null;

        return new BinderTypeModelBinder(typeof(DateTimeModelBinder));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的Startup.cs文件中注册提供程序:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        ...

        options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());

        ...
    }
}
Run Code Online (Sandbox Code Playgroud)


Gau*_*ain 8

您可能必须使用DateTime.ToUniversalTime()方法来获取UTC时间.

  • 这是基础设施可以更好地处理的事情,所以我们不必每次都考虑*我们处理DateTime. (15认同)

Sam*_*Sam 8

我创建了这个小属性.

public class ConvertDateToUTCAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var dateArgs =
            filterContext.ActionParameters.Where(
                x => x.Value != null && x.Value.GetType().IsAssignableFrom(typeof(DateTime))).ToList();

        foreach (var keyValuePair in dateArgs)
        {
            var date = (DateTime) keyValuePair.Value;

            if (date.Kind == DateTimeKind.Local)
                filterContext.ActionParameters[keyValuePair.Key] = date.ToUniversalTime();
        }

        base.OnActionExecuting(filterContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,这将留下未指定或已经单独使用Utc的日期.您可以将它应用于整个控制器.


小智 6

另外,您可以将绑定对象指定为DateTimeOffset而不是DateTime,并且不会进行自动转换。只要输入的字符串具有Z后缀,您就应该获得原始日期,其偏移量为+00:00