Связывание MVC DateTime с неправильным форматом даты

Теперь Asp.net-MVC допускает неявное связывание объектов DateTime. У меня есть действие в соответствии с

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

Это успешно преобразует строку из ajax-вызова в DateTime. Однако мы используем формат даты dd / MM / yyyy; MVC преобразуется в MM / dd / yyyy. Например, при отправке вызова в действие со строкой ’09 / 02/2009 ‘в DateTime из ’02 / 09/2009 00:00:00’ или 2 сентября в наших локальных настройках.

Я не хочу откатывать свое собственное связующее устройство для формата даты. Но, кажется, нет необходимости менять действие, чтобы принять строку, а затем использовать DateTime.Parse, если MVC способен сделать это для меня.

Есть ли способ изменить формат даты, используемый в привязке модели по умолчанию для DateTime? Не следует ли в любом случае использовать привязку к модели по умолчанию?

Я только что нашел ответ на этот вопрос с более исчерпывающим поиском:

Melvyn Harbor подробно объясняет, почему 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, когда они записываются в форму, и поэтому должны уважать ее при возврате из формы.

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

    

Или просто измените это для этой страницы.
Но во всем мире в web.config я думаю, что лучше

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

Чтобы сохранить путь к тому, что маршруты и т. Д. Регистрируются в глобальном файле ASAX, я также добавил новый class sytatic в папку 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 из моего Глобального 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, поскольку он назначает его текущему streamу сеанса. Как вы хорошо знаете, веб-приложения не имеют статуса, поэтому stream, который обслуживал ваш запрос ранее, является тем же самым streamом, который обслуживает ваш текущий запрос, поэтому ваша информация о культуре перешла к большому GC в цифровом небе.

Спасибо: Иван Златев – 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

Это будет немного отличаться в MVC 3.

Предположим, что у нас есть controller и представление с методом 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()); 

Также стоит отметить, что даже без создания собственного связующего устройства несколько разных форматов могут быть parsingчивыми.

Например, в США все следующие строки эквивалентны и автоматически привязаны к одному и тому же значению DateTime:

/ Компания / Пресс / май% 2001% 202008

/ Компания / Пресса / 2008-05-01

/ Компания / Пресса / 05.01.2008

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

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

Я установил ниже конфигурацию на мой MVC4, и он работает как шарм

  

Попробуйте использовать 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()); } 
  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; } 

Я установил CurrentCulture и CurrentUICulture своим настраиваемым базовым controllerом

  protected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB"); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB"); } 
  • Как я могу поместить целое число с нулями слева?
  • VBA: преобразовать текст в число
  • Intellij reformat для сохранения файлов
  • Формат номера до 2 знаков после запятой
  • Форматирование дат с помощью scale_x_date в ggplot2
  • Форматировать двойное значение в научной нотации
  • Как печатать форматированные значения BigDecimal?
  • Вставить текст в textarea с помощью jQuery
  • Подчеркивание текста в UIButton
  • Как я могу String.Format объект TimeSpan с пользовательским форматом в .NET?
  • Форматирование DATE в oracle
  • Давайте будем гением компьютера.