【问题标题】:MVC DateTime binding with incorrect date format日期格式不正确的 MVC DateTime 绑定
【发布时间】:2010-10-06 10:13:07
【问题描述】:

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 如果应用程序是全球性的,并且可能每个应用程序的日期时间格式不同,你怎么能这样做? ,您不应该去更改每个日期时间格式..
  • 这些答案都没有帮助我。表单需要本地化。一些用户可能以一种方式获取日期,而另一些用户则以另一种方式获取日期。在 web.config 中设置一些东西。或 global.asax 不会有帮助。我会继续寻找更好的答案,但一种方法是将日期作为字符串处理,直到我回到 c#。

标签: asp.net-mvc datetime localization formatting model-binders


【解决方案1】:

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

Melvyn Harbour 详细解释了 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 小时内被禁用。
  • 我强烈反对从技术上讲这是正确的。模型绑定器应该始终与 POST 和 GET 行为相同。如果不变的文化是 GET 的方式,那么它也适用于 POST。根据 http 动词改变行为是没有意义的。
  • 我有一个问题,我们的网站托管在另一个国家,它需要MM/dd/yyyy 格式,否则会显示验证错误The field BeginDate must be a date.,我怎样才能让服务器接受dd/MM/yyyy 格式?
  • URL 参数应该是明确的,就像使用 ISO 标准格式一样。那么文化设置就无关紧要了。
  • 这给出了解释原因的链接,但实际上并没有为此问题提供任何最简单的解决方案(例如通过从脚本中发布 ISO 日期时间字符串),这种方式总是有效的,我们不必关心在服务器上设置特定的文化或确保服务器和客户端之间的日期时间格式相同。
【解决方案2】:

我会在全球范围内设置您的文化。 ModelBinder 把它捡起来!

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

或者您只需为此页面更改此设置。
但我认为在 web.config 中全局使用更好

【讨论】:

  • 不适合我。如果我通过 2010 年 10 月 23 日,日期仍然为空。
  • 我认为这是最简单的解决方案。对我来说,它改变了所有 Date.ToString() 的格式。我认为它也适用于绑定,但没有检查,抱歉:-(
  • 我的开发服务器日期格式设置为 dd/MM/yyyy modelbinder 使用了 MM/dd/yyyy 格式。将 web.config 中的格式设置为 dd/MM/yyyy 现在会强制 modelbinder 使用欧洲格式。我认为它应该使用服务器的日期设置。不管怎样,你解决了我的问题。
  • 这对我来说非常有效......虽然我知道我的应用程序服务器在英国运行英国操作系统......但不知何故感觉很奇怪......:/
  • 在 Azure 网站上运行的 MVC4 中完美运行
【解决方案3】:

我在将短日期格式绑定到 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;
            }
        }
    }
}

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

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。

感谢: 伊万·兹拉特夫 - 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

【讨论】:

    【解决方案4】:

    在 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,尤其是在验证方面。此外,它仅在 DateTime 的名称为 dateTime 时才有效。
    • 另外,我发现DateTime?s 仅在您使用typeof(DateTime?)ModelBinders.Binders.Add 添加另一个调用时才有效。
    【解决方案5】:

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

    例如,在美国,以下所有字符串都是等效的,并自动绑定到相同的 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 是否在所有文化中都被普遍解析,所以也许知道的人可以添加评论。

    【讨论】:

    • 既然没有人说 yyyy-MM-dd 不通用,我猜它是。
    • 在我看来这比使用模型活页夹更好。 .datepicker("option", "dateFormat", "yy-mm-dd") 或将其设置为默认值。
    • yyyy-MM-dd 是“国际”日期格式 (ISO 8601)。也很好地按字母顺序排序。
    【解决方案6】:

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

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

    【讨论】:

    【解决方案7】:

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

    【讨论】:

      【解决方案8】:
        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 仍然无法识别
      【解决方案9】:
      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;
      }
      

      【讨论】:

      • 你应该通过添加一些解释让你的答案更丰富。
      • 从记忆中,这与内置模型绑定器的工作方式不一致,因此它可能没有相同的次要行为,例如保留键入的值以进行验证。
      【解决方案10】:

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

          protected override void Initialize(RequestContext requestContext)
          {
              base.Initialize(requestContext);
      
              Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
              Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-09
        • 1970-01-01
        • 2020-05-31
        • 1970-01-01
        • 2018-11-18
        • 1970-01-01
        相关资源
        最近更新 更多