Jason Taylor
Jason Taylor

Reputation: 85

Using regexp to validate a date using DD-Mon-YYYY format

Hello I am trying to validate a date in the format: 03-Mar-2011 or 12-Dec-2007. I currently have a regexp that fails on the 3 character Month. I probably just need to tweak that to get it to work properly.

    //function isDate(txtDate) {
    function isDate(sender, args) {
        var currVal = document.getElementById('<%=txtChange_Date.ClientID%>').value;

            if (currVal == '')
                return false;

            //Declare Regex  
            var rxDatePattern = /^(\d{1,2})(\/|-)[a-zA-Z]{3}(\/|-)(\d{4})$/;

            var dtArray = currVal.match(rxDatePattern); // is format OK?

            if (dtArray == null)
                return false;

            //formerly //Checks for dd/mm/yyyy format.
            //Checks for dd-MMM-yyyy format.
            var dtDay = dtArray[1];
            var dtMonth = dtArray[3];
            var dtYear = dtArray[4];

            switch(dtMonth)
            {
                case 'Jan':
                    dtMonth = '01';
                    break;
                case 'Feb':
                    dtMonth = '02';
                    break;
                case 'Mar':
                    dtMonth = '03';
                    break;
                case 'Apr':
                    dtMonth = '04';
                    break;
                case 'May':
                    dtMonth = '05';
                    break;
                case 'Jun':
                    dtMonth = '06';
                    break;
                case 'Jul':
                    dtMonth = '07';
                    break;
                case 'Aug':
                    dtMonth = '08';
                    break;
                case 'Sep':
                    dtMonth = '09';
                    break;
                case 'Oct':
                    dtMonth = '10';
                    break;
                case 'Nov':
                    dtMonth = '11';
                    break;
                case 'Dec':
                    dtMonth = '12';
                    break;
            }


if (dtMonth < 1 || dtMonth > 12)
                return false;
else if (dtDay < 1 || dtDay > 31)
                return false;
else if ((dtMonth == 4 || dtMonth == 6 || dtMonth == 9 || dtMonth == 11) && dtDay == 31)
                return false;
else if (dtMonth == 2) {
            var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));
                if (dtDay > 29 || (dtDay == 29 && !isleap))
                    return false;
            }

            return true;
        }

Upvotes: 2

Views: 11508

Answers (5)

Daniel Gimenez
Daniel Gimenez

Reputation: 20494

There were a few problems with your code, but your regular expression was fine except that month wasn't being grouped.

Other Changes

  • The switch that converts the month needed to have dtMonth converted to lower case and the case statements needed to match the case.
  • I explicitly converted the date parts to integers with parseInt. I don't know if it was an issue with dtDay or dtYear, but it was causing an issue with dtMonth.
  • With the parseInt change I added a not a number check and dtMonth.
  • I also moved the validator out of the event handler. Just a good practice to follow if the validation isn't trivial.

Code

function isDate(currVal) {
    if (currVal == '') return false;

    //Declare Regex  
    var rxDatePattern = /^(\d{1,2})(\/|-)([a-zA-Z]{3})(\/|-)(\d{4})$/;

    var dtArray = currVal.match(rxDatePattern); // is format OK?

    if (dtArray == null) return false;

    var dtDay = parseInt(dtArray[1]);
    var dtMonth = dtArray[3];
    var dtYear = parseInt(dtArray[4]);

    // need to change to lowerCase because switch is
    // case sensitive
    switch (dtMonth.toLowerCase()) {
        case 'jan':
            dtMonth = '01';
            break;
        case 'feb':
            dtMonth = '02';
            break;
        case 'mar':
            dtMonth = '03';
            break;
        case 'apr':
            dtMonth = '04';
            break;
        case 'may':
            dtMonth = '05';
            break;
        case 'jun':
            dtMonth = '06';
            break;
        case 'jul':
            dtMonth = '07';
            break;
        case 'aug':
            dtMonth = '08';
            break;
        case 'sep':
            dtMonth = '09';
            break;
        case 'oct':
            dtMonth = '10';
            break;
        case 'nov':
            dtMonth = '11';
            break;
        case 'dec':
            dtMonth = '12';
            break;
    }

    // convert date to number
    dtMonth = parseInt(dtMonth);

    if (isNaN(dtMonth)) return false;
    else if (dtMonth < 1 || dtMonth > 12) return false;
    else if (dtDay < 1 || dtDay > 31) return false;
    else if ((dtMonth == 4 || dtMonth == 6 || dtMonth == 9 || dtMonth == 11) && dtDay == 31) return false;
    else if (dtMonth == 2) {
        var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));
        if (dtDay > 29 || (dtDay == 29 && !isleap)) return false;
    }

    return true;
}

