Juan Ignacio
Juan Ignacio

Reputation: 3267

Javascript date regex DD/MM/YYYY

I know there are a lot of regex threads out there by I need a specific pattern I couldn't fin anywhere

This regex validates in a YYYY-MM-DD format

/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/

I need the pattern to be DD/MM/YYYY (day first since it's in spanish and only "/", "-" should not be allowed)

I searched several regex libraries and I think this one should work... but since I'm not familiar with regex I'm not sure it validates like that

(0[1-9]|[12][0-9]|3[01])[ \.-](0[1-9]|1[012])[ \.-](19|20|)\d\d

I also don't know ho to escape the slashes, I try to "see" the logic in the string but it's like trying "see" the Matrix code for me. I'm placing the regex string in a options .js

[...]  },
"date": {
                    "regex": (0[1-9]|[12][0-9]|3[01])[ \.-](0[1-9]|1[012])[ \.-](19|20|)\d\d,
                    "alertText": "Alert text AAAA-MM-DD"
                },
"other type..."[...]

So, if the regex is ok, how would I escape it? if it's not, what's the correct regex and how do I escape it? :P

Thanks a lot

Upvotes: 92

Views: 371303

Answers (14)

jerichaux
jerichaux

Reputation: 66

Here you go, an unfailing regex that works on all valid YYYY/MM/DD (bonus for regex for YYYY-MM-DD inputs) (including only VALID leap days):

This lovely piece matches ONLY valid dates (and accounts for leap years - and discounts those leap years which occur on centennial years which are not wholly divisible by 400 (thus rendering them ineligible for a February 29th).

I have included non-delimited, as well as slash and dash delimited variants for the YYYYMMDD, MMDDYYYY, and DDMMYYYY variations that are commonly seen.

These have been thoroughly tested against all numeric combinations from "0000-00-00" through "9999-99-99" without issue. See the section "testing" below.


Non-Delimited Variants:

YYYYMMDD:

/^(?:(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26]))))0229)|(?:\d{4}(?:(?:(?:0[13578]|1[02])(?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)(?:0[1-9]|[12]\d|30))|(?:02(?:0[1-9]|1[0-9]|2[0-8]))))$/

MMDDYYYY:

/^(?:0229(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:(?:0[13578]|1[02])(?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)(?:0[1-9]|[12]\d|30))|(?:02(?:0[1-9]|1[0-9]|2[0-8])))\d{4})$/

DDMMYYYY:

/^(?:2902(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:0[1-9]|[12]\d|3[01])(?:(?:0[13578]|1[02]))|(?:(?:0[1-9]|[12]\d|30)(?:0[469]|11))|(?:(?:0[1-9]|1[0-9]|2[0-8])02))\d{4})$/

Forward Slash Delimited Variants:

YYYY/MM/DD:

/^(?:(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26]))))[/]02[/]29)|(?:\d{4}[/](?:(?:(?:0[13578]|1[02])[/](?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)[/](?:0[1-9]|[12]\d|30))|(?:02[/](?:0[1-9]|1[0-9]|2[0-8]))))$/

MM/DD/YYYY:

/^(?:02[/]29[/](?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:(?:0[13578]|1[02])[/](?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)[/](?:0[1-9]|[12]\d|30))|(?:02[/](?:0[1-9]|1[0-9]|2[0-8])))[/]\d{4})$/

DD/MM/YYYY:

/^(?:29[/]02[/](?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:0[1-9]|[12]\d|3[01])[/](?:(?:0[13578]|1[02]))|(?:(?:0[1-9]|[12]\d|30)[/](?:0[469]|11))|(?:(?:0[1-9]|1[0-9]|2[0-8])[/]02))[/]\d{4})$/

Dash Delimited Variants:

YYYY-MM-DD:

/^(?:(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26]))))[-]02[-]29)|(?:\d{4}[-](?:(?:(?:0[13578]|1[02])[-](?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)[-](?:0[1-9]|[12]\d|30))|(?:02[-](?:0[1-9]|1[0-9]|2[0-8]))))$/

MM-DD-YYYY:

/^(?:02[-]29[-](?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:(?:0[13578]|1[02])[-](?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)[-](?:0[1-9]|[12]\d|30))|(?:02[-](?:0[1-9]|1[0-9]|2[0-8])))[-]\d{4})$/

DD-MM-YYYY

