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

发布于 2024-07-14 03:28:29 字数 473 浏览 9 评论 0原文

Asp.net-MVC 现在允许 DateTime 对象的隐式绑定。 我有一个类似的操作,

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

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

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

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

Asp.net-MVC now allows for implicit binding of DateTime objects. 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. However, we use the date format dd/MM/yyyy; MVC is converting to 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.

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.

Is there any way to alter the date format used in the default model binder for DateTime? Shouldn't the default model binder use your localisation settings anyway?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

记忆里有你的影子 2024-07-21 03:28:29

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

Melvyn Harbor 详细解释了为什么 MVC 以这种方式处理日期,以及如何在必要时覆盖它:

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

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

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

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

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:

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)
  2. URI query string
  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. 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. 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.

雄赳赳气昂昂 2024-07-21 03:28:29

我会在全球范围内设定你们的文化。 ModelBinder 来接吧!

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

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

I would globally set your cultures. ModelBinder pick that up!

  <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

等风来 2024-07-21 03:28:29

我在将短日期格式绑定到 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 文件夹中添加了一个新的 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());
        }
    }
}

然后,我只需从我的 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/

加里克 - https://stackoverflow.com/a/2468447/578208

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

I've been having the same issue with short date format binding to DateTime model properties. After looking at many different examples (not only concerning DateTime) I put together the follwing:

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:

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:

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:

@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. 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:

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. 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.

Thanks go to:
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

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

坐在坟头思考人生 2024-07-21 03:28:29

MVC 3 中会略有不同。

假设我们有一个控制器和一个具有 Get 方法的视图,

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

我们应该

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() 中添加 ModelBinder 和命令

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

It going to be slightly different in MVC 3.

Suppose we have a controller and a view with Get method

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

We should add 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

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
梦里°也失望 2024-07-21 03:28:29

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

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

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:

/company/press/may%2001%202008

/company/press/2008-05-01

/company/press/05-01-2008

I'd strongly suggest using yyyy-mm-dd because its a lot more portable. 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!

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.

寄与心 2024-07-21 03:28:29

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

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

I set the below config on my MVC4 and it works like a charm

<globalization uiCulture="auto" culture="auto" />
心头的小情儿 2024-07-21 03:28:29

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

Try to use toISOString(). It returns string in ISO8601 format.

GET method

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 method

javascript

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

c#

[HttpPost]
public JsonResult Do(DateTime date)
{
     return Json(date.ToString());
}
魔法唧唧 2024-07-21 03:28:29
  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 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);
                    }
                }

            }
        }
    }
}
梓梦 2024-07-21 03:28:29
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;
}
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;
}
白色秋天 2024-07-21 03:28:29

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

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

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

I set CurrentCulture and CurrentUICulture my custom base controller

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

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文