1
votes

The format of the regex is mm/dd/yyyy.

My current regex currently has a tiny problem, which I could not pinpoint. I could validate mm/dd/yyyy with valid months, leap year and year range. Although when the year written is '2016asdf' it still accepts the value.

The rules it should follow are:

  • Only valid dates are allowed
  • Feb 29 can be counted if it is a leap year
  • Year value ranges from 1900 - 2099

Valid values:

12/31/2016

01/15/1900

02/29/2016 (since 2016 is a leap year)

Invalid values:

1/01/1900 (since format is 'mm' not 'm')

01/32/1900 (since there's no such date)

13/22/1900 (since there's no such month)

03/01/2100 (beyond the year limit)

02/29/2017 (since 2017 is not a leap year)

5
Welcome to SO, please have a look at it before asking : stackoverflow.com/helpSagar V
Don't parse dates with regexp.user663031
Why dont't you use new Date(string) ?Maciej Kozieja
@MaciejKozieja the instructions specified me to use regex onlyRin Minase
@torazaburo I need to parse it with regex.Rin Minase

5 Answers

6
votes

You got it incorrect. This shouldn't be done via Regex and in fact, it is not meant to do that. In order to that, you need to take the following steps:

1- Check the pattern via Regex

2- Validate the date.

The pattern you are looking for is:

var pattern = /(0\d{1}|1[0-2])\/([0-2]\d{1}|3[0-1])\/(19|20)\d{2}/

Take a look at this to see the pattern: http://regexr.com/3fe3s

Finally, if you want to check whether the date is a valid date or not (after checking in the pattern).

Here is the entire method:

    function isValidDate(dtValue2) {
    // your desired pattern
    var pattern = /(0\d{1}|1[0-2])\/([0-2]\d{1}|3[0-1])\/(19|20)(\d{2})/
    var m = dtValue2.match(pattern);
    if (!m)
        return false;
    var d = new Date(dtValue2);
    // Now let's ensure that the date is not out of index


    if (d.getMonth()+1 == parseInt(m[1], 10) && d.getDate() ==parseInt(m[2], 10)) {
        return true;
    }
    return false;
}
1
votes

There is a very thorough answer here. You can read the explanations and caveats of using that method.

31 day months

(0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2}

30 day months

(0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2}

February 1-28 always valid

(02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2}

February 29 also valid on leap years

(02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)

which means it would be this if you put it all together:

((0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})|((0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2})|((02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

This version is a little shorter, but a little harder to understand.

((0[13578]|1[02])[\/.]31[\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\/.](29|30)[\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))

These scripts are long and unmaintainable. It should be clear that this isn't a good idea, but it is possible.

Caveats:

  • range 1800-2099 (more can be added without too much difficulty, but requires changes in 4-6 disparate places)
  • requires 2 digit months and days (the strictness could be removed from the expression in ~8 places)
  • [/.] as seperators (8 places)
  • Hasn't been tested (we could check it against all digit combinations and compare with the avascript date function? [proof that we're reinventing the wheel])
0
votes

It needs to have ^ and $ on every | so the regex should be:

^((0[13578]|1[02])[/.]31/.[0-9]{2})$|^((01|0[3-9]|1[1-2])/./.[0-9]{2})$|^((0[1-9]|1[0-2])/./.[0-9]{2})$|^((02)[/.]29/.)$

Working expression

0
votes

I would try this
^02\/(?:[01]\d|2\d)\/(?:19|20)(?:0[048]|[13579][26]|[2468][048])|(?:0[13578]|10|12)\/(?:[0-2]\d|3[01])\/(?:19|20)\d{2}|(?:0[469]|11)\/(?:[0-2]\d|30)\/(?:19|20)\d{2}|02\/(?:[0-1]\d|2[0-8])\/(?:19|20)\d{2}$
:

const regex = /^02\/(?:[01]\d|2\d)\/(?:19|20)(?:0[048]|[13579][26]|[2468][048])|(?:0[13578]|10|12)\/(?:[0-2]\d|3[01])\/(?:19|20)\d{2}|(?:0[469]|11)\/(?:[0-2]\d|30)\/(?:19|20)\d{2}|02\/(?:[0-1]\d|2[0-8])\/(?:19|20)\d{2}$/

const testData = [
  '02/02/1904',
  '02/12/2008',
  '02/29/2012',
  '02/29/2013',
  '06/31/2013',
  '01/31/2013',
  '02/02/1916',
  '02/02/2020',
  '02/02/2024',
  '02/02/2036',
  '02/02/1952',
  '02/02/2056',
  '01/01/2256',
  '02/29/2000',
  '02/29/2100',
  '02/29/1900',
  '02/29/1200',
  '02/29/2400'
]

for (let date of testData) {
  console.log(`${date} ${regex.test(date)}`)
}
0
votes

It validates these formats:

  • YYYY-DD-MM
  • YYYY/DD/MM
  • YYYY.DD.MM
  • YYYY.0M.0D
  • YYYY/0D/0M
  • YYYY-0D-0M
  • YYYY-D-M
  • YYYY.D.M
  • YYYY/D/M and so on
/(((19\d{2}|20[0-9]{2}))(\/|\.|\-)(0?[1-9]|1[0-9]|2[0-9]|3[0-1])(\/|\.|\-))(0[0-9]$|1[0-2]$|[0-9]$)/gm