Elegant date validation in Javascript

Why is date validation such a difficult concept for Javascript programmers? People are always trying way too hard to accomplish simple tasks. Let's look at some of the wrong ways to do it.

This is from the top result on Google for “javascript valid date function”:
// Declaring valid date character, minimum year and maximum year
var dtCh = "/";
var minYear = 1900;
var maxYear = 2100;

function isInteger(s) {
	var i;
	for (i = 0; i < s.length; i++) {
		// Check that current character is number.
		var c = s.charAt(i);
		if (((c < "0") || (c > "9"))) return false;
	}
	// All characters are numbers.
	return true;
}

function stripCharsInBag (s, bag){
	var i;
	var returnString = "";
	// Search through string's characters one by one.
	// If character is not in bag, append to returnString.
	for (i = 0; i < s.length; i++) {
		var c = s.charAt(i);
		if (bag.indexOf(c) == -1) returnString += c;
	}
	return returnString;
}

function daysInFebruary (year){
	// February has 29 days in any year evenly divisible by four,
	// EXCEPT for centurial years which are not also divisible by 400.
	return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
}

function DaysArray(n) {
	for (var i = 1; i <= n; i++) {
		this[i] = 31
		if (i==4 || i==6 || i==9 || i==11) {this[i] = 30}
		if (i==2) {this[i] = 29}
	}
	return this;
}

function isDate(dtStr) {
	var daysInMonth = DaysArray(12);
	var pos1 = dtStr.indexOf(dtCh);
	var pos2 = dtStr.indexOf(dtCh, pos1 + 1);
	var strMonth = dtStr.substring(0, pos1);
	var strDay = dtStr.substring(pos1 + 1, pos2);
	var strYear = dtStr.substring(pos2 + 1);
	strYr = strYear;
	if (strDay.charAt(0) == "0" && strDay.length > 1) strDay = strDay.substring(1);
	if (strMonth.charAt(0)=="0" && strMonth.length > 1) strMonth = strMonth.substring(1);
	for (var i = 1; i <= 3; i++) {
		if (strYr.charAt(0)=="0" && strYr.length > 1) strYr = strYr.substring(1);
	}
	month = parseInt(strMonth);
	day = parseInt(strDay);
	year = parseInt(strYr);
	if (pos1 == -1 || pos2 == -1) {
		alert("The date format should be : mm/dd/yyyy");
		return false;
	}
	if (strMonth.length < 1 || month < 1 || month > 12) {
		alert("Please enter a valid month");
		return false;
	}
	if (strDay.length < 1 || day < 1 || day > 31 || (month == 2 && day > daysInFebruary(year)) || day > daysInMonth[month]) {
		alert("Please enter a valid day");
		return false;
	}
	if (strYear.length != 4 || year == 0 || year < minYear || year > maxYear) {
		alert("Please enter a valid 4 digit year between " + minYear + " and " + maxYear);
		return false;
	}
	if (dtStr.indexOf(dtCh, pos2 + 1) != -1 || isInteger(stripCharsInBag(dtStr, dtCh)) == false) {
		alert("Please enter a valid date");
		return false;
	}
	return true;
}

Holy cow! That's a large amount of code to include into a project, just for date validation. And worse, if you click through the links on the Google search, you'll find countless examples of huge, over-complicated validation functions, occasionally mixed with smaller functions that are simpler, but inaccurate. Why is this so hard?

Let's look at another one. This is actual code that was used by one of the companies I've worked for:
function validateDate(dateValue) {
	var RegExPattern = /^(?=\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\/|-|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|-|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:0?2(\/|-|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\/|-|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2}))($|\ (?=\d)))?(((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))|([01]\d|2[0-3])(:[0-5]\d){1,2})?$/;
	if ((dateValue.match(RegExPattern)) && dateValue.length >= 8 && dateValue.length <= 10) {
		return true;
	} else {
		return false;
	}
}


This regular expression seems to accurately validate dates, even for leap year, but it's absolutely unmanageable. If any aspect of this validation is ever found to be lacking in some way, I pity the person who will have to fix it.

Why don't people just use Javascript's built-in Date object to validate their dates? JavaScript's Date object is built to do some rather neat stuff. If you set the month or day to a value that is out of range, the Date object subtracts the difference and updates the entire date. For instance, if you set the month to April (which has 30 days), and then set the date to the 31st, the Date object will automatically change the month to May and the date to the 1st.

Thus, validating a date is rather easy. Here's what I do:
function validDate(date){
	//split the date using ANY separator
	var re = /(\d+)\D+(\d+)\D+(\d+)/;
	var a = date.match(re);
	if (a) {
		//check the year format
		if (a[3].length==2) a[3]= (a[3] > 69 ? '19' : '20') + a[3];
		if (a[3].length < 4) return false;

		//use integers and avoid octal parse errors for numbers starting with '0'
		a[1] = parseInt(a[1], 10);
		a[2] = parseInt(a[2], 10);
		a[3] = parseInt(a[3], 10);

		//use the Javascript Date object to validate the date
		var dt = new Date();
		dt.setFullYear(a[3]);
		dt.setMonth(a[1] - 1);
		dt.setDate(a[2]);
		if((a[1]==dt.getMonth() + 1) && a[2]==dt.getDate() && a[3]==dt.getFullYear())
			return (String(a[1]).length > 1 ? a[1] : '0' + a[1]) + '/' + (String(a[2]).length > 1 ? a[2] : '0' + a[2]) + '/' + a[3];
		else return null;
	}
	else return null;
}


There are a number of advantages to doing it this way. The first advantage is flexibility of formatting. This function accepts dates with ANY delimiter — you don't have to check independently for dots, dashes, slashes, etc. It also gracefully handles 1-digit or 2-digit numbers for month and day, and 2-digit or 4-digit years.

The second advantage is that the hard work is done by JavaScript's Date object, which is compiled and fast. All we're doing here is setting the year, month, and day, and then checking to make sure it stayed where we set it. If it did, all the values were in their acceptable ranges.

And the third advantage to this function is that it returns a consistently formatted date for use throughout your code, or null if the input value is invalid.

So, if you want to validate and format a date in a text field, you can just add one line to the onblur handler:
<input type="text" name="datefield" value="" onblur="if (!(this.value=validDate(this.value))) alert('Please enter a valid date.');" />


Of course, I recommend using a more elegant event handler to do the validation, but if you're lazy, it really is that simple.

So there you have it, a much better way to validate dates. Please note that this function assumes the date is in m/d/y format. Naturally, I have a way of handling different formats, as well, but I chose not to address that here since the functions I found for comparison don't bother to address it.
comments powered by Disqus