1
votes

I'm trying to validate if a string is a valid date. Date.parse(dateString) or new Date(dateString) both are very lenient. For example a date like 14/14/2000 is coming out as 2/14/2001. I tried a regular expression but now I'm needing to validate more than mm/dd/yyyy, I also need to validate yyyy-mm-ddThh:mm:ss e.g. 1951-02-05T00:00:00.

I was using regular expressions and some basic date checks right now, but the second date format above is failing the regex.

function isDate(dateToCheck) {
    if (dateToCheck == "" || dateToCheck == null) { return true; }
    var timestamp = Date.parse(dateToCheck);
    var d = new Date(timestamp);
    var re = /(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/;

    var valid = re.test(dateToCheck);
    valid = valid && !isNaN(timestamp);
    valid = valid && d.getFullYear() > 1900 && d.getFullYear() < 2100;
    return valid;
}

The regex is specifically for mm/dd/yyyy where month and day are both double digits (single digit months get a leading zero) and won't allow invalid months or days like 14 as a month or 32 as a day. The year can be anything from 1900 to 2099.

It's not air tight. It would allow 02/31/2000 which is invalid because February never has 31 days.

How can I validate both of these date formats without allowing lenient dates?

3
Date.parse('14/14/2000') returns NaN - Fiddle. Date.parse returns an integer conversion of the date you provide it. If it is NaN, the date is invalid. new Data('14/14/2000') returns Invalid Date.Bic
@Bic Date.parse('14/14/2000') in IE10 returns 982134000000. I cannot trust it for an internal website that will only run on IE10.Corey Ogburn
Fair enough. Leave it to IE to do things differently and make life hard.Bic
@Bic At least I don't have to worry about cross browser fixes...Corey Ogburn
I added a full example of my response. If it works for you please select it as the answer.QueueHammer

3 Answers

3
votes

Look at moment.js, and all their parsing options.

http://momentjs.com/docs/#/parsing/string/

Their is an isValid() function that you can use to validate if the input string was correct.

moment("not a real date").isValid(); // false

If you combing that with a date validation string then you get the result you are looking for: http://momentjs.com/docs/#/parsing/string-formats/

var m = moment('14/14/2000', 'DD/MM/YYYY');
console.log(m.toString(),m.isValid());
//"Not a valid date"
//false

Update: I made a test harness on JS-Bin with your full sample data. Here is the full working example code using Moment.js. You can specify multiple formats for to try before it will return isValid().

function momentIsDate (dateToCheck) {
  return moment(dateToCheck, [
    'DD/MM/YYYY',
    'YYYY-MM-DDTHH:mm:ss'
  ]).isValid();
}
1
votes

They both can be validated with this.

 #  /^(?:(?:(?:19|20)\d\d[- \/.](?:0[1-9]|1[012])[- \/.](?:0[1-9]|[12][0-9]|3[01])T\d{2}:\d{2}:\d{2})|(?:(?:0[1-9]|1[012])[- \/.](?:0[1-9]|[12][0-9]|3[01])[- \/.](?:19|20)\d{2}))$/

 ^ 
 (?:
      (?:
           (?: 19 | 20 )
           \d\d [- /.] 
           (?: 0 [1-9] | 1 [012] )
           [- /.] 
           (?: 0 [1-9] | [12] [0-9] | 3 [01] )
           T \d{2} : \d{2} : \d{2} 
      )
   |  (?:
           (?: 0 [1-9] | 1 [012] )
           [- /.] 
           (?: 0 [1-9] | [12] [0-9] | 3 [01] )
           [- /.] 
           (?: 19 | 20 )
           \d{2} 
      )
 )
 $
1
votes

You can actually use the "lenient date" functionality against itself in a pretty cool way to handle this issue. Starting out by using your mm/dd/yyyy example, first make sure that the value matches that format, then, if it does, use the value to create a new Date object:

var checkDateVal = new Date(dateToCheck);

Then, using that new value, rebuild the date string:

var checkDateMonth = checkDateVal.getDate();
var checkDateDay = checkDateVal.getMonth() + 1;
var checkDateYear = checkDateVal.getFullYear();

// force two digits for the day and month, if necessary
checkDateMonth = (checkDateMonth < 10) ? "0" + checkDateMonth : checkDateMonth;
checkDateDay = (checkDateDay < 10) ? "0" + checkDateDay : checkDateDay;

var checkDateString = checkDateMonth + "/" + checkDateDay + "/" + checkDateYear;

Now compare the original date input to the new string value that you created:

if (dateToCheck !== checkDateString) {
    // Yell at user for bad data
}

If the date string that was input by the user does not match the date string that was created by the Date object, that means that the object was forced to "massage" the input value to create a real date . . . which means that original input value was an invalid date value to begin with.

And there is your check. :)

The same logic can be applied to the longer date format that you mention . . . I'd recommend using two regex values at the beginning to determine which format the original input is in, and then use that as a flag to drive logic downstream (e.g., how the checkDateString needs to be built to see if the input was valid).