22

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
  • 24,354
  • 5
  • 48
  • 79
user1015214
  • 2,733
  • 10
  • 36
  • 66
  • 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

7 Answers7

59

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,315
  • 18
  • 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
  • No works for dates before 1970. Ex: 1969-12-31. – Luis Armando Feb 13 '23 at 22:16
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,419
  • 14
  • 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
2

Assuming month input is 1-12 (1-based, not 0-based):

function isValidDate(year, month, day) {
    var d = new Date(year, month - 1, day);
    return month == d.getMonth() + 1;
}
isValidDate(2019, 12, 0); //=> false
isValidDate(2020, 2, 29); //=> true
isValidDate(2021, 2, 29); //=> false
isValidDate(2022, 2, 31); //=> false
stemar
  • 21
  • 2
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
  • 102,654
  • 32
  • 106
  • 127
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
0

I may be a little late for posting an answer but here is what worked best for me

var user_date = (`${user_values.month_value} ${user_values.date_value} , ${user_values.year_value}`)
        const d = new Date(user_date);
        let day = d.getDate()
        
        if(user_values.date_value != day){
            setdate_validation({
                display:'flex'
            })

          }
          else{
            setdate_validation({
                display:'none'
            })            
            console.log(user_values)

so in the above code what happens is i get different inputs from my user like one dropdown for date another for month and so on , i collect them and store it with .getdate() now .getdate() function returns the value of day , so if i stored (02 21 , 2002) then the .getdate() will return 21 ,

but there is a catch if i enter an invalid date like (02 30, 2002) where 30 is invalid in month of february then the .getdate() function returns not the same date but the date in next month or increment as much you are far away from a valid date like if 28 is valid and I entered 29 then .getdate() will show 1 as the output so i just compare the result of .getdate() with my current date value which is entered and if it is not same then the date is invalid.

(this code is from react using usestates)

Samyak jain
  • 137
  • 1
  • 7