130 votes

MVC DateTime liaison avec le format de date incorrect

Asp.net MVC permet maintenant de l'implicite de la liaison des objets DateTime. J'ai une action dans le sens de

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

Ce succès convertit une chaîne de caractères à partir d'un appel ajax dans un DateTime. Cependant, nous utilisons le format de date jj/MM/aaaa; MVC est la conversion à jj/MM/aaaa. Par exemple, la présentation d'un appel à l'action avec une corde " 09/02/2009 résultats dans un DateTime '02/09/2009 00:00:00', ou 2 septembre dans nos locaux de paramètres.

Je ne veux pas rouler mon propre modèle de classeur pour l'amour d'un format de date. Mais il semble inutile d'avoir à modifier l'action à accepter une chaîne, puis utiliser DateTime.Analyser si MVC est capable de faire cela pour moi.

Est-il possible de modifier le format de date utilisé par défaut dans le modèle de classeur pour DateTime? Ne pas le modèle de classeur par défaut utiliser vos paramètres de localisation de toute façon?

163voto

Sam Wessel Points 4906

Je viens de trouver la réponse à cette avec un peu plus exhaustive googler:

Melvyn Port a une très bonne explication de pourquoi MVC travaille avec les dates de la façon dont il le fait, et comment vous pouvez la remplacer si nécessaire:

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

Lors de la recherche de la valeur à l'analyse, le cadre se regarde dans un ordre précis, à savoir:

  1. RouteData (non illustré ci-dessus)
  2. Chaîne de requête URI
  3. Formulaire de demande de

Seule la dernière de ces sera la culture compte cependant. Il y a une très bonne raison pour cela, à partir d'un point de vue de la localisation. Imaginez que j'ai écrit une application web montrant vol de la compagnie d'informations que je publie en ligne. Je rechercher des vols sur une certaine date en cliquant sur un lien pour la journée (peut-être quelque chose comme http://www.melsflighttimes.com/Flights/2008-11-21), puis souhaitez envoyer ce lien à mon collègue dans le NOUS. La seule façon que nous pourrions garantir que nous allons tous les deux être à la recherche à la même page de données est si InvariantCulture est utilisé. En revanche, si j'utilise un formulaire pour réserver mon vol, tout ce qui se passe dans un contexte de pénurie de cycle. Les données peuvent respecter les CurrentCulture quand il est écrit à la forme, et donc doit le respecter en rentrant de la forme.

35voto

Peter Gfader Points 3410

Je voudrais définir globalement les cultures. ModelBinder ramasser ça!

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

Ou vous venez de modifier cette page.
Mais à l'échelle mondiale dans le web.config je pense que c'est mieux

30voto

WernerVA Points 165

J'ai eu le même problème avec le format de date courte liaison de type DateTime propriétés du modèle. Après avoir regardé de nombreux exemples (il ne concerne pas seulement DateTime) j'ai mis en place les suivantes:

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

Pour garder le chemin qui les routes etc sont regiseterd dans le Global ASAX fichier j'ai également ajouté une nouvelle sytatic classe à la App_Start dossier de mon MVC4 projet nommé 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());
        }
    }
}

J'ai ensuite il suffit d'appeler la statique RegisterCustomModelBinders de mes ASASX Application_Start comme ceci:

protected void Application_Start()
{
    /* bla blah bla the usual stuff and then */

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Il est important de noter ici est que si vous écrivez une valeur de type DateTime à un hiddenfield comme ceci:

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

Je l'ai fait et la valeur réelle sur la page dans le format "MM/jj/aaaa hh:mm:ss tt" au lieu de "dd/MM/yyyy hh:mm:ss tt" comme je le voulais. Ce la cause de mon modèle de validation d'échouer ou de retour de la mauvaise date (évidemment l'échange le jour et le mois de valeurs autour).

Après beaucoup de casse-tête et de tentatives infructueuses, la solution a été de définir la culture d'info pour chaque demande de faire cela dans le monde.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;
}

Il ne fonctionnera pas si vous vous en tenez dans Application_Start ou même Session_Start depuis qu'il l'attribue à un thread en cours de la session. Comme vous le savez bien, les applications web sont apatrides, de sorte que le thread qui a réparé votre demande précédemment n'est donc pas le même thread serviceing votre demande ainsi que votre culture info est allé à la grande GC dans le ciel numérique.

Les remerciements vont à: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/

garik - http://stackoverflow.com/a/2468447/578208

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

13voto

Dmitry Points 111

Il va être légèrement différente dans MVC 3.

Supposons que nous avons un contrôleur et d'un point de vue avec la méthode Get

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

Il convient d'ajouter 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
}

et la commande dans Application_Start() de la Global.asax

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

8voto

Simon_Weaver Points 31141

Il est également intéressant de noter que, même sans la création de votre propre modèle de classeur de multiples formats peut être analysée.

Par exemple, dans le NOUS toutes les chaînes de caractères suivantes sont équivalentes et obtenez automatiquement lié à la même valeur DateTime:

/entreprise/presse/mai%2001%202008

/entreprise/presse/2008-05-01

/entreprise/presse/05-01-2008

J'avais suggèrent fortement à l'aide de aaaa-mm-jj parce que c'est beaucoup plus portable. Vous vraiment ne voulez pas faire face à la manipulation de plusieurs localisée formats. Si quelqu'un livres un vol le 1er Mai à la place du 5 janvier, vous allez avoir de gros problèmes!

NB: je ne suis pas clair exactement si aaaa-mm-jj est universellement analysée dans toutes les cultures, alors peut-être quelqu'un qui connaît pouvez ajouter un commentaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X