Прив’язка MVC DateTime з невірним форматом дати


132

Тепер Asp.net-MVC дозволяє неявно прив’язувати об'єкти DateTime. У мене є дія в руслі

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

Це успішно перетворює рядок з виклику ajax в DateTime. Однак ми використовуємо формат дати dd / MM / yyyy; MVC перетворюється на MM / dd / yyyy. Наприклад, надсилання дзвінка до дії з рядком "09 / 02/2009 "призводить до дати" 02/09/2009 00:00:00 "або 2 вересня в наших локальних налаштуваннях.

Я не хочу згортати власну палітурку моделі заради формату дати. Але видається необхідним змінювати дію, щоб прийняти рядок, а потім використовувати DateTime.Parse, якщо MVC здатний зробити це для мене.

Чи є спосіб змінити формат дати, який використовується у палітурці для замовчування моделі для DateTime? Чи не повинен в’яжуча модель за замовчуванням все-таки використовувати налаштування локалізації?


Привіт .. Просто змініть формат дати вашої системи - DD / MM / yyyy на MM / DD / yyyy і зробив це .. У мене також є така ж проблема, і я вирішив це, змінивши формат дати системи.
— заборона

@banny, якщо додаток є глобальним і може бути, кожен має не той самий формат часу дати, як ви могли це зробити? , ви не гадаєте, щоб перейти та змінити всі дати часу Формат ..
— Раві Мехта

Жодна з цих відповідей мені не допомагає. Форму потрібно локалізувати. Деякі користувачі можуть мати дату в одну сторону, інші - в інший спосіб. Налаштування чогось у web.config. або global.asax не допоможе. Я буду продовжувати шукати кращу відповідь, але одним із способів було б просто розглянути дату як рядок, поки я не повернусь до c #.
— астростев

Відповіді:


164

Я щойно знайшов відповідь на це дещо вичерпнішим гуглінням:

У гавані Мельвіна є ґрунтовне пояснення того, чому MVC працює з датами, як це відбувається, і як ви можете це змінити за потреби:

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

Шукаючи значення для розбору, рамки виглядають у певному порядку, а саме:

  1. RouteData (не показано вище)
  2. Рядок запиту URI
  3. Форма запиту

Однак остання з них буде відома лише культурі. Для цього є дуже вагома причина з точки зору локалізації. Уявіть, що я написав веб-програму, де відображалася інформація про рейси авіакомпанії, яку я публікую в Інтернеті. Я шукаю рейси в певну дату, натиснувши посилання на цей день (можливо, щось на зразок http://www.melsflighttimes.com/Flights/2008-11-21 ), а потім хочу надіслати це посилання моєму колезі в США. Єдиний спосіб, який ми могли б гарантувати, що ми обидва будемо переглядати одну і ту ж сторінку даних, це якщо використовувати InvariantCulture. На противагу цьому, якщо я використовую форму для бронювання свого польоту, все відбувається в тісному циклі. Дані можуть поважати CurrentCulture, коли вони записуються у форму, і тому вони повинні їх поважати при поверненні з форми.


Зроблю. Ця функція вимкнена протягом 48 годин після публікації питання.
— Сем Вессел

43
Я категорично не погоджуюся, що технічно це правильно. В’яжуча модель повинна ВЖЕ завжди поводитись однаково з POST та GET. Якщо інваріантна культура - це шлях для GET, зробіть це і для POST. Змінювати поведінку залежно від http дієслова - це не має сенсу.
— Барт Калікто

У мене є питання, наш веб-сайт розміщений в іншій країні, йому потрібен MM/dd/yyyyформат, інакше він відображає помилку перевірки The field BeginDate must be a date., як я можу зробити так, щоб прийняти dd/MM/yyyyформат?
— shaijut

Параметр URL повинен бути однозначним, як, наприклад, стандартне форматування ISO. Тоді налаштування культури не мали б значення.
— nforss

це дає посилання, що пояснює причину, але насправді не забезпечує найпростішого вирішення цієї проблеми (наприклад, розміщення рядка дати ISO із сценарію), такий спосіб завжди працює, і нам не потрібно піклуватися про встановлення конкретної культури на сервер або переконайтеся, що формат часу ідентичний між сервером і клієнтом.
— Безнадійне

36

Я б глобально встановив ваші культури. ModelBinder забрати це!

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

Або ви просто зміните це для цієї сторінки.
Але глобально в web.config я думаю, що це краще


27
Не для мене. Ця дата все ще стає недійсною, якщо я пройду 23.10.2010.
— GONeale

Я думаю, що це найпростіше рішення. Для мене це змінює формат у всіх Date.ToString (). Думаю, він також буде працювати з прив’язкою, але не перевірив, вибачте :-(
— msa.im

1
У мене на серверах розробників датформату встановлено значення dd / MM / yyyy Modelbinder використовував формат MM / dd / yyyy Встановлення формату в web.config на dd / MM / yyyy тепер змушує модельний палітур використовувати європейський формат. На мою думку, він повинен використовувати налаштування дати сервера. Все одно ти вирішив мою проблему.
— Карел

Це працювало для мене ідеально ... якось це було дивно, хоча я знаю, що мій сервер додатків знаходиться у Великобританії, де працює ОС Великобританії ...: /
— Tallmaris

Відмінно працював у MVC4 на веб-сайтах Azure
— Matty Bear

31

У мене виникла та сама проблема із прив’язкою формату коротких дат до властивостей моделі 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;
            }
        }
    }
}

