48

I am attempting to validate a date in this format: (yyyy-mm-dd). I found this solution but it is in the wrong format for what I need, as in: (mm/dd/yyyy).

Here is the link to that solution: http://jsfiddle.net/ravi1989/EywSP/848/

My code is below:

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

    var rxDatePattern = /^(\d{1,2})(\/|-)(\d{1,2})(\/|-)(\d{4})$/; //Declare Regex
    var dtArray = currVal.match(rxDatePattern); // is format OK?

    if (dtArray == null) 
        return false;

    //Checks for mm/dd/yyyy format.
    dtMonth = dtArray[1];
    dtDay= dtArray[3];
    dtYear = dtArray[5];        

    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;
}

What regex pattern can I use for this that will account for invalid dates and leap years?

user2648752
  • 2,123
  • 5
  • 21
  • 29

11 Answers11

130

I expanded just slightly on the isValidDate function Thorbin posted above (using a regex). We use a regex to check the format (to prevent us from getting another format which would be valid for Date). After this loose check we then actually run it through the Date constructor and return true or false if it is valid within this format. If it is not a valid date we will get false from this function.

function isValidDate(dateString) {
  var regEx = /^\d{4}-\d{2}-\d{2}$/;
  if(!dateString.match(regEx)) return false;  // Invalid format
  var d = new Date(dateString);
  var dNum = d.getTime();
  if(!dNum && dNum !== 0) return false; // NaN value, Invalid date
  return d.toISOString().slice(0,10) === dateString;
}


/* Example Uses */
console.log(isValidDate("0000-00-00"));  // false
console.log(isValidDate("2015-01-40"));  // false
console.log(isValidDate("2016-11-25"));  // true
console.log(isValidDate("1970-01-01"));  // true = epoch
console.log(isValidDate("2016-02-29"));  // true = leap day
console.log(isValidDate("2013-02-29"));  // false = not leap day
Goblinlord
  • 3,290
  • 1
  • 20
  • 24
  • This works really well, but why is the bitwise OR needed? – Jacob Raccuia Apr 14 '17 at 15:18
  • 1
    It is used to coalesce a valid date to a truthy value. `Invalid Date` results in `0` when which is falsy (dates including `Invalid Date` are falsy). Without this you get a result like: `!!(new Date('2016-11-25')) // true`, `!!(new Date('2016-11-50')) // true`. With this you get something that distinguishes between the 2: `!!(new Date('2016-11-50')|0) // false`, `!!(new Date('2016-11-25')|0) // true`. The `|` operator coalesces the date into a number that is truthy or falsey. – Goblinlord Aug 02 '17 at 03:53
  • Modified this from using a bitwise OR to use `getTime` instead for clarity. @JacobRaccuia – Goblinlord Aug 27 '17 at 03:36
  • Does it work in all the browser as the `Date` constructor works differently in different browsers? – Akshay Feb 17 '18 at 10:30
  • 1
    Received errors from this function (mobile browsers, IE, etc...): 1) (IE) isNaN is not a function 2) (SAFARI Exception) JS ERROR: RangeError: Invalid Date in **** toISOString@[native code] – CETb Apr 02 '19 at 12:11
  • @CETb You seem like a javascript dev... so... fix the problem. For isNaN you can substitute with your own polyfill function which I expect shouldn't be difficult. For Safari... the NaN check should fail `getTime()` should return `NaN`. If you ignore it, when it calls `toISOString` will result in an error. It actually results in an error in that case in all browsers I believe. You need to ensure the value returned from `getTime` is not NaN. – Goblinlord Apr 30 '19 at 02:05
  • Beyond that... I would say just start actually debugging the problem on the platform you need to support and find the problem. – Goblinlord Apr 30 '19 at 02:11
  • If I write "31/02/2019" it'll consider as "valid" the date itself. – Istorn Nov 06 '19 at 13:45
  • Is there a way to check to make sure that day has past? Future dates for this still pass validation – Rhett Harrison Dec 04 '21 at 01:39
  • @RhettHarrison You can just do a comparison between `Date.now()` and `new Date()`. I would probably not do that here though. I might do something like: 1) check if valid 2) check if date is in the past. You can do that depending on what you need. This implementation does not check if anything is past/future as that was not part of the original request. – Goblinlord Mar 15 '22 at 13:47
