Format datetime in asp.net mvc 4
Asked Answered
M

3

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.

Mosesmosey answered 30/6, 2012 at 8:57 Comment(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?Outmost
application_start in executed only once! Use application_beginRequest instead!Plyler
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 3Mosesmosey
O
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://mcmap.net/q/490017/-asp-net-mvc3-datetime-format


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.

Outmost answered 30/6, 2012 at 9:20 Comment(9)
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.Mosesmosey
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.Outmost
Out of curiosity, what type of MVC project it is (4 or 3) ?Mosesmosey
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.Outmost
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.Mosesmosey
Perfect, no more UK dates interpreted as US dates from jQuery UI's date picker in MVC 5.2.2.Modernistic
I have one question. Does it work with both DateTime and DatTime? types?Jaquelynjaquenetta
Does this solution works with jquery datepicker ? ... and where to add this custom model binderMesmerism
Thank you sir very much for your help ... I wish if I could + this post infinite number of times :))) . Thanks againMesmerism
J
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
Janitress answered 8/3, 2017 at 15:57 Comment(0)
P
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...

Pice answered 11/8, 2016 at 8:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.