/^(?:29[-]02[-](?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))))|(?:(?:(?:0[1-9]|[12]\d|3[01])[-](?:(?:0[13578]|1[02]))|(?:(?:0[1-9]|[12]\d|30)[-](?:0[469]|11))|(?:(?:0[1-9]|1[0-9]|2[0-8])[-]02))[-]\d{4})$/

Testing

It has also been thoroughly tested against all possible combinations of YYYY-MM-DD, from 0000-00-00 through 9999-99-99.

Crucially, it also aligns with JavaScript's produced Date objects when passed those dates. Thus, no Date object can exist between 0000-01-01 through 9999-12-31 which is not validated correctly by this regular expression.

This has been produced using non-capturing groups, as attempting to capture would (effectively) be pointless, due to the indexed nature of capturing groups. Don't bother, just separate the string by indexes (minding to skip the hyphens) if you need the "YYYY"/"MM"/"DD" strings for whatever reason. And, if we're not going to use them, we might as well save the compute on it.

Enjoy. Explanation below.

Note, the explanation is focused on the YYYY-MM-DD regex, but both work precisely the same way. The only difference is the delimiter.


Leap Day Matching:

The Regex:

    /(?:(?:(?:(?:(?:[02468][048])|(?:[13579][26]))00)|(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26]))))-02-29)/

The Trick

Rationale: if a number's last two digits are evenly divisible by 4, the whole number is evenly divisible by 4.
If we look at all numbers (left-padded zero) between 00-99, we can separate them into two distinct groups

00 04 08 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96
  1. Two digit pairs with an even leading digit, which will always be trailed by either 0, 4, or 8: /[02468][048]/
  2. Two digit pairs with an odd leading digit, which will always be trailed by either 2 or 6: /[13579][26]/

Centennial Years

We can use that to isolate those valid centennial years, whereby the year is wholly divisible by both 100 and 400. The last two digits are always double-zero, so we only care about the first two digits, which means they must fit the above trend of an even digit followed by [048] or an odd digit followed by [26]:

/(?:(?:(?:[02468][048])|(?:[13579][26]))00)/

Non-Centennial Years

Now we want to take the same logic and allow it to capture any combination of [0-9] for the first two digits of the year, and any combination of the above "trick" for the last two digits of the year: (/[02468][048]|[13579][26]/).

The only difference is that we DON'T capture years ending in double-zero for this part of the regex, which gives us the expression:

/(?:[0-9][0-9](?:(?:0[48])|(?:[2468][048])|(?:[13579][26])))/

All together: February 29th

Then, to put it all together, as we know leap days can ONLY be on February 29th, we just tack on /-02-29/ at the end of the group, giving us the full expression for only VALID leap days.


Matching Valid Non-Leap Days

/(?:\d{4}-(?:(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01]))|(?:(?:0[469]|11)-(?:0[1-9]|[12]\d|30))|(?:(02)-(?:0[1-9]|1[0-9]|2[0-8]))))/

This is the easy part, now that leap-days are out of the equation.

The Year:

We literally just want any combination of 4 digits followed by a dash:

/\d{4}-/

Then we account for the different combinations of months with their maximum number of days.

31 Day Months

The months with 31 days are:

01 03 05 07 08 10 12

So, we make sure the month is one of those, followed by a hyphen:

/(?:0[13578]|1[02])-/

And that the days are anywhere between 01-31:

/(?:0[1-9]|[12]\d|3[01])/

Giving us:

/(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01]))/

30 Day Months:

Next up, we have the 30 day months:

04 06 09 11

So, basically the same tact, just with different targets. First the month followed by a dash:

/(?:0[469]|11)-/

Then the days, which must be between 01-30:

/(?:0[1-9]|[12]\d|30)/

Giving us:

/(?:(?:0[469]|11)-(?:0[1-9]|[12]\d|30))/

28 Day Months:

Finally, we have our last part, the only 28 day month there is. This one is much simpler, so I'm not going to separate it like I did the others:

/(?:02)-(?:0[1-9]|1[0-9]|2[0-8]))/

Finally, if we put it all together, we get the abomination at the top.

Upvotes: 0

H S W
H S W

Reputation: 7129

It can be done like this for dd/mm/yyyy:

^(3[01]|[12][0-9]|0[1-9])/(1[0-2]|0[1-9])/[0-9]{4}$

For mm/dd/yy, mm/dd/yyyy, dd/mm/yy, and dd/mm/yyyy:

Allowing leading zeros to be omitted:

