Regex date validation with 2/2/4 characters

Asked

Viewed 806 times

2

Looking hard, I found some explanations on how to validate date with Regex but my knowledge in regex is a little limited yet.

With some modifications in the explanations I found, I came to a nice object but I did not understand exactly where I modify to accept only the format DD/MM/AAAA because what I have accepts both this format but also accepts the formats D/M/AA or DD/M/AA or D/MM/AAAA or whatever.

That is, I wanted to validate only the format with the following number of characters 2/2/4 or DD(2 números)/MM(2 números)/AAAA(4 Números)

In case he accepts 1|2 / 1|2 / 2|4 characters. How would I do in the example https://regexr.com/4b1eb | https://regex101.com/r/cRGVvH/1 be able to accept only the format 2/2/4 characters or DD/MM/AAAA or 29/02/2016 and not 29/2/16.

This expression also validates leap year and months that do not have 31 days, and works well on this issue what did not want to change because it works legal at this point to validate leap year and months without 31 days

^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26]))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$

And in this case, I can also accept the characters / or - or . as splitter that has no problem at this point also that in the case is this stretch (\/|-|\.) that repeats itself a few times in the expression where if I want to limit the / all I have to do is (\/) that it accepts only bar, but I could also accept nada how to divide doing this (\/|-|\.|) I just don’t know if it’s the best way to accept nada as a divider.

I mean, the squeeze is functional, even though it’s huge it’s functional, at least it appears to be, I just really wanted to not allow less than two numbers for Day, two numbers for Month and four numbers for Year, it’s okay that a smaller squeeze that continues to validate leap year and months without 31 days would help but I can’t use javascript on you, it needs to be regex.

This example given as a possible solution by @hkotsubo

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

Where I modified according to your guidance and added the nada as divisor is pretty cool, I’ll just see how put the validators of leap and months without 31 days, ie, see if it is possible to merge one in the other, the nothing I include in this excerpt (\/|\-|\.|) if otherwise better, I also accept the explanation

2 answers

7


Honestly, this regex that you are using, in my opinion, is nothing practical and I would avoid using in any system in production. Just see the difficulty you’re having to understand and modify it. Think about whether it’s really worth it.

This regex makes validations that are much easier to do outside of it, such as checking if a year is leap (and knowing whether February should be 28 or 29 days), and checking months with 30 or 31 days. But why all this, if it is much simpler to check out it? Regex processes text, even digits like 1 or 2 are treated as mere characters, and any mathematical calculation is much easier to do outside the regex.

That being said, if you receive a string and want to check if it matches a date in the 'dd/mm/yyyy' format, you do not need to use a super-regex-complicated-all-rounder.

You can use a simpler regex, only to validate the format "two digits, bar, two digits, bar, four digits" and then validate these values separately:

let match = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec('20/10/2018');
if (match) {
    let dia = parseInt(match[1]);
    let mes = parseInt(match[2]);
    let ano = parseInt(match[3]);

    let d = new Date(ano, mes - 1, dia);
    let dataValida = d.getFullYear() == ano
                     && d.getMonth() + 1 == mes
                     && d.getDate() == dia;
    console.log(dataValida); // true
} else {
    console.log('string não está no formato dd/mm/aaaa, ou não é uma data');
}

The regex only serves to check if the string has exactly two digits, bar, plus two digits, bar, plus 4 digits. The markers ^ and $ means, respectively, the beginning and the end of the string, thus ensuring that the string only has what is in regex.

Using \d{2} and \d{4} I guarantee the correct amounts of digits, and in case the regex returns a match, I can turn the respective values into numbers, using parseInt. This is possible because each number is in parentheses in the regex, and this forms a catch group (with this I can get each group using match[numero_do_grupo]).

The builder of Date receives the year, month and day, with the care of subtracting 1 of the month, since for this class January is zero, February is 1, etc.

The problem is that this class accepts invalid values, such as month 13, day 32, etc., and makes some adjustments to the final result (for example, if you try to create January 32, the result is February 1). Therefore, to know if the values of the day, month and year are valid, just compare them with the Date valet. If there was no adjustment it is because the values are valid, otherwise it is an indicative that some value is invalid (it can be day 31 in months that only has 30 days, or February 29 in a non-leap year, etc).


