Enlace MVC DateTime con formato de fecha incorrecto

Asp.net-MVC ahora permite el enlace implícito de los objetos DateTime. Tengo una acción a lo largo de las líneas de

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

Esto convierte con éxito una cadena de una llamada ajax a DateTime. Sin embargo, usamos el formato de fecha dd / MM / aaaa; MVC se está convirtiendo a MM / dd / aaaa. Por ejemplo, enviar una llamada a la acción con una cadena ’09 / 02/2009 ‘da como resultado un DateTime de ’02 / 09/2009 00:00:00’, o el 2 de septiembre en nuestra configuración local.

No quiero rodar mi propia carpeta de modelos por el formato de fecha. Pero parece innecesario tener que cambiar la acción para aceptar una cadena y luego usar DateTime.Parse si MVC es capaz de hacer esto por mí.

¿Hay alguna manera de alterar el formato de fecha utilizado en el cuaderno de modelo predeterminado para DateTime? ¿No debería la carpeta de modelo predeterminada usar la configuración de localización de todos modos?

Acabo de encontrar la respuesta a esto con un google más exhaustivo:

Melvyn Harbour tiene una explicación completa de por qué MVC funciona con las fechas de la forma en que lo hace, y cómo puede anular esto si es necesario:

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

Cuando se busca el valor para analizar, el marco se ve en un orden específico, a saber:

  1. RouteData (no se muestra arriba)
  2. Cadena de consulta URI
  3. Formulario de solicitud

Sin embargo, solo el último de ellos será consciente de la cultura. Hay una muy buena razón para esto, desde una perspectiva de localización. Imagine que he escrito una aplicación web que muestra la información de vuelo de la línea aérea que publico en línea. Busco vuelos en una fecha determinada haciendo clic en un enlace para ese día (tal vez algo así como http://www.melsflighttimes.com/Flights/2008-11-21 ), y luego quiero enviar ese enlace a mi colega en los Estados Unidos. La única forma en que podemos garantizar que los dos buscaremos en la misma página de datos es si se usa InvariantCulture. Por el contrario, si estoy usando un formulario para reservar mi vuelo, todo está sucediendo en un ciclo apretado. Los datos pueden respetar CurrentCulture cuando se escriben en el formulario, por lo que deben respetarse al volver del formulario.

Yo establecería globalmente sus culturas. ¡ModelBinder lo recoge!

    

O simplemente cambia esto para esta página.
Pero a nivel mundial en web.config, creo que es mejor

He tenido el mismo problema con el enlace de formato de fecha corta a las propiedades del modelo DateTime. Después de mirar muchos ejemplos diferentes (no solo con respecto a DateTime), armé lo siguiente:

 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 seguir con la forma en que se enrutan las rutas, etc., en el archivo ASAX global, también agregué una nueva clase sétática a la carpeta App_Start de mi proyecto MVC4 llamado 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()); } } } 

Luego, llamo a la staticCostMustomModelBinders estática de mi Global ASASX Application_Start así:

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

Una nota importante aquí es que si escribe un valor de DateTime en un campo oculto como este:

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

Lo hice y el valor real en la página estaba en el formato “MM / dd / aaaa hh: mm: ss tt” en lugar de “dd / MM / aaaa hh: mm: ss tt” como quería. Esto causó que mi validación de modelo fallara o devolviera la fecha incorrecta (obviamente intercambiando los valores de día y mes).

Después de una gran cantidad de arañazos e bashs fallidos, la solución fue establecer la información de cultivo para cada solicitud haciendo esto en 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; } 

No funcionará si lo inserta en Application_Start o incluso Session_Start ya que eso lo asigna al hilo actual de la sesión. Como bien sabe, las aplicaciones web son apátridas, por lo que el hilo que atendió su solicitud previamente es del mismo hilo que atiende su solicitud actual, por lo tanto, su información cultural ha pasado al gran GC en el cielo digital.

Gracias a: 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

Va a ser ligeramente diferente en MVC 3.

Supongamos que tenemos un controlador y una vista con el método Get

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

Deberíamos agregar 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 } 

y el comando en Application_Start () de Global.asax

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

También vale la pena señalar que incluso sin crear su propia carpeta de modelos, se pueden analizar múltiples formatos diferentes.

Por ejemplo, en los EE. UU., Todas las siguientes cadenas son equivalentes y se vinculan automáticamente al mismo valor de DateTime:

/ company / press / may% 2001% 202008

/ compañía / prensa / 2008-05-01

/ compañía / prensa / 05-01-2008

Recomiendo utilizar aaaa-mm-dd porque es mucho más portátil. Realmente no desea lidiar con el manejo de múltiples formatos localizados. Si alguien reserva un vuelo el 1 de mayo en lugar del 5 de enero, ¡tendrá grandes problemas!

NB: No estoy muy claro si aaaa-mm-dd se analiza universalmente en todas las culturas, así que tal vez alguien que sabe puede agregar un comentario.

Configuro la configuración siguiente en mi MVC4 y funciona como un encanto

  

Intenta usar toISOString (). Devuelve una cadena en formato ISO8601.

Método GET

javascript

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

do#

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

do#

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

Configuré CurrentCulture y CurrentUICulture mi controlador 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"); }