137

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 的默认模型绑定器中使用的日期格式?默认模型绑定器不应该使用您的本地化设置吗?

4

10 回答 10

166

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

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

于 2009-02-09T15:18:49.430 回答
38

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

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

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

于 2010-05-24T12:37:42.603 回答
33

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

感谢: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

于 2013-07-15T10:00:13.683 回答
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());
于 2012-08-10T14:35:21.767 回答
8

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

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

/公司/出版社/5月%2001%202008

/公司/出版社/2008-05-01

/公司/出版社/05-01-2008

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

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

于 2009-02-15T03:40:59.563 回答
6

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

<globalization uiCulture="auto" culture="auto" />
于 2013-01-10T10:40:00.423 回答
6

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

获取方法

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());
}
于 2015-01-29T16:30:16.777 回答
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);
                    }
                }

            }
        }
    }
}
于 2013-01-30T15:24:04.567 回答
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;
}
于 2013-05-31T11:14:27.613 回答
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");
    }
于 2016-08-27T21:56:22.973 回答