Прив’язка 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.