首页 文章

MVC DateTime绑定具有不正确的日期格式

提问于
浏览
125

Asp.net-MVC现在允许隐式绑定DateTime对象 . 我有一个行动

public ActionResult DoSomething(DateTime startDate) 
{ 
... 
}

这成功地将字符串从ajax调用转换为DateTime . 但是,我们使用日期格式dd / MM / yyyy; MVC正在转换为MM / dd / yyyy . 例如,使用字符串'09 / 02/2009'提交对操作的调用会导致DateTime为'02 / 09/2009 00:00:00',或在我们的本地设置中为9月2日 .

我不想为了日期格式而滚动我自己的模型 Binders . 但是,如果MVC能够为我执行此操作,则无需更改操作以接受字符串然后使用DateTime.Parse .

有没有办法改变DateTime的默认模型 Binders 中使用的日期格式?默认型号 Binders 不应该使用您的本地化设置吗?

10 回答

  • 5

    我设置 CurrentCultureCurrentUICulture 我的自定义基本控制器

    protected override void Initialize(RequestContext requestContext)
        {
            base.Initialize(requestContext);
    
            Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
        }
    
  • 0
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var str = controllerContext.HttpContext.Request.QueryString[bindingContext.ModelName];
        if (string.IsNullOrEmpty(str)) return null;
        var date = DateTime.ParseExact(str, "dd.MM.yyyy", null);
        return date;
    }
    
  • 157

    我刚刚用一些更详尽的谷歌搜索找到了答案:

    Melvyn Harbour详细解释了为什么MVC使用日期的方式,以及如何在必要时覆盖它:

    http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx

    在查找要解析的值时,框架按特定顺序查找:RouteData(上面未显示)URI查询字符串请求表单只有最后一个才能识别文化 . 从本地化的角度来看,有一个很好的理由 . 想象一下,我写了一个网络应用程序,显示我在线发布的航班信息 . 我通过点击当天的链接(可能是http://www.melsflighttimes.com/Flights/2008-11-21)查找特定日期的航班,然后想通过电子邮件将该链接发送给我的同事美国 . 我们可以保证我们将同时查看同一页数据的唯一方法是使用InvariantCulture . 相比之下,如果我使用表格预订我的航班,一切都在紧张的周期中发生 . 当数据写入表单时,数据可以尊重CurrentCulture,因此在从表单返回时需要尊重它 .

  • 28

    我会全球设定你的文化 . ModelBinder选择了!

    <system.web>
        <globalization uiCulture="en-AU" culture="en-AU" />
    

    或者您只需更改此页面 .
    但是在web.config中我认为更好

  • 1

    我一直遇到与DateTime模型属性绑定的短日期格式相同的问题 . 在查看了许多不同的例子(不仅仅是关于DateTime)之后,我将以下内容放在一起:

    using System;
    using System.Globalization;
    using System.Web.Mvc;
    
    namespace YourNamespaceHere
    {
        public class CustomDateBinder : IModelBinder
        {
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if (controllerContext == null)
                    throw new ArgumentNullException("controllerContext", "controllerContext is null.");
                if (bindingContext == null)
                    throw new ArgumentNullException("bindingContext", "bindingContext is null.");
    
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
                if (value == null)
                    throw new ArgumentNullException(bindingContext.ModelName);
    
                CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
                cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
    
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
    
                try
                {
                    var date = value.ConvertTo(typeof(DateTime), cultureInf);
    
                    return date;
                }
                catch (Exception ex)
                {
                    bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
                    return null;
                }
            }
        }
    
        public class NullableCustomDateBinder : IModelBinder
        {
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if (controllerContext == null)
                    throw new ArgumentNullException("controllerContext", "controllerContext is null.");
                if (bindingContext == null)
                    throw new ArgumentNullException("bindingContext", "bindingContext is null.");
    
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
                if (value == null) return null;
    
                CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone();
                cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
    
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
    
                try
                {
                    var date = value.ConvertTo(typeof(DateTime), cultureInf);
    
                    return date;
                }
                catch (Exception ex)
                {
                    bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
                    return null;
                }
            }
        }
    }
    

    为了与在Global ASAX文件中注册路由等的方式保持一致,我还在我的MVC4项目的App_Start文件夹中添加了一个新的sytatic类,名为CustomModelBinderConfig:

    using System;
    using System.Web.Mvc;
    
    namespace YourNamespaceHere
    {
        public static class CustomModelBindersConfig
        {
            public static void RegisterCustomModelBinders()
            {
                ModelBinders.Binders.Add(typeof(DateTime), new CustomModelBinders.CustomDateBinder());
                ModelBinders.Binders.Add(typeof(DateTime?), new CustomModelBinders.NullableCustomDateBinder());
            }
        }
    }
    

    然后我从我的Global ASASX Application_Start中调用静态RegisterCustomModelBinders,如下所示:

    protected void Application_Start()
    {
        /* bla blah bla the usual stuff and then */
    
        CustomModelBindersConfig.RegisterCustomModelBinders();
    }
    

    这里一个重要的注意事项是,如果您将DateTime值写入这样的隐藏字段:

    @Html.HiddenFor(model => model.SomeDate) // a DateTime property
    @Html.Hiddenfor(model => model) // a model that is of type DateTime
    

    我这样做了,页面上的实际值是“MM / dd / yyyy hh:mm:ss tt”而不是“dd / MM / yyyy hh:mm:ss tt”,就像我想要的那样 . 这导致我的模型验证失败或返回错误的日期(显然交换日期和月份值) .

    经过大量的努力和尝试失败后,解决方案是在Global.ASAX中为每个请求设置文化信息:

    protected void Application_BeginRequest()
    {
        CultureInfo cInf = new CultureInfo("en-ZA", false);  
        // NOTE: change the culture name en-ZA to whatever culture suits your needs
    
        cInf.DateTimeFormat.DateSeparator = "/";
        cInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
        cInf.DateTimeFormat.LongDatePattern = "dd/MM/yyyy hh:mm:ss tt";
    
        System.Threading.Thread.CurrentThread.CurrentCulture = cInf;
        System.Threading.Thread.CurrentThread.CurrentUICulture = cInf;
    }
    

    如果你将它粘贴在Application_Start甚至Session_Start中它将无法工作,因为它将它分配给会话的当前线程 . 众所周知,Web应用程序是无状态的,因此先前为您的请求提供服务的线程与服务当前请求的线程相同,因此您的文化信息已转移到数字天空中的GC .

    谢谢你:Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/

    加里克 - https://stackoverflow.com/a/2468447/578208

    德米特里 - https://stackoverflow.com/a/11903896/578208

  • 13

    它在MVC 3中会略有不同 .

    假设我们有一个控制器和一个带Get方法的视图

    public ActionResult DoSomething(DateTime dateTime)
    {
        return View();
    }
    

    我们应该添加ModelBinder

    public class DateTimeBinder : IModelBinder
    {
        #region IModelBinder Members
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            DateTime dateTime;
            if (DateTime.TryParse(controllerContext.HttpContext.Request.QueryString["dateTime"], CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out dateTime))
                return dateTime;
            //else
            return new DateTime();//or another appropriate default ;
        }
        #endregion
    }
    

    和Global.asax的Application_Start()中的命令

    ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
    
  • 8

    值得注意的是,即使没有创建自己的模型 Binders ,也可以解析多种不同的格式 .

    例如,在美国,以下所有字符串都是等效的,并自动绑定到 the same DateTime值:

    / company / press / may%2001%202008 / company / press / 2008-05-01 / company / press / 05-01-2008

    我强烈建议使用yyyy-mm-dd因为它更便携 . 你真的不想处理多种本地化格式 . 如果有人在5月1日而不是1月5日预订航班,那么您将遇到大问题!

    NB:我不是如果yyyy-mm-dd在所有文化中得到普遍解析,那么也许明白了,所以也许知道的人可以添加评论 .

  • 1

    我在我的MVC4上设置了以下配置,它就像一个魅力

    <globalization uiCulture="auto" culture="auto" />
    
  • 35

    尝试使用toISOString() . 它以ISO8601格式返回字符串 .

    GET方法

    JavaScript的

    $.get('/example/doGet?date=' + new Date().toISOString(), function (result) {
        console.log(result);
    });
    

    C#

    [HttpGet]
    public JsonResult DoGet(DateTime date)
    {
        return Json(date.ToString(), JsonRequestBehavior.AllowGet);
    }
    

    POST方法

    JavaScript的

    $.post('/example/do', { date: date.toISOString() }, function (result) {
        console.log(result);
    });
    

    C#

    [HttpPost]
    public JsonResult Do(DateTime date)
    {
         return Json(date.ToString());
    }
    
  • 5
    public class DateTimeFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.Request.RequestType == "GET")
            {
    
                foreach (var parameter in filterContext.ActionParameters)
                {
                    var properties = parameter.Value.GetType().GetProperties();
    
                    foreach (var property in properties)
                    {
                        Type type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    
                        if (property.PropertyType == typeof(System.DateTime) || property.PropertyType == typeof(DateTime?))
                        {
                            DateTime dateTime;
    
                            if (DateTime.TryParse(filterContext.HttpContext.Request.QueryString[property.Name], CultureInfo.CurrentUICulture, DateTimeStyles.None, out dateTime))
                                property.SetValue(parameter.Value, dateTime,null);
                        }
                    }
    
                }
            }
        }
    }
    

相关问题