19

I am trying to write some code with will validate form data. I have a date field which should have a mm/dd/yyyy format. I needed to catch exceptions such as February 31, so I added this code:

var d = new Date(dob);
if (isNaN(d.getTime())) { //this if is to take care of February 31, BUT IT DOESN'T!
  error = 1;
  message += "<li>Invalid Date</li>";
} else {
  var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/;
  var validFormat = date_regex.test(dob);
  if (!(validFormat)) {
    error = 1;
    message += "<li>Invalid date format - date must have format mm/dd/yyyy</li>";
  }
}

However I found something very weird: while the date 02/32/2000 errors as an invalid date, 02/31/2000 does not!

Rajesh
  • 22,581
  • 5
  • 41
  • 70
user1015214
  • 2,421
  • 10
  • 33
  • 58
  • 3
    `new Date('2013-02-31 00:00:00')` actually evaluates to `Sun Mar 03 2013 00:00:00 GMT-0500 (Eastern Standard Time)` so that's why it doesn't error. Not 100% sure on the reason for this though – Jeff Shaver Jan 17 '14 at 14:48
  • This will happen for every month with less than 31 days... – Christoph Jan 17 '14 at 14:54

5 Answers5

56

Due to what I said in the comments...

Another way you could check if a date is valid is by checking whether or not the stuff you passed into the new Date function is the same as what comes out of it, like this:

// Remember that the month is 0-based so February is actually 1...
function isValidDate(year, month, day) {
    var d = new Date(year, month, day);
    if (d.getFullYear() == year && d.getMonth() == month && d.getDate() == day) {
        return true;
    }
    return false;
}

then you could do this:

if (isValidDate(2013,1,31))

and it would return true if valid and false if invalid.

Jeff Shaver
  • 3,235
  • 17
  • 19
  • 3
    Nice, just change `== date` for `== day` – Eldelshell Apr 17 '14 at 09:26
  • 1
    Really nice! Could also just be `return d.getFullYear() == year && d.getMonth() == month && d.getDate() == day` – pmrotule May 15 '18 at 05:45
  • This method is not **consumable-friendly**. The `0-indexed` nature of month on `Date` constructor is just a big design flaw (https://stackoverflow.com/q/2552483/2561091). I wasted time because didn't pay attention to the comment on your method, and didn't realize the month parameter was expected to be `0-indexed`. I've re-wrote the method as a new answer to make is more readable (and slightly modern). – Reuel Ribeiro May 08 '19 at 01:16
  • I love elegant answers like this – DavesPlanet Dec 03 '20 at 15:27
4

After wrecking my head with the obscurity of Date .getMonth() (and also weekday by .getDay()) being 0-index (despite year, day and all the others not being like so... oh god...) I've re-wrote Jeff's answer to make it more readable and more friendly-usable to whom consume the method from outside.

ES6 code

You can call passing month as 1-indexed as you'd normally expect.

I've parsed inputs using Number constructor so I can use strict equality to more confidently compare values.

I'm using the UTC version methods to avoid having to deal with the local timezone.

Also, I broke steps down into some variables for the sake of readability.

/**
 *
 * @param { number | string } day
 * @param { number | string } month
 * @param { number| string } year
 * @returns { boolean }
 */
function validateDateString(day, month, year) {

    day = Number(day);
    month = Number(month) - 1; //bloody 0-indexed month
    year = Number(year);

    let d = new Date(year, month, day);

    let yearMatches = d.getUTCFullYear() === year;
    let monthMatches = d.getUTCMonth() === month;
    let dayMatches = d.getUTCDate() === day;

    return yearMatches && monthMatches && dayMatches;
}
Reuel Ribeiro
  • 1,396
  • 13
  • 23
  • Shouldn't it be `month = Number(month) + 1` since it's 0 indexed meaning January = 0 when it should be = 1? It seems like your code would say that January is -1 and December is 10 – Ryan Russell Jul 10 '20 at 23:23
  • 1
    Hi @RyanRussell, my method does `Number(month) -1` because my interface `validateDateString` is simplifying the mental process of using 1-index months. For for January you'd use my function passing `1` as a parameter and internally I subtract it to comply with `Date`'s confusing interface. – Reuel Ribeiro Jul 12 '20 at 16:06
3

Are you able to use a library?

My first port of call for date handling in Javascript is moment.js: "A javascript date library for parsing, validating, manipulating, and formatting dates."

Stephen
  • 621
  • 5
  • 14
1

The ususal way to validate a 'mm/dd/yyyy' date string is to create a date object and verify that its month and date are the same as the input.

function isvalid_mdy(s){
    var day, A= s.match(/[1-9][\d]*/g);
    try{
        A[0]-= 1;
        day= new Date(+A[2], A[0], +A[1]);
        if(day.getMonth()== A[0] && day.getDate()== A[1]) return day;
        throw new Error('Bad Date ');
    }
    catch(er){
        return er.message;
    }
}

isvalid_mdy('02/31/2000')

/* returned value: (Error)Bad Date */

kennebec
  • 98,993
  • 30
  • 103
  • 125
0

Basically an alternative to the above-mentioned examples

function (date) {

    if (!/(0[1-9]|1[0-9]|2[0-9]|3[0-1])\/(0[1-9]|1[0-2])\/([1-2][0-9]{3})/g.test(date))
    {
        alert('Incorrect date format please follow this form: dd/mm/yyyy');
        return;
    }
    else
    {
        // secondary validation
        const parts = (date).split('/').map((p) => parseInt(p, 10));
        let day = Number(parts[0]);
        let month = Number(parts[1]) - 1; // 0-indexed month
        let year = Number(parts[2]);
        let d = new Date(year, month, day);
        if (!(d.getFullYear() == year && d.getMonth() == month && d.getDate() == day))
        {
            alert('Incorrect date, please enter the correct day entry');
            return;
        }
    }
}
Kgopo
  • 1