简体   繁体   English

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

[英]MVC DateTime binding with incorrect date format

Asp.net-MVC now allows for implicit binding of DateTime objects. Asp.net-MVC现在允许隐式绑定DateTime对象。 I have an action along the lines of 我有一个行动

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

This successfully converts a string from an ajax call into a DateTime. 这成功地将字符串从ajax调用转换为DateTime。 However, we use the date format dd/MM/yyyy; 但是,我们使用日期格式dd / MM / yyyy; MVC is converting to MM/dd/yyyy. MVC正在转换为MM / dd / yyyy。 For example, submitting a call to the action with a string '09/02/2009' results in a DateTime of '02/09/2009 00:00:00', or September 2nd in our local settings. 例如,使用字符串'09 / 02/2009'提交对操作的调用会导致DateTime为'02 / 09/2009 00:00:00',或者在我们的本地设置中为9月2日。

I don't want to roll my own model binder for the sake of a date format. 我不想为了日期格式而滚动我自己的模型绑定器。 But it seems needless to have to change the action to accept a string and then use DateTime.Parse if MVC is capable of doing this for me. 但是,如果MVC能够为我执行此操作,则无需更改操作以接受字符串然后使用DateTime.Parse。

Is there any way to alter the date format used in the default model binder for DateTime? 有没有办法改变DateTime的默认模型绑定器中使用的日期格式? Shouldn't the default model binder use your localisation settings anyway? 默认型号绑定器不应该使用您的本地化设置吗?

I've just found the answer to this with some more exhaustive googling: 我刚刚用一些更详尽的谷歌搜索找到了答案:

Melvyn Harbour has a thorough explanation of why MVC works with dates the way it does, and how you can override this if necessary: Melvyn Harbour详细解释了为什么MVC使用日期的方式,以及如何在必要时覆盖它:

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

When looking for the value to parse, the framework looks in a specific order namely: 在查找要解析的值时,框架以特定顺序查找:

  1. RouteData (not shown above) RouteData(上图未显示)
  2. URI query string URI查询字符串
  3. Request form 申请表

Only the last of these will be culture aware however. 然而,只有最后一个才会有文化意识。 There is a very good reason for this, from a localization perspective. 从本地化的角度来看,有一个很好的理由。 Imagine that I have written a web application showing airline flight information that I publish online. 想象一下,我写了一个网络应用程序,显示我在线发布的航班信息。 I look up flights on a certain date by clicking on a link for that day (perhaps something like http://www.melsflighttimes.com/Flights/2008-11-21 ), and then want to email that link to my colleague in the US. 我通过点击当天的链接(可能是http://www.melsflighttimes.com/Flights/2008-11-21 )查找特定日期的航班,然后想通过电子邮件将该链接发送给我的同事美国。 The only way that we could guarantee that we will both be looking at the same page of data is if the InvariantCulture is used. 我们可以保证我们将同时查看同一页数据的唯一方法是使用InvariantCulture。 By contrast, if I'm using a form to book my flight, everything is happening in a tight cycle. 相比之下,如果我使用表格预订我的航班,一切都在紧张的周期中发生。 The data can respect the CurrentCulture when it is written to the form, and so needs to respect it when coming back from the form. 当数据写入表单时,数据可以尊重CurrentCulture,因此在从表单返回时需要尊重它。

I would globally set your cultures. 我会全球设定你的文化。 ModelBinder pick that up! ModelBinder选择了!

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

Or you just change this for this page. 或者您只需更改此页面。
But globally in web.config I think is better 但是在web.config中我认为更好

I've been having the same issue with short date format binding to DateTime model properties. 我一直遇到与DateTime模型属性绑定的短日期格式相同的问题。 After looking at many different examples (not only concerning DateTime) I put together the follwing: 在查看了许多不同的例子(不仅仅是关于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;
            }
        }
    }
}

