4

I have tried two popular answers from Detecting an "invalid date" Date instance in JavaScript for checking valid dates. I tested both of them in IE8 – Unfortunately both are disappointing. See it here http://jsfiddle.net/Lijo/uzSU6/2/

Is there a better JavaScript code that will work in IE8 + Chrome + Firefox?

Note: To my surprise, it doesn't work well in Firefox too...

CONDITION

The date format is expected to be US date format with slashes (/)

enter image description here

CODE

isValidDateCheck2('12/33/2012') ;
isValidDateCheck1('12/12/2012') ;

function isValidDateCheck1(d) 
{
  alert(Object.prototype.toString.call(d));
  if ( Object.prototype.toString.call(d) !== "[object Date]" )
  {
    alert('Not Valid');
  }
  if(!isNaN(d.getTime()))
  {
  alert(d.getTime());
  }
  }

  function  isValidDateCheck2(d)
  {    
    var timestamp=Date.parse(d);
    alert(timestamp);

    if (isNaN(timestamp)==false)
    {
        var date=new Date(timestamp);
        alert(date);                    
    }
   }

EDIT

@mplungjan approach (first suggested) is listed in http://jsfiddle.net/Lijo/uzSU6/7/. This was failed in IE8 for one scenario - http://jsfiddle.net/Lijo/uzSU6/12/.

Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418

3 Answers3

8

You seem to be conflating two things here. Valid date objects and valid dates. These are not the same problem.

The question you linked to answers how to test for validity of date objects (whether a date object is an "invalid date" instance). Invalid date objects are generated when you use invalid parameters when constructing them: new Date('?')

What you want is to test if a date string conforms to a predefined date format. This is an entirely different problem that should not be solved by using only date objects.

Generally speaking, there are a couple of reasons for this; the first is that the browsers will helpfully compute overflow months/days/time to the correct date: new Date(2012,0,290) === Oct 06 2012.

Secondly because the parser may be locale dependent (mm/dd vs. dd/mm?). When the date is parsed by the browser my locale may cause it roll it to my timezone/DST thus skewing it and messing up detection (.getDate may now return next day over). Even worse, this may only occur across some timezones at certain parts of the year.

I strongly encourage using a library like date.js to handle this stuff because dates are much harder than you think! If you absolutely must validate by hand, then I recommend doing it in detail like this:

