asp.net-mvc MVC DateTime 绑定日期格式不正确
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/528545/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MVC DateTime binding with incorrect date format
提问by Sam Wessel
Asp.net-MVC now allows for implicit binding of DateTime objects. I have an action along the lines of
Asp.net-MVC 现在允许隐式绑定 DateTime 对象。我有一个行动
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.
这成功地将字符串从 ajax 调用转换为 DateTime。但是,我们使用日期格式 dd/MM/yyyy;MVC 正在转换为 MM/dd/yyyy。例如,使用字符串 '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? Shouldn't the default model binder use your localisation settings anyway?
有什么方法可以更改 DateTime 的默认模型绑定器中使用的日期格式?无论如何,默认模型绑定器不应该使用您的本地化设置吗?
回答by Sam Wessel
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 Harbor 详细解释了为什么 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:
- RouteData (not shown above)
- URI query string
- 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.
在查找要解析的值时,框架按特定顺序查找,即:
- RouteData(上面未显示)
- URI 查询字符串
- 申请表
然而,只有最后一个才是文化意识。从本地化的角度来看,这是有充分理由的。想象一下,我编写了一个 Web 应用程序,显示我在线发布的航空公司航班信息。我通过单击当天的链接(可能类似于http://www.melsflighttimes.com/Flights/2008-11-21)来查找某个日期的航班,然后想将该链接通过电子邮件发送给我的同事美国。我们可以保证我们将查看同一页数据的唯一方法是使用 InvariantCulture。相比之下,如果我使用表格来预订我的航班,那么一切都在一个紧凑的循环中发生。数据在写入表单时可以尊重 CurrentCulture,因此从表单返回时需要尊重它。
回答by Peter Gfader
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 中我认为全局更好
回答by WernerVA
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:
我在将短日期格式绑定到 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;
}
}
}
}
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:
为了保持路由等在全局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. This caused my model validation to either fail or return the wrong date (obviously swapping the day and month values around).
我这样做了,页面上的实际值采用格式“MM/dd/yyyy hh:mm:ss tt”而不是我想要的“dd/MM/yyyy hh:mm:ss tt”。这导致我的模型验证失败或返回错误的日期(显然交换了日期和月份值)。
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. 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.
如果您将其粘贴在 Application_Start 甚至 Session_Start 中,它将不起作用,因为它将它分配给会话的当前线程。众所周知,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
回答by Dmitry
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());
回答by Simon_Weaver
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 sameDateTime value:
例如,在美国,以下所有字符串都是等效的,并且会自动绑定到相同的DateTime 值:
/company/press/may%2001%202008
/company/press/2008-05-01
/company/press/05-01-2008
/公司/新闻/可能%2001%202008
/公司/新闻/2008-05-01
/公司/新闻/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!
我强烈建议使用 yyyy-mm-dd 因为它更便携。您真的不想处理多种本地化格式。如果有人在 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 是否在所有文化中都被普遍解析,所以也许知道的人可以添加评论。
回答by rnofenko
Try to use toISOString(). It returns string in ISO8601 format.
尝试使用 toISOString()。它以 ISO8601 格式返回字符串。
GET method
获取方法
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());
}
回答by JeeShen Lee
I set the below config on my MVC4 and it works like a charm
我在我的 MVC4 上设置了以下配置,它就像一个魅力
<globalization uiCulture="auto" culture="auto" />
回答by tobias
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);
}
}
}
}
}
}
回答by Teth
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;
}
回答by Korayem
I set CurrentCultureand CurrentUICulturemy custom base controller
我设置CurrentCulture和CurrentUICulture我的自定义基本控制器
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
}

