2009-02-09 14 views
119

Asp.net-MVC ahora permite el enlace implícito de los objetos DateTime. Tengo una acción en las líneas deMVC Enlace de fecha y hora con el formato de fecha incorrecto

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

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 hacer rodar mi propio archivador modelo 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 forma de alterar el formato de fecha utilizado en la carpeta de modelo predeterminada para DateTime? ¿No debería la carpeta de modelo predeterminada usar la configuración de localización de todos modos?

+0

Hey .. Sólo cambia la fecha del sistema formato- DD/MM/AAAA a DD/MM/aaaa y lo ha hecho. También tengo el mismo problema, lo resolví cambiando el formato de fecha del sistema. – banny

+0

@banny si la aplicación es global y podría ser que cada uno no tiene el mismo formato de fecha y hora, ¿cómo podría hacerlo? , no debes ir y cambiar cada fecha y hora Formato. –

+0

Ninguna de estas respuestas me está ayudando. La forma necesita ser localizada. Algunos usuarios pueden tener la fecha de una manera, otros a la inversa. Configurando algo en la web.config. o global.asax no va a ayudar. Seguiré buscando una mejor respuesta, pero una de ellas sería tratar la fecha como una cadena hasta que regrese a C#. – astrosteve

Respuesta

151

Me acabo de encontrar la respuesta a esto con algunas google más exhaustiva:

Melvyn Harbor tiene una explicación detallada de por qué MVC trabaja con las fechas de la manera que lo hace, y cómo se puede anular este si es necesario:

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

al buscar el valor de analizar, el marco se ve en un orden específico a saber:

  1. RouteData (no se muestran arriba)
  2. URI de cadena de consulta
  3. formulario de Solicitud de

Sólo el último de ellos serán conscientes sin embargo 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.

+0

Haré. Esa funcionalidad está desactivada durante 48 horas después de publicar la pregunta. –

+37

Estaré en total desacuerdo con que técnicamente esto sea correcto. La carpeta de modelo SIEMPRE debe comportarse de la misma manera que POST y GET. Si la cultura invariante es el camino a seguir para OBTENER también para POST. Cambiar el comportamiento según el verbo http no tiene sentido. –

+1

+1 votaron por la aclaración sobre el tema. –

8

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 las todas las siguientes cadenas estadounidenses son equivalentes y obtener automáticamente obligado a el mismo valor Tiempo:

/empresa/prensa/mayo% 2001% 202008

/empresa/pulse/2008-05-01

/empresa/prensa/05-01-2008

que había fuertes Sugiero 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.

+3

Como nadie dice que aaaa-MM-dd no es universal, creo que sí. – deerchao

+0

esto es, en mi opinión, mejor que usar una carpeta modelo. .datepicker ("opción", "fechaFormato", "aa-mm-dd") o simplemente configúrelo en los valores predeterminados. –

34

Definiría globalmente sus culturas. ¡ModelBinder lo recoge!

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

O simplemente cambia esto para esta página.
Pero globalmente en web.config Creo que es mejor

+26

No lo hizo para mí. La fecha todavía viene como nula si paso el 23/10/2010. – GONeale

+0

Creo que es la solución más fácil. Para mí cambia el formato en todos Date.ToString(). Creo que funcionará también con el enlace, pero no lo compré, lo siento :-( – msa7

+1

Tengo mis servidores de desarrollo dateformat en dd/MM/aaaa El modelbinder utilizó el formato MM/dd/aaaa Configurando el formato en la web .config a dd/MM/aaaa ahora obliga al modelbinder a usar el formato europeo. En mi opinión, debería usar la configuración de fecha del servidor. De todos modos, resolvió mi problema. – Karel

13

Se va a ser ligeramente diferente en MVC 3.

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

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

Debemos añadir 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()); 
+0

Este es un punto de partida decente, pero no implementa correctamente 'IModelBinder', particularmente en lo que respecta a la validación. Además, solo funciona si el nombre de 'DateTime' es * dateTime *. – Sam

+2

Además, he descubierto que 'DateTime?' S solo funciona si agrega otra llamada a 'ModelBinders.Binders.Add' con' typeof (DateTime?) '. – Sam

5

que establezca la configuración más adelante en mi MVC4 y funciona como un encanto

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

Esto funcionó para mí también. Tenga en cuenta que el elemento de globalización se encuentra en Configuración> Sistema.Web. http://msdn.microsoft.com/en-us/library/hy4kkhe0%28v=vs.85%29.aspx – Jowen

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

      } 
     } 
    } 
} 
+0

Esto no funciona. dd-MM-aaaa aún no se reconoce – jao

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

Debe enriquecer su respuesta agregando algunas explicaciones. –

+0

Desde la memoria, esto no es coherente con la forma en que funcionan los enlazadores de modelo incorporados, por lo que podría no tener el mismo comportamiento secundario, como retener el valor tipeado para la validación. – Sam

27

He tenido el mismo problema con formato de fecha corta unión a DateTime del modelo . Después de ver muchos ejemplos diferentes (no sólo en relación con DateTime) pongo juntos los siguientes aparatos:

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 mantener con la forma en que las rutas etc se regiseterd en el archivo global ASAX También he añadido una nueva clase sytatic a la App_Start carpeta de mi proyecto llamado MVC4 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()); 
     } 
    } 
} 

me llamo entonces sólo los RegisterCustomModelBinders estáticas 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 se escribe un valor DateTime a un HiddenField así:

@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 de la página estaba en el formato "MM hh/dd/aaaa: mm: ss tt "en lugar de" dd/MM/aaaa hh: mm: ss tt "como yo 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 rascarse la cabeza y los intentos fallidos La solución fue establecer la información de cultivo para cada petición al hacer esto en el 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 usted se pega en Application_Start o incluso Session_Start desde que 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.

agradecimiento 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

3

intenta utilizar toISOString(). Devuelve una cadena en 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()); 
} 
0

que establece CurrentCulture y CurrentUICulture mi 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"); 
    } 
Cuestiones relacionadas