function isValidDate (str) {
  // parse to numbers
  const rm = str.split('/');
  const m = 1 * rm[0];
  const d = 1 * rm[1];
  const y = 1 * rm[2];
  if (isNaN(m * d * y)) {
    return false;
  }
  // day can't be 0
  if (d < 1) {
    return false;
  }
  // month must be 1-12
  if (m < 1 || m > 12) {
    return false;
  }
  // february
  if (m === 2) {
    const isLeapYear = ((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0);
    // leap year
    if (isLeapYear && d > 29) {
      return false;
    }
    // non-leap year
    if (!isLeapYear && d > 28) {
      return false;
    }
  }
  // test any other month
  else if (
    ((m === 4 || m === 6 || m === 9 || m === 11) && d > 30) ||
    ((m === 1 || m === 3 || m === 5 || m === 7 || m === 8 || m === 10 || m === 12) && d > 31)) {
    return false;
  }
  return true;
}

As a jsFiddle: http://jsfiddle.net/3pMPp/1/
As a jsPerf: http://jsperf.com/silly-date-valiation

Borgar
  • 37,817
  • 5
  • 41
  • 42
  • Thanks. Can you please provide examples in which my solution will fail? http://jsfiddle.net/Lijo/uzSU6/20/ – LCJ Dec 17 '12 at 06:28
  • 1
    There is no need to test all that if the numbers entered gives a valid date object when created AND what is entered matches the parts of the date. Please explain why your complicated code is necessary when you can get JavaScript to do the work for you by just creating a date object... – mplungjan Dec 17 '12 at 10:15
  • 1
    The code in my answer is not subject to Date implementation differences (if any exist), it supports a full range of years, and is demonstratively much faster than the currently accepted answer. I still maintain that you shouldn't use it: Use a library! – Borgar Dec 18 '12 at 22:53
  • +1 thanks for this one. Sometimes by team decision or following orders from above, we can't add libraries by own taste.. – Andre Figueiredo Jan 31 '14 at 12:00
3

This will handle actual dates and give you the chance to find what part of the date was invalid - using the DATE OBJECT

NOTE: several browsers will happily parse what seems to be an invalid date and make a date object out of it. For example 02/29/2013 will parse as 1st of March 2013, hence my test to see if the parts entered made sense when used in an actual date.

DEMO

Tested in

Win7:

  • Chrome 23 (only one to give isNaN on the first date)
  • IE 9

Win XP:

  • FX 17
  • IE 8
  • Safari 5
  • Opera 11 and 12
function isValidDateCheck(dString) {

    // test it is nn/nn/nnnn or nn/nn/nn
    var dRe = /^(\d{1,2})([\-\/])(\d{1,2})\2(\d{4}|\d{2})$/

    if (!dRe.exec(dString)) {
      return false; 
    }   

    // make sure it parses as date 
    // replace this part if you do not allow dashes        
    dString.replace(/-/g,"/"); 

    var date = new Date(dString); // create a date object
    if (!isNaN(date)) { // it may give NaN - if not test the parts
        var parts = dString.split("/"); // split on slash
        var dd = parseInt(parts[1],10); // day number
        var mm = parseInt(parts[0],10)-1; // month - JS months start at 0
        var yyyy = parseInt(parts[2],10); // year
        // return true if all parts match
        return dd===date.getDate() && mm === date.getMonth() && yyyy===date.getFullYear();
    }
    // here the date was not parsed as a date
    return false;
}


window.onload=function() {
  document.getElementById("output").innerHTML+="<br/>12/33/2012: "+isValidDateCheck('12/33/2012');
  document.getElementById("output").innerHTML+="<br/>12/12/2012: "+isValidDateCheck('12/12/2012') ;
  document.getElementById("output").innerHTML+="<br/>02/29/2012: "+isValidDateCheck('02/29/2012') ;
  document.getElementById("output").innerHTML+="<br/>02/29/2013: "+isValidDateCheck('02/29/2013') ;
  document.getElementById("output").innerHTML+="<br/>01/01/2013A: "+isValidDateCheck('01/01/2013A') ;
}
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • Thank you... Do you have any known limitations to this approach? Have you been using it quite long? – LCJ Dec 14 '12 at 12:58
  • 1
    No limitations if the format is always US with slashes. I have used it or similar versions since 1996 ;) – mplungjan Dec 14 '12 at 13:05
1

Thanks to @mplungjan. I have upvoted that answer.

@mplungjan approach (first suggested) is listed in http://jsfiddle.net/Lijo/uzSU6/7/. This was failed in IE8 for one scenario - http://jsfiddle.net/Lijo/uzSU6/12/.

So I have used a slightly different approach after referring How to validate a date?. See it here http://jsfiddle.net/Lijo/uzSU6/20/

EDIT

Please refer http://jsfiddle.net/uzSU6/37/ for scenarios that handle blank spaces

Feel free to give your suggestions/ challenges with this approach.

References

  1. Check whether white spaces exist without using trim
  2. Which equals operator (== vs ===) should be used in JavaScript comparisons?
  3. How to validate a date?

CODE

function isValidDate(s) 
{
var bits = s.split('/');

if(s.indexOf(' ') != -1)
{
    //White space exists in the original date string
    return false;
}

//Javascript month starts at zero
var d = new Date(bits[2], bits[0] - 1, bits[1]);


if ( isNaN( Number(bits[2]) ) ) 
{
    //Year is not valid number
    return false;
}

if ( Number(bits[2]) < 1 ) 
{
    //Year should be greater than zero
    return false;
}



//1. Check whether the year is a Number
//2. Check whether the date parts are eqaul to original date components
//3. Check whether d is valid

return d && ( (d.getMonth() + 1) == bits[0]) && (d.getDate() == Number(bits[1]) );

} 

Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • fails on isValidDate('2/29/2012') – mplungjan Dec 14 '12 at 14:48
  • 1
    It seems to work, but I do not like it uses the side effects of not testing with === so for example so it passed `isValidDate('2/ 29 /2012')` which mine handles – mplungjan Dec 14 '12 at 15:26
  • @mplungjan Thanks. I have updated it to handle white spaces also. Do you see any other possibility of failing it? – LCJ Dec 17 '12 at 10:12