29

You could also just use regular expressions to accomplish a slightly simpler job if this is enough for you (e.g. as seen in [1]).

They are build in into javascript so you can use them without any libraries.

function isValidDate(dateString) {
  var regEx = /^\d{4}-\d{2}-\d{2}$/;
  return dateString.match(regEx) != null;
}

would be a function to check if the given string is four numbers - two numbers - two numbers (almost yyyy-mm-dd). But you can do even more with more complex expressions, e.g. check [2].

isValidDate("23-03-2012") // false
isValidDate("1987-12-24") // true
isValidDate("22-03-1981") // false
isValidDate("0000-00-00") // true
Community
  • 1
  • 1
Thorben Croisé
  • 12,407
  • 8
  • 39
  • 50
10

Since jQuery is tagged, here's an easy / user-friendly way to validate a field that must be a date (you will need the jQuery validation plugin):

html

<form id="frm">
<input id="date_creation" name="date_creation" type="text" />
</form>

jQuery

$('#frm').validate({
  rules: {
    date_creation: {
      required: true,
      date: true
    }
  }
});

DEMO + Example


UPDATE: After some digging, I found no evidence of a ready-to-go parameter to set a specific date format.

However, you can plug in the regex of your choice in a custom rule :)

$.validator.addMethod(
    "myDateFormat",
    function(value, element) {
        // yyyy-mm-dd
        var re = /^\d{4}-\d{1,2}-\d{1,2}$/;

        // valid if optional and empty OR if it passes the regex test
        return (this.optional(element) && value=="") || re.test(value);
    }
);

$('#frm').validate({
  rules: {
    date_creation: {
      // not optional
      required: true,
      // valid date
      date: true
    }
  }
});

This new rule would imply an update on your markup:

<input id="date_creation" name="date_creation" type="text" class="myDateFormat" />
Frederik.L
  • 5,522
  • 2
  • 29
  • 41
  • You custom regex version will not check for valid number ranges and will also match things like "0000-00-00". You can also accomplish this with simpler, pure javascript, which - non the less for educational reasons - should be preferred IMO. – Thorben Croisé Sep 12 '13 at 08:46
  • The first rule does check for valid number ranges (tested 2000-44-23 , returned invalid). I will update my answer so that is more clear. Sure, you can do it with pure javascript, but I thought it would be interesting for the OP to be aware of an approach that uses jQuery's features. – Frederik.L Sep 12 '13 at 09:07
  • I think using jQuery is just fine, I just wanted to make you aware that the second solution may have some issues – Thorben Croisé Sep 12 '13 at 09:13
6

Here's the JavaScript rejex for YYYY-MM-DD format

/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/

Koko
  • 111
  • 1
  • 7
5

try this Here is working Demo:

$(function() {
    $('#btnSubmit').bind('click', function(){
        var txtVal =  $('#txtDate').val();
        if(isDate(txtVal))
            alert('Valid Date');
        else
            alert('Invalid Date');
    });

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

    var rxDatePattern = /^(\d{4})(\/|-)(\d{1,2})(\/|-)(\d{1,2})$/; //Declare Regex
    var dtArray = currVal.match(rxDatePattern); // is format OK?

    if (dtArray == null) 
        return false;

    //Checks for mm/dd/yyyy format.
    dtMonth = dtArray[3];
    dtDay= dtArray[5];
    dtYear = dtArray[1];        

    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;
}

});

changed regex is:

var rxDatePattern = /^(\d{4})(\/|-)(\d{1,2})(\/|-)(\d{1,2})$/; //Declare Regex
Zaheer Ahmed
  • 28,160
  • 11
  • 74
  • 110
5
 moment(dateString, 'YYYY-MM-DD', true).isValid() ||
 moment(dateString, 'YYYY-M-DD', true).isValid() ||
 moment(dateString, 'YYYY-MM-D', true).isValid();
Yoav Schniederman
  • 5,253
  • 3
  • 28
  • 32
4

I recommend to use the Using jquery validation plugin and jquery ui date picker