If you want to use other tabs besides the bar (such as - or .), the regex changes a little:

let match = /^(\d{2})([-\/.]?)(\d{2})\2(\d{4})$/.exec('20.10.2018');
if (match) {
    let dia = parseInt(match[1]);
    let mes = parseInt(match[3]);
    let ano = parseInt(match[4]);

    let d = new Date(ano, mes - 1, dia);
    let dataValida = d.getFullYear() == ano
                     && d.getMonth() + 1 == mes
                     && d.getDate() == dia;
    console.log(dataValida); // true
} else {
    console.log('string não está no formato dd/mm/aaaa, ou não é uma data');
}

Now I put another capture group to the tab: ([-\/.]?). The excerpt [-\/.] means "a hyphen, or a bar, or a point", and the ? makes this section optional.

Then I use the reference \2, which means "the same string that was captured in group 2". Group 2 is the second pair of parentheses, which in this case is the separator. This ensures that the same separator that was used between day and month will be between month and year. And as it is optional, if there is no separator, the \2 will be the empty string, ensuring that either it has the same separator, or it has no.

Another point of attention is that as I have included another capture group (for the tab), the month and year are now groups 3 and 4 (match[3] and match[4]).


Moment js.

Another alternative is to use the library Moment js.. In case, you can pass an array of possible formats and then use isValid() to determine whether the date is valid.

let formatos = ['DD/MM/YYYY', 'DD-MM-YYYY', 'DD.MM.YYYY', 'DDMMYYYY'];

console.log(moment('29/02/2016', formatos).isValid()); // true
console.log(moment('30-04-2017', formatos).isValid()); // true
console.log(moment('31.12.2018', formatos).isValid()); // true
console.log(moment('10032019', formatos).isValid()); //true

// datas inválidas
console.log(moment('29/02/2017', formatos).isValid()); // false
console.log(moment('31-04-2017', formatos).isValid()); // false
<script src="https://momentjs.com/downloads/moment.min.js"></script>


If you want even proceed with the giant regex, you can withdraw the ? after zeros, so they are no longer optional (with the optional zero to regex accepts 04 and 4).

I also traded the separators for [-\/.]? to make them optional. Your regex uses \/|-|\. (a bar or hyphenate or point), but when we have several options with only one character each, it is easier to use the square brackets. Putting the ? then make the snippet optional, and from what I saw regex uses the same thing I did in the second example above, using \1, \2, etc so that the separator is the same.

Finally, I removed the ? after the first two digits of the year, so regex will require years with only 4 digits:

