Format Date in ASP.NET MVC using Custom Model Binders

Problem:
Recently I came across an unusual problem while I am trying to allow the user to enter Date in dd/mm/yyyy format. I added attribute in jQuery Calendar control so that it gives the date in above format. But when it is submitted to appropriate action I am getting date as 1/1/0001.

Solution:
After trying a couple of things like specifying DataFormatString and making ApplyFormatInEditMode to true in DisplayFormat, I came to a conclusion that Custom Model Binder is solution to this.

Model:

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

Controller:

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

View:

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

Application_Start: We need to register the custom model binder in Application_Start

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

Custom Model Binder:

public class CustomDateModelBinder : 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);
            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);
    }
}

Conclusion:
With this solution, irrespective of what culture you have specified in web.config or current thread culture, the custom model binder will override them and use the DisplayFormat attribute’s date format when parsing dates. This works perfectly in ASP.NET MVC 3 & 4.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s