Для того, щоб маршрути тощо реєструвалися у файлі Global ASAX, я також додав новий ситатичний клас до папки App_Start мого проекту 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());
        }
    }
}

Тоді я просто закликаю статичні RegisterCustomModelBinders з мого Global ASASX Application_Start так:

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

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

Тут важлива примітка: якщо ви пишете значення DateTime в приховане поле, як це:

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

Я це зробив, і фактичне значення на сторінці було у форматі "MM / dd / yyyy hh: mm: ss tt" замість "dd / MM / yyyy hh: mm: ss tt", як я хотів. Це спричинило збій моєї моделі або повернення неправильної дати (очевидно, міняючи значення дня та місяця навколо).

Після багатьох подряпин і невдалих спроб рішенням було встановити інформацію про культуру для кожного запиту, зробивши це в 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;
}

Він не працюватиме, якщо вставити його в Application_Start або навіть Session_Start, оскільки він призначає його поточній темі сеансу. Як ви добре знаєте, веб-додатки не мають статусу, тому потік, який раніше обслуговував ваш запит, є тим самим потоком, що обслуговує ваш поточний запит, отже, інформація про культуру перейшла до великої ГК у цифровому небі.

Дякуємо: Іван Златев - 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/11903896/578208


13

Це буде дещо іншим у MVC 3.

Припустимо, у нас є контролер і перегляд методом Get

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

Ми повинні додати 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
}

і команду в Application_Start () Global.asax

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

Це гідний вихідний пункт, але він неправильно реалізується IModelBinder, особливо щодо перевірки. Крім того, він працює лише у тому випадку, якщо ім'ям DateTimeє dateTime .
— Сем,

2
Крім того , я виявив , що DateTime?S тільки роботу , якщо додати ще один виклик ModelBinders.Binders.Addз typeof(DateTime?).
— Сем

8

Варто також зазначити, що навіть без створення власної модельної палітурки декілька різних форматів можуть бути доступними.

Наприклад, у США всі наступні рядки еквівалентні і автоматично прив'язуються до того самого значення DateTime:

/ компанія / преса / травень 2001% 202008

/ компанія / преса / 2008-05-01

/ компанія / преса / 01.05.2008

Я настійно пропоную використовувати yyyy-mm-dd, оскільки це набагато більш портативно. Ви дійсно не хочете мати справу з обробкою декількох локалізованих форматів. Якщо хтось забронює рейс 1 травня замість 5 січня, у вас виникнуть великі проблеми!

NB: Я не чіткий конкретно, якщо yyyy-mm-dd є універсальним аналізом у всіх культурах, тому, можливо, хтось, хто знає, може додати коментар.


3
Оскільки ніхто не каже, що yyyy-MM-dd не є універсальним, я думаю, що це так.
— deerchao

це, на мій погляд, краще, ніж використання в’яжучої моделі. .datepicker ("параметр", "dateFormat", "yy-mm-dd") або просто встановіть його за замовчуванням.
— Барт Калікто

6

Спробуйте використовувати toISOString (). Він повертає рядок у форматі ISO8601.

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

Метод POST

javascript

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

c #

[HttpPost]
public JsonResult Do(DateTime date)
{
     return Json(date.ToString());
}


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

            }
        }
    }
}

Це не працює. дд-мм-рррр ще не визнається
— Джао

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
Ви повинні зробити свою відповідь більш насиченою, додавши пояснення.
— Олександр Лавуа

З пам’яті це не узгоджується з тим, як працюють вбудовані моделі в'яжучих файлів, тому може не мати такої ж вторинної поведінки, як збереження набраного значення для перевірки.
— Сем

1

Я встановив 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");
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.