jsFiddle

Alternate Solution

You might not like a really complicated regex, but you can really reduce your code by handling the switching in the regex itself. The following gets the matched month by looping through the matched group into an undefined one is not encountered and then offsetting the value.

function isDate(currVal) {

    if (currVal == '') return false;

    //Declare Regex  
    var rxDatePattern = /^(\d{1,2})(\/|-)(?:(\d{1,2})|(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|(aug)|(sep)|(oct)|(nov)|(dec))(\/|-)(\d{4})$/i;

    var dtArray = currVal.match(rxDatePattern);

    if (dtArray == null) return false;

    var dtDay = parseInt(dtArray[1]);
    var dtMonth = parseInt(dtArray[3]);
    var dtYear = parseInt(dtArray[17]);

    if (isNaN(dtMonth)) {
        for (var i = 4; i <= 15; i++) {
            if ((dtArray[i])) {
                dtMonth = i - 3;
                break;
            }
        }
    }

    if (dtMonth < 1 || dtMonth > 12) return false;
    else if (dtDay < 1 || dtDay > 31) return false;
    else if ((dtMonth == 4 || dtMonth == 6 || dtMonth == 9 || dtMonth == 11) && dtDay == 31) return false;
    else if (dtMonth == 2) {
        var isleap = (dtYear % 4 == 0 && (dtYear % 100 != 0 || dtYear % 400 == 0));
        if (dtDay > 29 || (dtDay == 29 && !isleap)) return false;
    }

    return true;
}

jsFiddle

Upvotes: 3

Logan Murphy
Logan Murphy

Reputation: 6230

Here is the code, no regexp required and use of javascript build in date parsing.

var isDate = (function() {
    var MONTHS = [
        "Jan", 
        "Feb", 
        "Mar", 
        "Apr", 
        "May", 
        "Jun", 
        "Jul",
        "Aug",
        "Sep",
        "Oct",
        "Nov",
        "Dec"
    ];

    return function (date) {
        var v = new Date(date);
        var day = v.getDate();
        if(day < 10) {
            day = "0" + day;
        }
        var month = MONTHS[v.getMonth()];
        var year = v.getFullYear();
        var total = day + "-" + month + "-" + year;
        return date.toUpperCase() == total.toUpperCase();
    }
}());

THE FIDDLE

Upvotes: 0

collapsar
collapsar

Reputation: 17238

your regex is ok, you haven't enclosed the month subpattern with parentheses so you haven't defined a capture group. consequently, your switch statement fails:

try

var rxDatePattern = /^(\d{1,2})(\/|-)([a-zA-Z]{3})(\/|-)(\d{4})$/;

and

var dtYear = dtArray[5];  // now 5 instead of 4

Upvotes: 2

jozxyqk
jozxyqk

Reputation: 17266

It looks like there's just brackets missing around the month section to make it a separate group.

        /^(\d{1,2})(\/|-)([a-zA-Z]{3})(\/|-)(\d{4})$/

        var dtDay = dtArray[1];
        var dtMonth = dtArray[3];
        var dtYear = dtArray[5];

Upvotes: 0

James Montagne
James Montagne

Reputation: 78650

You need parentheses around the month to capture it:

/^(\d{1,2})(\/|-)([a-zA-Z]{3})(\/|-)(\d{4})$/

Which will mean you then need to adjust the index of your year:

var dtYear = dtArray[5];  // now 5 instead of 4

Upvotes: 0

Related Questions