^[0-3]?[0-9]/[0-3]?[0-9]/(?:[0-9]{2})?[0-9]{2}$

Requiring leading zeros:

^[0-3][0-9]/[0-3][0-9]/(?:[0-9][0-9])?[0-9][0-9]$

For more details: https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s04.html

Upvotes: -1

Visakh V A
Visakh V A

Reputation: 320

This validates date like dd-mm-yyyy

([0-2][0-9]|(3)[0-1])(\-)(((0)[0-9])|((1)[0-2]))(\-)([0-9][0-9][0-9][0-9])

This can use with javascript like angular reactive forms

Upvotes: 0

OammieR
OammieR

Reputation: 2850

Take a look from here https://www.regextester.com/?fam=114662

Use this following Regular Expression Details, This will support leap year also.

var reg = /^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([1][26]|[2468][048]|[3579][26])00))))$/g;

Example

Upvotes: 45

Pau
Pau

Reputation: 16106

Scape slashes is simply use \ before / and it will be escaped. (\/=> /).

Otherwise you're regex DD/MM/YYYY could be next:

/^[0-9]{2}[\/]{1}[0-9]{2}[\/]{1}[0-9]{4}$/g

Explanation:

  • [0-9]: Just Numbers
  • {2} or {4}: Length 2 or 4. You could do {2,4} as well to length between two numbers (2 and 4 in this case)
  • [\/]: Character /
  • g : Global -- Or m: Multiline (Optional, see your requirements)
  • $: Anchor to end of string. (Optional, see your requirements)
  • ^: Start of string. (Optional, see your requirements)

An example of use:

var regex = /^[0-9]{2}[\/][0-9]{2}[\/][0-9]{4}$/g;

var dates = ["2009-10-09", "2009.10.09", "2009/10/09", "200910-09", "1990/10/09", 
    "2016/0/09", "2017/10/09", "2016/09/09", "20/09/2016", "21/09/2016", "22/09/2016",
    "23/09/2016", "19/09/2016", "18/09/2016", "25/09/2016", "21/09/2018"];

//Iterate array
dates.forEach(
    function(date){
        console.log(date + " matches with regex?");
      console.log(regex.test(date));
    });

Of course you can use as boolean:

if(regex.test(date)){
     //do something
}

Upvotes: 20

YannickIngenierie
YannickIngenierie

Reputation: 622

I build this regular to check month 30/31 and let february to 29.

new RegExp(/^((0[1-9]|[12][0-9]|3[01])(\/)(0[13578]|1[02]))|((0[1-9]|[12][0-9])(\/)(02))|((0[1-9]|[12][0-9]|3[0])(\/)(0[469]|11))(\/)\d{4}$/)

I think, it's more simple and more flexible and enough full.

Perhaps first part can be contract but I Don't find properly.

Upvotes: 0

bkaancelen
bkaancelen

Reputation: 163

Try using this..

[0-9]{2}[/][0-9]{2}[/][0-9]{4}$

this should work with this pattern DD/DD/DDDD where D is any digit (0-9)

Upvotes: 9

user1693371
user1693371

Reputation: 124

((?=\d{4})\d{4}|(?=[a-zA-Z]{3})[a-zA-Z]{3}|\d{2})((?=\/)\/|\-)((?=[0-9]{2})[0-9]{2}|(?=[0-9]{1,2})[0-9]{1,2}|[a-zA-Z]{3})((?=\/)\/|\-)((?=[0-9]{4})[0-9]{4}|(?=[0-9]{2})[0-9]{2}|[a-zA-Z]{3})

Regex Compile on it

2012/22/Jan
2012/22/12 
2012/22/12
2012/22/12
2012/22/12
2012/22/12
2012/22/12
2012-Dec-22
2012-12-22
23/12/2012
23/12/2012
Dec-22-2012
12-2-2012
23-12-2012
23-12-2012

Upvotes: 3

Anjana Silva
Anjana Silva

Reputation: 9201

For people who needs to validate years earlier than year 1900, following should do the trick. Actually this is same as the above answer given by [@OammieR][1] BUT with years including 1800 - 1899.