jQuery.validator.addMethod("customDateValidator", function(value, element) {
// dd-mm-yyyy
   var re = /^([0]?[1-9]|[1|2][0-9]|[3][0|1])[./-]([0]?[1-9]|[1][0-2])[./-]([0-9]{4}|[0-9]{2})$/ ; 
   if (! re.test(value) ) return false
   // parseDate throws exception if the value is invalid
   try{jQuery.datepicker.parseDate( 'dd-mm-yy', value);return true ;}
   catch(e){return false;} 
   },
   "Please enter a valid date format dd-mm-yyyy"
);

this.ui.form.validate({
    debug: true,
    rules : {
    title : { required : true, minlength: 4 }, 
    date : { required: true, customDateValidator: true }
    }
}) ;

Using Jquery and date picker just create a function with

// dd-mm-yyyy
var re = /^([0]?[1-9]|[1|2][0-9]|[3][0|1])[./-]([0]?[1-9]|[1][0-2])[./-]([0-9]{4}|[0-9]{2})$/ ; 
 if (! re.test(value) ) return false
// parseDate throws exception if the value is invalid
try{jQuery.datepicker.parseDate( 'dd-mm-yy', value);return true ;}
catch(e){return false;}

You might use only the regular expression for validation

// dd-mm-yyyy
var re = /^([0]?[1-9]|[1|2][0-9]|[3][0|1])[./-]([0]?[1-9]|[1][0-2])[./-]([0-9]{4}|[0-9]{2})$/ ; 
return re.test(value) 

Of course the date format should be of your region

Pascal
  • 2,377
  • 3
  • 25
  • 40
2

Just use Date constructor to compare with string input:

function isDate(str) {
  return 'string' === typeof str && (dt = new Date(str)) && !isNaN(dt) && str === dt.toISOString().substr(0, 10);
}

console.log(isDate("2018-08-09"));
console.log(isDate("2008-23-03"));
console.log(isDate("0000-00-00"));
console.log(isDate("2002-02-29"));
console.log(isDate("2004-02-29"));

Edited: Responding to one of the comments

Hi, it does not work on IE8 do you have a solution for – Mehdi Jalal

function pad(n) {
  return (10 > n ? ('0' + n) : (n));
}

function isDate(str) {
  if ('string' !== typeof str || !/\d{4}\-\d{2}\-\d{2}/.test(str)) {
    return false;
  }
  var dt = new Date(str.replace(/\-/g, '/'));
  return dt && !isNaN(dt) && 0 === str.localeCompare([dt.getFullYear(), pad(1 + dt.getMonth()), pad(dt.getDate())].join('-'));
}

console.log(isDate("2018-08-09"));
console.log(isDate("2008-23-03"));
console.log(isDate("0000-00-00"));
console.log(isDate("2002-02-29"));
console.log(isDate("2004-02-29"));
1

Rearrange the regex to:

/^(\d{4})([\/-])(\d{1,2})\2(\d{1,2})$/

I have done a little more than just rearrange the terms, I've also made it so that it won't accept "broken" dates like yyyy-mm/dd.

After that, you need to adjust your dtMonth etc. variables like so:

dtYear = dtArray[1];
dtMonth = dtArray[3];
dtDay = dtArray[4];

After that, the code should work just fine.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
0

Working Demo fiddle here Demo

Changed your validation function to this

function isDate(txtDate)
{
return txtDate.match(/^d\d?\/\d\d?\/\d\d\d\d$/);
}
-1

You can use this one it's for YYYY-MM-DD. It checks if it's a valid date and that the value is not NULL. It returns TRUE if everythings check out to be correct or FALSE if anything is invalid. It doesn't get easier then this!

function validateDate(date) {
    var matches = /^(\d{4})[-\/](\d{2})[-\/](\d{2})$/.exec(date);
    if (matches == null) return false;
    var d = matches[3];
    var m = matches[2] - 1;
    var y = matches[1] ;
    var composedDate = new Date(y, m, d);
    return composedDate.getDate() == d &&
            composedDate.getMonth() == m &&
            composedDate.getFullYear() == y;
}

Be aware that months need to be subtracted like this: var m = matches[2] - 1; else the new Date() instance won't be properly made.

Haroun Hajem
  • 5,223
  • 3
  • 26
  • 39