^(?:(?:31([-\/.]?)(?:0[13578]|1[02]))\1|(?:(?:29|30)([-\/.]?)(?:0[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)\d{2})$|^(?:29([-\/.]?)02\3(?:(?:(?:1[6-9]|[2-9]\d)(?:0[48]|[2468][048]|[13579][26]))))$|^(?:0[1-9]|1\d|2[0-8])([-\/.]?)(?:(?:0[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)\d{2})$

See here some examples of it working. Below are some tests as well:

let r = /^(?:(?:31([-\/.]?)(?:0[13578]|1[02]))\1|(?:(?:29|30)([-\/.]?)(?:0[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)\d{2})$|^(?:29([-\/.]?)02\3(?:(?:(?:1[6-9]|[2-9]\d)(?:0[48]|[2468][048]|[13579][26]))))$|^(?:0[1-9]|1\d|2[0-8])([-\/.]?)(?:(?:0[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)\d{2})$/;

console.log(r.test('29/02/2016')); // true
console.log(r.test('30-04-2019')); // true
console.log(r.test('20.12.2018')); // true
console.log(r.test('15032019')); // true


console.log(r.test('10-10/2018')); // false
console.log(r.test('29/02/2017')); // false
console.log(r.test('31/04/2018')); // false
console.log(r.test('32/01/2019')); // false

  • 2

    Without a doubt, the @hkotsubo kills letter when it comes to regex.

  • without doubt incredible explanation, and its function is really great, the problem is that I really need Regex for not being able to use javascript, I can only use regex even

  • @flourigh I updated the answer with an alternative, see if that’s what you need

  • @flourigh if you can’t use JS, you could ask the [tag:javascript]

  • is that the site uses javascript, I can not send javascript, I can put a regex in the indicated location that is all done in javascript, or vuejs

  • 1

    @Andréfilipe the guy is too genius, besides explaining in detail, made the thing work perfectly, did not solve the problem, taught to solve by showing the errors, man, I understood more in this answer than in several manuals

  • 1

    @flourigh Most "manuals" of regex are really bad, because they only explain the syntax, but not how to use it. But it has good sites too, I like it a lot that and that. And books, I recommend that and that :-)

  • @hkotsubo Thank you very much for helping also with the references for study, will help me very much because I need to do several validations with regex, now I’m going to go to the validation of CPF, I have qm javascript but also need to be in regex, I hope one day I can help as you helped me

Show 3 more comments

0

Man, I don’t quite understand your question, but... I believe this Regex solves your problem:

\d{2}[-\/\.]\d{2}[-\/\.]\d{4}|\d{8}

It accepts date formats like (I believe that’s what you asked right?):
dd/mm/yyyy
dd.mmyyyy
dd-mm-yyyy
ddmmyyyy

  • really, that’s right, but I need to know if the year is leap too, in the case of your it only validates quantity, in case, I need the checks that are contained in the example I gave, which checks year, day of the month among other words, whether the date even exists

  • The problem is that this regex also accepts mixed separators such as 23/01-2019 or 23-01.2019, see

  • 1

    @hkotsubo in this case, we can use regex mirrors, it would look something like this: link However, for what was asked I do not indicate the use of regex because the date needs to be validated when we take into account the months that are 28, 30, 31 days (apart from the bisex years)...

  • @A.Chiele Yes, I agree 100% with you (regex is not the best solution for this problem), and the solution you suggested I had already put in my answer :-)

  • @hkotsubo really its expression is good and does not allow year with 2 characters and I tested with leap year 29/02/2016 and it worked, nullifying 2015 for example, in case I did not understand for sure, your expression does not calculate these dates? " because the date needs to be validated when we take into account the months that have 28, 30, 31 days (outside the bisex years)", but I found an error in the Month, it does not validate 10|11|12 only from 01-09 or 1-9 not validating those with beginning 1 (Oct, Nov, ten)

  • @flourigh If you’re talking about the giant regex, it uses several alternations (|), that is, several possibilities (day x and month and year z or day to, month b and year c or, etc). It tests various possibilities by making 29/02 accepted only in leap years, etc. But the regex does not account for (she does not divide the year by 4 to know if it is leap, for example), what she does is to test several options so that in the end the values are only expected (that’s why she gets so big)

  • @flourigh In my reply I added some tests at the end and see that I tested with 20.12.2018 and it worked. If you can edit your question by putting exactly the tests that fail, I can take a look

  • @hkotsubo I didn’t quite understand, the first time I clicked on this link https://regex101.com/r/cRGVvH/1 the date 29/02/2015 did not work, as I commented, but now it worked, how weird, I understand the information about size and account and everything they mentioned about the giant regex, the one I put in the example, works with leap year and invalidates months without 31 days, I just really wanted to know where I invalidate the amount of characters for DD(2)/MM(2)/YYYY(4) to be able to use better, I just did not understand this part of it

  • @flourigh This regex that you have passed (https://regex101.com/r/cRGVvH/1) only checks whether the day is between 1 and 31. The other rules (February has 28 or 29 days depending on the year, etc.), only the giant regex has. And the regex of this link accepts months with 1 digit because of the (0[1-9]|[1-9]|1[0-2]) (look in the middle has |[1-9]|, which is a digit from 1 to 9, remove this and just leave (0[1-9]|1[0-2]) that there will accept only months with 2 digits. Another detail is that \d{1} is redundant, can only use \d which is the same thing

  • @hkotsubo really, I noticed later, it must have been a mistake of mine in the euphoria, but it started to give error in the validation of the month when I added a | in this excerpt (/|-|.|) to allow nothing as a splitter, I edited the question

  • 1

    @flourigh Na my answer I put an alternative to the (\/|\-|\.|) (at the end of it, see the last regex), you came to test it?

  • @hkotsubo really, it’s right there, I think I walked right past it after I saw the Avascripts, I’ll read it right, it actually worked even https://regex101.com/r/afx8f1/1/

Show 7 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.