/^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((18|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((18|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/

Hope this helps someone who needs to validate years earlier than 1900, such as 01/01/1855, etc.

Thanks @OammieR for the initial idea.

Upvotes: 1

Jayesh Bhalodia
Jayesh Bhalodia

Reputation: 1

Do the following change to the jquery.validationengine-en.js file and update the dd/mm/yyyy inline validation by including leap year:

"date": {
    // Check if date is valid by leap year
    "func": function (field) {
    //var pattern = new RegExp(/^(\d{4})[\/\-\.](0?[1-9]|1[012])[\/\-\.](0?[1-9]|[12][0-9]|3[01])$/);
    var pattern = new RegExp(/^(0?[1-9]|[12][0-9]|3[01])[\/\-\.](0?[1-9]|1[012])[\/\-\.](\d{4})$/);
    var match = pattern.exec(field.val());
    if (match == null)
    return false;

    //var year = match[1];
    //var month = match[2]*1;
    //var day = match[3]*1;
    var year = match[3];
    var month = match[2]*1;
    var day = match[1]*1;
    var date = new Date(year, month - 1, day); // because months starts from 0.

    return (date.getFullYear() == year && date.getMonth() == (month - 1) && date.getDate() == day);
},
"alertText": "* Invalid date, must be in DD-MM-YYYY format"

Upvotes: 0

edid
edid

Reputation: 359

I use this function for dd/mm/yyyy format :

// (new Date()).fromString("3/9/2013") : 3 of september
// (new Date()).fromString("3/9/2013", false) : 9 of march
Date.prototype.fromString = function(str, ddmmyyyy) {
    var m = str.match(/(\d+)(-|\/)(\d+)(?:-|\/)(?:(\d+)\s+(\d+):(\d+)(?::(\d+))?(?:\.(\d+))?)?/);
    if(m[2] == "/"){
        if(ddmmyyyy === false)
            return new Date(+m[4], +m[1] - 1, +m[3], m[5] ? +m[5] : 0, m[6] ? +m[6] : 0, m[7] ? +m[7] : 0, m[8] ? +m[8] * 100 : 0);
        return new Date(+m[4], +m[3] - 1, +m[1], m[5] ? +m[5] : 0, m[6] ? +m[6] : 0, m[7] ? +m[7] : 0, m[8] ? +m[8] * 100 : 0);
    }
    return new Date(+m[1], +m[3] - 1, +m[4], m[5] ? +m[5] : 0, m[6] ? +m[6] : 0, m[7] ? +m[7] : 0, m[8] ? +m[8] * 100 : 0);
}

Upvotes: 11

maerics
maerics

Reputation: 156434

A regex is good for matching the general format but I think you should move parsing to the Date class, e.g.:

function parseDate(str) {
  var m = str.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
  return (m) ? new Date(m[3], m[2]-1, m[1]) : null;
}

Now you can use this function to check for valid dates; however, if you need to actually validate without rolling (e.g. "31/2/2010" doesn't automatically roll to "3/3/2010") then you've got another problem.

[Edit] If you also want to validate without rolling then you could add a check to compare against the original string to make sure it is the same date:

function parseDate(str) {
  var m = str.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/)
    , d = (m) ? new Date(m[3], m[2]-1, m[1]) : null
    , nonRolling = (d&&(str==[d.getDate(),d.getMonth()+1,d.getFullYear()].join('/')));
  return (nonRolling) ? d : null;
}

[Edit2] If you want to match against zero-padded dates (e.g. "08/08/2013") then you could do something like this:

function parseDate(str) {
  function pad(x){return (((''+x).length==2) ? '' : '0') + x; }
  var m = str.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/)
    , d = (m) ? new Date(m[3], m[2]-1, m[1]) : null
    , matchesPadded = (d&&(str==[pad(d.getDate()),pad(d.getMonth()+1),d.getFullYear()].join('/')))
    , matchesNonPadded = (d&&(str==[d.getDate(),d.getMonth()+1,d.getFullYear()].join('/')));
  return (matchesPadded || matchesNonPadded) ? d : null;
}

However, it will still fail for inconsistently padded dates (e.g. "8/08/2013").

Upvotes: 47

Naraen
Naraen

Reputation: 3250

If you are in Javascript already, couldn't you just use Date.Parse() to validate a date instead of using regEx.

RegEx for date is actually unwieldy and hard to get right especially with leap years and all.

Upvotes: 1

mVChr
mVChr

Reputation: 50185

You could take the regex that validates YYYY/MM/DD and flip it around to get what you need for DD/MM/YYYY:

/^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/

BTW - this regex validates for either DD/MM/YYYY or DD-MM-YYYY

P.S. This will allow dates such as 31/02/4899

Upvotes: 122

Related Questions