ASP.NET MVC Don’t mess up with date formats and UI culture

Download source

when i first came with the requirement of parsing/displaying all dates as dd/mm/yyyy instead of mm/dd/yyyy (the default format) in my new ASP.NET mvc project, i was like huh! consider it done. Btw, isn’t it the format for culture en-AU? It sure is, cuz this is an all Australian company!

So i went on changing my Web.config to add the globalization entry and then spent some stackoverflow (what would we do without this site) moments and within next couple of hours i was on my moment WTF…

And today, i found another source during a code review, a developer trying to achieve the same thing ended up with some string properties for each DateTime property and manually parsing everytime those values being bound in the server-side. I found the developer tried all those steps available in stackoverflow by cnp(copy n paste). So i think we developers are often poised to make some changes in our code according to some instructions and hope this will solve our issue in-hand and we just make up an understanding of our own about the effect of those changes.

So i thought i better explain some of the concepts and those steps to follow along with explanation of when and why would we do things.

 

What we want to achieve

First what we want to achieve here:

-We need some clean separated generic codes which will deal with culture specific date formatting for displaying, editing and validating (both server side and client-side).
-And in order to switch to a different culture all we want to do is changing the globalization culture/uiculture value in web.config. Please note we are not internationalising the sites, which involves string resource internationalisation. But it will help making your site I18n enabled easily later. We think the formats of dates should be driven by the culture set in the web.config rather than just random dd/MM/yyyy strings all over different places.

Solution

Heres a screenshot of files added for that to a default asp.net mvc 4 web application template created from VS 2010:

image

For a demo I’ve created this simple project model:

   1: public class Project
   2: {        
   3:    public string Name { get; set; }
   4:                    
   5:    public DateTime StartDate { get; set; }
   6:    
   7:    public DateTime EndDate { get; set; }
   8: }
Look we don’t want to use those DisplayFormat attributes either as it makes our code ugly and we are again stuck with format changes everytime there needs to be a change in culture.


Here is the form View or Edit view code where we use the Html.EditorFor(DateTime):









   1: @using (Html.BeginForm()) {
   2:     @Html.ValidationSummary(true)
   3:  
   4:     <fieldset>
   5:         <legend>Project</legend>
   6:  
   7:         <div class="editor-label">
   8:             @Html.LabelFor(model => model.Name)
   9:         </div>
  10:         <div class="editor-field">
  11:             @Html.EditorFor(model => model.Name)
  12:             @Html.ValidationMessageFor(model => model.Name)
  13:         </div>
  14:  
  15:         <div class="editor-label">
  16:             @Html.LabelFor(model => model.StartDate)
  17:         </div>
  18:         <div class="editor-field">
  19:             @Html.EditorFor(model => model.StartDate)
  20:             @Html.ValidationMessageFor(model => model.StartDate)
  21:         </div>
  22:  
  23:         <div class="editor-label">
  24:             @Html.LabelFor(model => model.EndDate)
  25:         </div>
  26:         <div class="editor-field">
  27:             @Html.EditorFor(model => model.EndDate)
  28:             @Html.ValidationMessageFor(model => model.EndDate)
  29:         </div>
  30:  
  31:         <p>
  32:             <input type="submit" value="Create" />
  33:         </p>
  34:     </fieldset>
  35: }
 


It shows a form like this:



image




Where does this format come from. Please note the default web config globalization entry value is “auto” if not defined into your site, which tries to set the culture based on the browser request or server date settings etc which in my case found to be en-AU. Please note there is two things culture and uiculture. The .net framework tries to automatically set the culture but not the uiculture maybe which is default en-US and configurable by globalization. So i am using the CultureInfo.CurrentCulture settings to determine all the formattings.



So now we create the Views/Shared/EditorTemplates/DateTime.chtml to format edit view of datetime according to current culture settings:



 





   1: @using System.Globalization
   2: @model DateTime?
   3:  
   4:  
   5: @Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, String.Format("{{0: {0} }}",CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern), new { @class = "datepicker" })


Note the Mvc FormattedModelValue does not take care automatic formatting according to current culture but rather it formats the model value according to the DisplayFormat attribute that has been applied. As we are not using DisplayFormat it returns to be just DateTime.ToString() without any formats and we are formatting it based on current culture. We’ve put the datepicker class for the input as we later want a jquery datepicker intialised based on that.



So now when we submit the form, the mvc validation framework does validate the format based on current culture. So when globalization culture config is en-AU it parses dd/MM/yyyy but not M/d/yyyy which is the format for en-US culture. You can determine that by putting the MM value as greater than 12, the server side ModelState will report with an invalid date format.



So now we are left with the corresponding displayformat for date which simply is:






   1: @using System.Globalization
   2: @model DateTime?
   3:  
   4: @(Model.HasValue ? Model.Value.ToShortDateString() : "")


Because ToShortDateString takes care of formatting based on current culture.



Now for the client-side we want a jquery datepicker for inputs with datepicker class and format dates with culture and we want the client-side validation according to the culture settings. I use the jquery globalize plugin for parsing dates according to different cultures. And i want to override the default jquery date validation with Globalize.parse call to parse date strings. In order to conveniently use the scripts I’ve defined this Razor helper:



 





   1: @helper GlobalDateScript(string culture) {
   2:     
   3:     <script type="text/javascript" src="@String.Format("/Scripts/globalize/globalize.culture.{0}.js", culture)"></script> 
   4:     <script type="text/javascript">
   5:         Globalize.culture('@culture');
   6:  
   7:         //overriding client side jquery date validation
   8:         $.validator.methods.date = function (value, element) {
   9:             //console.log(value);
  10:             if (value == '' || Globalize.parseDate(value)) {
  11:                 return true;
  12:             }
  13:             return false;
  14:         }; 
  15:  
  16:         
  17:         // this is because jquery datepicker expects a different dateformat string than .NET
  18:         var formats = {                 
  19:                         'en-US': 'm/dd/yy', 
  20:                         'en-AU': 'dd/mm/yy'
  21:                        };
  22:         $('.datepicker').datepicker({ dateFormat: formats['@culture'] });
  23:  
  24:     </script>
  25: }


The helper takes as argument the culture string (i.e. en-AU, en-US) and it does the following:





  • includes corresponding culture files for jquery globalize

  • sets current jquery culture and overrides jquery date validation to parse date with Globalize

  • initialise datepicker with correct formatting. Note, we are using a lookup dictionary for culture to jquery datepicker format string as the formatstring the jquery datepicker expects does not match up with anything .net or even the globalize plugin.


So now at the end of the form view we add a call like this:






   1: @section Scripts {        
   2:     
   3:     @Scripts.Render("~/bundles/jqueryval")
   4:     @Scripts.Render("~/bundles/jqueryui")
   5:     
   6:     <script type="text/javascript" src="/Scripts/globalize/globalize.js"></script>
   7:     
   8:  
   9:     @RazorHelpers.GlobalDateScript(Culture)
  10:     @*@RazorHelpers.GlobalDateScript(UICulture)*@
  11: }


Which takes care of the rest.



I know there are lot of stuffs i didn’t talk about like long date formats for same culture, currency number formats and message internationalisation. However i think this arrangment can be easily extended to deal with other things consistently. This is just to not mess up with date formatting and providing some re-usable codes to use whenever you fall into the trap of date formatting.



Please find the source of the VS 2010 solution here in case you struggle with any details.

Comments

Unknown said…
This comment has been removed by the author.
Unknown said…
Thanks a lot!
Now I was successful with your code!
Dr not-so-evil said…
Worked like a charm... many thanks (also from Australia)!!!

Popular Posts