To keep with the way that routes etc are regiseterd in the Global ASAX file I also added a new sytatic class to the App_Start folder of my MVC4 project named CustomModelBinderConfig: 为了与在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());
        }
    }
}

I then just call the static RegisterCustomModelBinders from my Global ASASX Application_Start like this: 然后我从我的Global ASASX Application_Start中调用静态RegisterCustomModelBinders,如下所示:

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

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

An important note here is that if you write a DateTime value to a hiddenfield like this: 这里一个重要的注意事项是,如果您将DateTime值写入这样的隐藏字段:

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

I did that and the actual value on the page was in the format "MM/dd/yyyy hh:mm:ss tt" instead of "dd/MM/yyyy hh:mm:ss tt" like I wanted. 我这样做了,页面上的实际值是“MM / dd / yyyy hh:mm:ss tt”而不是“dd / MM / yyyy hh:mm:ss tt”,就像我想要的那样。 This caused my model validation to either fail or return the wrong date (obviously swapping the day and month values around). 这导致我的模型验证失败或返回错误的日期(显然交换日期和月份值)。

After a lot of head scratching and failed attempts the solution was to set the culture info for every request by doing this in the Global.ASAX: 经过大量的努力和尝试失败后,解决方案是在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;
}

It won't work if you stick it in Application_Start or even Session_Start since that assigns it to the current thread for the session. 如果你将它粘贴在Application_Start甚至Session_Start中它将无法工作,因为它将它分配给会话的当前线程。 As you well know, web applications are stateless so the thread that serviced your request previously is ot the same thread serviceing your current request hence your culture info has gone to the great GC in the digital sky. 众所周知,Web应用程序是无状态的,因此先前为您的请求提供服务的线程与服务当前请求的线程相同,因此您的文化信息已转移到数字天空中的GC。

Thanks go to: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/ 谢谢你: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/2468447/578208

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

It going to be slightly different in MVC 3. 它在MVC 3中会略有不同。

Suppose we have a controller and a view with Get method 假设我们有一个控制器和一个带Get方法的视图

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

We should add ModelBinder 我们应该添加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
}

and the command in Application_Start() of Global.asax 和Global.asax的Application_Start()中的命令

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

It is also worth noting that even without creating your own model binder multiple different formats may be parsable. 值得注意的是,即使没有创建自己的模型绑定器,也可以解析多种不同的格式。

For instance in the US all the following strings are equivalent and automatically get bound to the same DateTime value: 例如,在美国,以下所有字符串都是等效的,并自动绑定到相同的 DateTime值:

/company/press/may%2001%202008 /公司/新闻/可2001%%202008

/company/press/2008-05-01 /公司/新闻/ 2008-05-01

/company/press/05-01-2008 /公司/新闻/ 2008年5月1日

I'd strongly suggest using yyyy-mm-dd because its a lot more portable. 我强烈建议使用yyyy-mm-dd因为它更便携。 You really dont want to deal with handling multiple localized formats. 你真的不想处理多种本地化格式。 If someone books a flight on 1st May instead of 5th January you're going to have big issues! 如果有人在5月1日而不是1月5日预订航班,那么您将遇到大问题!

NB: I'm not clear exaclty if yyyy-mm-dd is universally parsed in all cultures so maybe someone who knows can add a comment. 注意:如果yyyy-mm-dd在所有文化中得到普遍解析,我也不清楚,所以也许有人知道可以添加评论。

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

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

Try to use toISOString(). 尝试使用toISOString()。 It returns string in ISO8601 format. 它以ISO8601格式返回字符串。

GET method GET方法

javascript JavaScript的

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

c# C#

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

POST method POST方法

javascript JavaScript的

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

c# C#

[HttpPost]
public JsonResult Do(DateTime date)
{
     return Json(date.ToString());
}
  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;
}

I set CurrentCulture and CurrentUICulture my custom base controller 我将CurrentCultureCurrentUICulture设置为自定义基本控制器

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM