Ligação DateTime MVC com formato de data incorreto

O asp.net-MVC agora permite a binding implícita de objects DateTime. Eu tenho uma ação ao longo das linhas de

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

Isso converte com êxito uma string de uma chamada ajax em um DateTime. No entanto, usamos o formato de data dd / mm / aaaa; O MVC está convertendo para MM / dd / aaaa. Por exemplo, enviar uma chamada para a ação com uma string ’09 / 02/2009 ‘resulta em um DateTime de ’02 / 09/2009 00:00:00’ ou em 2 de setembro em nossas configurações locais.

Eu não quero rolar meu próprio fichário de modelo por causa de um formato de data. Mas parece desnecessário ter que alterar a ação para aceitar uma string e depois usar DateTime.Parse se o MVC for capaz de fazer isso para mim.

Existe alguma maneira de alterar o formato de data usado no fichário de modelo padrão para DateTime? O fichário de modelo padrão não deveria usar suas configurações de localização?

Acabei de encontrar a resposta para isso com um pouco mais de googling exaustivo:

Melvyn Harbour tem uma explicação detalhada de por que o MVC trabalha com datas da maneira que faz, e como você pode sobrescrevê-lo se necessário:

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

Ao procurar o valor a analisar, a estrutura é exibida em uma ordem específica, a saber:

  1. RouteData (não mostrado acima)
  2. String de consulta de URI
  3. Formulário de solicitação

Somente o último deles será consciente da cultura. Há uma boa razão para isso, do ponto de vista da localização. Imagine que eu escrevi um aplicativo da web mostrando informações de voos de companhias aéreas que publico on-line. Eu olho para os vôos em uma determinada data, clicando em um link para esse dia (talvez algo como http://www.melsflighttimes.com/Flights/2008-11-21 ), e, em seguida, quero enviar esse link por email para o meu colega em os EUA. A única maneira que poderíamos garantir que ambos estaremos olhando para a mesma página de dados é se a InvariantCulture for usada. Por outro lado, se estou usando um formulário para reservar meu voo, tudo está acontecendo em um ciclo apertado. Os dados podem respeitar o CurrentCulture quando ele é gravado no formulário e, portanto, precisa respeitá-lo quando voltar do formulário.

Eu definiria globalmente suas culturas. ModelBinder pegar isso!

    

Ou você acabou de mudar isso para esta página.
Mas globalmente no web.config eu acho que é melhor

Eu tenho tido o mesmo problema com a vinculação de formato de data abreviada para as propriedades do modelo DateTime. Depois de olhar para muitos exemplos diferentes (não apenas sobre o DateTime), eu juntei o seguinte:

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

Para manter o caminho que as rotas etc são regiseterd no arquivo ASAX Global, também adicionei uma nova class sytatic à pasta App_Start do meu projeto MVC4 chamado 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()); } } } 

Eu apenas chamo o RegisterCustomModelBinders estático do meu Global ASASX Application_Start assim:

 protected void Application_Start() { /* bla blah bla the usual stuff and then */ CustomModelBindersConfig.RegisterCustomModelBinders(); } 

Uma observação importante aqui é que se você escrever um valor DateTime em um campo oculto como este:

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

Eu fiz isso e o valor real na página estava no formato “MM / dd / aaaa hh: mm: ss tt” em vez de “dd / mm / aaaa hh: mm: ss tt” como eu queria. Isso fez com que a validação do meu modelo falhasse ou retornasse a data errada (obviamente, trocando os valores de dia e mês ao redor).

Depois de muitos arranhões na cabeça e tentativas malsucedidas, a solução era definir as informações de cultura para cada solicitação fazendo isso no 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; } 

Não funcionará se você colocá-lo em Application_Start ou mesmo em Session_Start, desde que o atribua ao thread atual da session. Como você bem sabe, os aplicativos da Web são sem estado, portanto, o thread que atendeu a sua solicitação anteriormente é o mesmo thread que atende ao seu pedido atual, portanto, suas informações culturais foram transferidas para o excelente GC no céu digital.

Obrigado ir para: 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

Vai ser um pouco diferente no MVC 3.

Suponha que temos um controlador e uma visualização com o método Get

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

Nós devemos adicionar 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 } 

e o comando em Application_Start () de Global.asax

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

Também vale a pena notar que, mesmo sem criar seu próprio modelo, vários formatos diferentes podem ser analisados.

Por exemplo, nos EUA, todas as strings a seguir são equivalentes e automaticamente vinculadas ao mesmo valor DateTime:

/ company / press / may% 2001% 202008

/ empresa / imprensa / 2008-05-01

/ empresa / imprensa / 05-01-2008

Eu sugiro fortemente o uso de aaaa-mm-dd porque é muito mais portátil. Você realmente não quer lidar com vários formatos localizados. Se alguém reservar um voo no dia 1º de maio em vez de no dia 5 de janeiro, você terá grandes problemas!

NB: Eu não sou claro se dd-aaaa-mm-dd é universalmente analisada em todas as culturas, então talvez alguém que sabe pode adicionar um comentário.

Eu defino a configuração abaixo no meu MVC4 e funciona como um encanto

  

Tente usar toISOString (). Ele retorna string no formato ISO8601.

Método 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); } 

Método POST

javascript

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

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

Eu defino CurrentCulture e CurrentUICulture meu controlador de base personalizado

  protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB"); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB"); }