日期格式不正确的 MVC 日期时间绑定

Net-MVC 现在允许隐式绑定 DateTime 对象

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

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

我不想为了日期格式而滚动我自己的模型活页夹。但似乎不必更改操作以接受字符串,然后使用 DateTime。解析 MVC 是否有能力为我做这些。

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

158011 次浏览

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

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,因此在从表单返回时需要尊重它。

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

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

2001% 202008

公司/新闻处/2008-05-01

公司/新闻处/05-01-2008

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

注意: 我不清楚 yyyy-mm-dd 是否在所有文化中都被普遍解析,所以也许知道的人可以添加注释。

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

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

或者你可以把这个改成这个页面。
但在全球范围内,web.config 我认为更好

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

我在我的 MVC4上设置了下面的配置,它工作起来非常有效

<globalization uiCulture="auto" culture="auto" />
  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);
}
}


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

在短日期格式绑定到 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 文件夹中添加了一个新的系统类,名为 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。

谢谢浏览: 伊万 · 兹拉特夫- http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/

Garik-https://stackoverflow.com/a/2468447/578208

Dmitry-https://stackoverflow.com/a/11903896/578208

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

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

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


Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
}