Tuesday, 10 May 2011

ASP.NET: Regular Expression client side date validation using "d MMMM yyyy" format

In my apps, I like to use the AJAX CalendarExtender control to allow the user to select dates from the calendar picker. I also like to allow the display of the full date format since I believe it to be more readable, for example

31 December 2012

The problem is, I find it really tough to validate this date format and even when I tried to switch to other date formats and change the localization settings in web.config, I still had no real success. I tried CompareValidators, CustomValidators and various combinations of RegularExpressionValidator but simply could not get it to work.

Eventually, I found a regular expression which pretty much does the trick. I don't believe it’s over-engineering the solution, because I could not find another solution which works.

Basically I added the regular expression to my web.config file so that it was only listed in one place, should I need to change it. Then I created a little shared function which returns the RegEx, and on Page_Load I set the ValidationExpression property of the RegularExpressionValidator controls to the result of the function, and therefore to the RegEx.

Here's the code:
Page Markup

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<asp:TextBox ID="txtStartDate" runat="server" AutoPostBack="true" ></asp:TextBox>
<asp:CalendarExtender ID="calStartDate" runat="server" TargetControlID="txtStartDate" Format="dd MMMM yyyy"></asp:CalendarExtender>

<asp:RegularExpressionValidator ID="reValStartDate" runat="server" ControlToValidate="txtStartDate"
ErrorMessage="Enter a valid date" ></asp:RegularExpressionValidator>

Code Behind

Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
reValStartDate.ValidationExpression = UIUtils.DateValidatorRegEx()
End If
End Sub

''' <summary>
''' Return the regular expression used to validate dates
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function DateValidatorRegEx() As String
Return ConfigurationManager.AppSettings("DateValidatorRegEx")
End Function


<add key="DateValidatorRegEx" value="^(?:((31 (January|March|May|July|August|October|December))|((([0-2]\d)|30) (January|March|April|May|June|July|August|September|October|November|December))|(([01]\d|2[0-8]) February))|(29 February(?=-((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))) ((1[6-9]|[2-9]\d)\d{2})$"/>

This solution isn't perfect, because the above validator does not allow for leap years (i.e. 29 February 2008 was a valid date), and I don't really like having to explicitly bind the ValidationExpression property of every date validation control in the code behind. I think it would have been better to set the ValidationExpression in the markup and directly reference the function result, but I couldn't get that to work. Apparently you can use a CodeExpressionBuilder to set server side control properties using code blocks, but I felt that would be over-engineering the solution.

Anyway, I hope this is of use to someone. I'll certainly be using this method in future projects.