57

How can I force the format of datetime in asp.net mvc 4 ? In display mode it shows as I want but in edit model it doesn't. I am using displayfor and editorfor and applyformatineditmode=true with dataformatstring="{0:dd/MM/yyyy}" What I have tried:

  • globalization in web.config (both of them) with my culture and uiculture.
  • modifying the culture and uiculture in application_start()
  • custom modelbinder for datetime

I have no idea how to force it and I need to input the date as dd/MM/yyyy not the default.

MORE INFO: my viewmodel is like this

 [DisplayName("date of birth")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)] public DateTime? Birth { get; set; } 

in view I use @Html.DisplayFor(m=>m.Birth) but this works as expected (I see the formatting) and to input the date I use @Html.EditorFor(m=>m.Birth) but if I try and input something like 13/12/2000 is fails with the error that it is not a valid date (12/13/2000 and 2000/12/13 are working as expected but I need dd/MM/yyyy).

The custom modelbinder is called in application_start() b/c I don't know where else.

Using <globalization/> I have tried with culture="ro-RO", uiCulture="ro" and other cultures that would give me dd/MM/yyyy. I have also tried to set it on a per thread basis in application_start() (there are a lot of examples here, on how to do this)


For all that will read this question: It seems that Darin Dimitrov's answer will work as long as I don't have client validation. Another approach is to use custom validation including client side validation. I'm glad I found this out before recreating the entire application.

3
  • Could you please provide a little more info? Your model, controller and view? Also provide an example of the different output you get between the display and editor template. Also notice that the culture is set per thread. You mentioned something about Application_Start but this is executed only once, when your application starts. What about subsequent requests? How are you setting the culture for them? Commented Jun 30, 2012 at 9:01
  • application_start in executed only once! Use application_beginRequest instead! Commented Jun 30, 2012 at 9:23
  • Nas, where would application_beginRequest be ? I only see application_start in Global. In mvc 4 things started to be a little different then mvc 3 Commented Jun 30, 2012 at 9:55

3 Answers 3

103

Ahhhh, now it is clear. You seem to have problems binding back the value. Not with displaying it on the view. Indeed, that's the fault of the default model binder. You could write and use a custom one that will take into consideration the [DisplayFormat] attribute on your model. I have illustrated such a custom model binder here: https://stackoverflow.com/a/7836093/29407


Apparently some problems still persist. Here's my full setup working perfectly fine on both ASP.NET MVC 3 & 4 RC.

Model:

public class MyViewModel { [DisplayName("date of birth")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)] public DateTime? Birth { get; set; } } 

Controller:

public class HomeController : Controller { public ActionResult Index() { return View(new MyViewModel { Birth = DateTime.Now }); } [HttpPost] public ActionResult Index(MyViewModel model) { return View(model); } } 

View:

@model MyViewModel @using (Html.BeginForm()) { @Html.LabelFor(x => x.Birth) @Html.EditorFor(x => x.Birth) @Html.ValidationMessageFor(x => x.Birth) <button type="submit">OK</button> } 

Registration of the custom model binder in Application_Start:

ModelBinders.Binders.Add(typeof(DateTime?), new MyDateTimeModelBinder()); 

And the custom model binder itself:

public class MyDateTimeModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var displayFormat = bindingContext.ModelMetadata.DisplayFormatString; var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (!string.IsNullOrEmpty(displayFormat) && value != null) { DateTime date; displayFormat = displayFormat.Replace("{0:", string.Empty).Replace("}", string.Empty); // use the format specified in the DisplayFormat attribute to parse the date if (DateTime.TryParseExact(value.AttemptedValue, displayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out date)) { return date; } else { bindingContext.ModelState.AddModelError( bindingContext.ModelName, string.Format("{0} is an invalid date format", value.AttemptedValue) ); } } return base.BindModel(controllerContext, bindingContext); } } 

Now, no matter what culture you have setup in your web.config (<globalization> element) or the current thread culture, the custom model binder will use the DisplayFormat attribute's date format when parsing nullable dates.

Sign up to request clarification or add additional context in comments.

9 Comments

This is the one I have used but with no luck. I have registered it with both datetime and datetime? It doesn't even show my error message so I don't think it's getting used.
Then I will guess you will have to show your full code allowing us to reproduce the problem, because I am using this model binder without any problems.
Out of curiosity, what type of MVC project it is (4 or 3) ?
Tested and working on both ASP.NET MVC 3 & 4 RC. I have updated my answer with my full working example. So now the question becomes: how is your setup different than mine and could you please provide a full example (as I did) allowing us to reproduce the problem you are encountering. Otherwise I don't see how I could help you any further.
I have tested your example and it's working. Funny thing is that on my full application it's still failing to bind. What I have extra from you is a @helper in the view so I can insert fields easily. I will have to recreate the entire application and see where it starts to fail. But other then this glitch (on my part) your custom model binder is perfect.
|
1

Client validation issues can occur because of MVC bug (even in MVC 5) in jquery.validate.unobtrusive.min.js which does not accept date/datetime format in any way. Unfortunately you have to solve it manually.

My finally working solution:

$(function () { $.validator.methods.date = function (value, element) { return this.optional(element) || moment(value, "DD.MM.YYYY", true).isValid(); } }); 

You have to include before:

@Scripts.Render("~/Scripts/jquery-3.1.1.js") @Scripts.Render("~/Scripts/jquery.validate.min.js") @Scripts.Render("~/Scripts/jquery.validate.unobtrusive.min.js") @Scripts.Render("~/Scripts/moment.js") 

You can install moment.js using:

Install-Package Moment.js 

Comments

0

Thanks Darin, For me, to be able to post to the create method, It only worked after I modified the BindModel code to :

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var displayFormat = bindingContext.ModelMetadata.DisplayFormatString; var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (!string.IsNullOrEmpty(displayFormat) && value != null) { DateTime date; displayFormat = displayFormat.Replace("{0:", string.Empty).Replace("}", string.Empty); // use the format specified in the DisplayFormat attribute to parse the date if (DateTime.TryParse(value.AttemptedValue, CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out date)) { return date; } else { bindingContext.ModelState.AddModelError( bindingContext.ModelName, string.Format("{0} is an invalid date format", value.AttemptedValue) ); } } return base.BindModel(controllerContext, bindingContext); } 

Hope this could help someone else...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.