具有错误日期格式的MVC DateTime绑定


132

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日。

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

有什么方法可以更改用于DateTime的默认模型联编程序中使用的日期格式?默认模型联编程序是否不应该使用您的本地化设置?


嘿..只需将您的系统日期格式-DD / MM / yyyy更改为MM / DD / yyyy并完成。.我也有同样的问题,我通过更改系统日期格式解决了。
banny

@banny如果应用程序是全局的,并且可能每个应用程序的日期时间格式都不相同,那么您该怎么做?你不要以为去改变每个位日期时间格式..
拉维·梅塔

这些答案都没有帮助我。该表格需要本地化。有些用户可能以一种方式约会,而其他用户则以另一种方式约会。在web.config中设置内容。或global.asax无法提供帮助。我将继续寻找更好的答案,但是一种方法就是将日期作为字符串处理,直到回到c#。
astrosteve,

Answers:


164

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

梅尔文·哈伯(Melvyn Harbor)全面解释了MVC为什么与日期一起使用,以及在必要时如何覆盖日期:

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

在寻找要解析的值时,框架按特定顺序查找,即:

  1. RouteData(上面未显示)
  2. URI查询字符串
  3. 申请表

但是,只有其中的最后一个会了解文化。从本地化的角度来看,这有一个很好的理由。想象一下,我编写了一个Web应用程序,显示了我在线发布的航班信息。我通过单击当天的链接(也许像http://www.melsflighttimes.com/Flights/2008-11-21之类的链接)来查找特定日期的航班,然后通过电子邮件将该链接发送给我的同事美国。我们可以保证我们都将查看同一页数据的唯一方法是是否使用InvariantCulture。相比之下,如果我使用表格来预订我的航班,那么一切都会在一个紧要的周期中发生。数据在写入表单时可以遵循CurrentCulture,因此从表单返回时需要遵循数据。


会做。发布问题后48个小时该功能被禁用。
山姆·韦瑟尔

43
我强烈不同意从技术上讲这是正确的。模型绑定程序应始终与POST和GET行为相同。如果不变文化是获取GET的方法,那么也将其用于POST。根据http动词更改行为是无稽之谈。
Bart Calixto

我有一个问题,我们的网站托管在另一个国家,它需要MM/dd/yyyy格式,否则它显示验证错误The field BeginDate must be a date.,我该如何切断才能接受dd/MM/yyyy格式?
shaijut 2015年

URL参数应该是明确的,例如使用ISO标准格式。然后,文化设置将无关紧要。
nforss 2015年

这提供了解释原因的链接,但实际上并没有提供针对此问题的任何最简单的解决方案(例如通过从脚本中发布ISO日期时间字符串),这种方式始终有效,我们不必在意设置特定的区域性服务器或确保服务器和客户端之间的日期时间格式相同。
绝望

36

我会在全球范围内设定您的文化。ModelBinder接起来!

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

或者,您只需为此页面进行更改。
但是在全球范围内,我认为更好


27
不是为了我 如果我通过2010年10月23日,则日期仍为null。
GONeale

我认为这是最简单的解决方案。对我来说,它更改所有Date.ToString()中的格式。我认为它也可以与绑定一起使用,但是没有检查,对不起:-(
msa.im 2012年

1
我的开发服务器dateformat设置为dd / MM / yyyy modelbinder使用MM / dd / yyyy格式。现在将web.config中的格式设置为dd / MM / yyyy会强制modelbinder使用欧洲格式。在我看来,它应该使用服务器的日期设置。无论如何,您解决了我的问题。
卡雷尔2012年

这对我来说非常有效...尽管我知道我的应用程序服务器在英国运行UK OS,但是以某种方式感到奇怪...:/
Tallmaris 2013年

在Azure网站上运行的MVC4中完美工作
Matty Bear

31

我在将短日期格式绑定到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文件中重新注册路由等的方式,我还向名为CustomModelBinderConfig的MVC4项目的App_Start文件夹中添加了一个新的语法类:

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());
        }
    }
}

然后,我仅从全局ASASX Application_Start调用静态RegisterCustomModelBinders,如下所示:

protected void Application_Start()
{
    /* bla blah bla the usual stuff and then */

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

这里的重要说明是,如果您将DateTime值写入这样的hiddenfield:

@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/

garik- 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());

这是一个不错的起点,但不能正确实现IModelBinder,尤其是在验证方面。另外,仅当名称DateTimedateTime时,此方法才有效。
2014年

2
另外,我发现,DateTime?仅当您向ModelBinders.Binders.Addwith 添加另一个调用时,它才有效typeof(DateTime?)
2014年

8

还值得注意的是,即使不创建自己的模型绑定程序,也可以解析多种不同的格式。

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

/ company / press / may%2001%202008

/ company / press / 2008-05-01

/ company / press / 05-01-2008

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

注意:如果在所有文化中都对yyyy-mm-dd进行了通用解析,则我不清楚,因此也许有人知道可以添加评论。


3
由于没有人说yyyy-MM-dd不是通用的,所以我想是通用的。
deerchao 2011年

我认为这比使用模型绑定器更好。.datepicker(“ option”,“ dateFormat”,“ yy-mm-dd”)或仅将其设置为默认值。
Bart Calixto

6

尝试使用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());
}


1
  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);
                    }
                }

            }
        }
    }
}

这行不通。dd-MM-yyyy仍然无法识别
2014年

1
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;
}

1
您应该通过添加一些解释来使答案更加丰富。
亚历山大·拉沃

从内存来看,这与内置模型绑定程序的工作方式不一致,因此它可能没有相同的辅助行为,例如保留用于验证的类型化值。
2014年

1

我将CurrentCultureCurrentUICulture我的自定义基本控制器

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.