Regex validating only normal and leap years in C++

Asked

Viewed 334 times

3

I was trying to validate leap years using regex in C++.

If the user type 28/02/1900, it would return valid. But if the same type 29/02/1900, he would return error.

Searching, I found this regex that compiled, but is not validating.

const std::regex pattern("^(?:(?:31(/)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(/)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$|^(?:29(/)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\\d|2[0-8])(/)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\\d)?\\d{2})$");

I tried to simplify based on another regex for something like:

const std::regex pattern("(0[1-9]|1[0-9]|2[8|9])[*-. /](0[2]|Feb)[*-. /](19[04|08|12|16])\\d{2,2}"); //dd/mm/yyyy

I simplified from this based on the first:

const std::regex pattern("(0[1-9]|1[0-9]|2[8|9]|3[0|1])[*-. /](0[1-9]|1[0|1|2]|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[*-. /](19|20)\\d{2,2}"); //dd/mm/yyyy

To conclude I am trying to write a regex that reads both but if the person type more than 29 days for February and 29 days for a non-leap year, it should not validate.

If anyone can help me I’d be grateful.

  • 3

    Do you need through regular expressions to do that account? It is not better to do a simple account no?

  • yes he wanted the calculation to be done only by regex validating only by it...

  • 2

    I’m with @Jeffersonquesado. Sounds like a cannon to kill a fly

  • 3

    Why do you want to validate if a date is leap using regular expressions? This seems to be a XY problem.

  • 1

    @Isac is not a cannon to kill fly. It is an electric weld to kill a fly. Namely: A tool totally unsuitable for work.

  • 1

    @Victorstafusa Yes it would be more correct to see it that way

  • It is possible, but it will be long and boring. I will do over the definition: multiple of 400, or else multiple of 4 but not 100

  • 1

    @Jeffersonquesado Doing mathematics with regular expressions is a terrible thing.

  • i have programs in c++ that validates but wanted to learn and understand how to do only by regex as it would be because if I am not mistaken in other languages I think I would have but my focus is to do it using c++...

  • @Victorstafusa I know, so the part of "extensive and boring@. Could by tedious too

  • 3

    @dark77 Learning to use regex also concerns knowing what kind of work they are not able to do or are a bad choice. Regex is not a good answer to this problem.

  • yes is a bad choice but it would be really to understand and learn how would be using only the same...

  • 1

    @dark777 I understand what you say about trying to learn. My view is that this is so far from the real context of a regex that it does not ultimately learn much, because much of it will not be applicable in other contexts. I think it becomes much more productive and easy to learn from other realistic and useful examples of regex

  • https://stackoverflow.com/questions/24319295/date-leap-year-validation-regex this can help you.

  • @dark777 I replied. Much more mathematics than regex. There is no escape. Look for another alternative to study regex in a less mathematical way, such as recognizing emails

  • I’ve done a regex to validate emails until I created a theme here and the regex I found searching on google was absurdly big full of things that in my opinion were unnecessary so I was moving until shortening and validate in the formats I needed was show ball... This is the topic: https://answall.com/questions/182756/express%C3%a3o-regular-para-e-mail-em-c

Show 11 more comments

2 answers

9


You got the gun, I give you the ammo, but you decide whether to shoot yourself in the leg or not.

I come here to offer you the regex that recognizes leap years, does not do everything as the reply of @Victorstafusa. My regex also does not have Lookahead, then it tends to make it longer

First, definition of leap year:

multiple year of 400, or if multiple of 4 but not 100.

Bearing in mind that we are dealing with decimally represented numbers. I will simplify here and assume that every year has 4 digits. It means that 720 is not possible, but 0720 is.

A multiple of 400 is a multiple of any 4 followed by 00. So I need an expression that detects multiples of 4 of two digits. A number is multiple of 4 of 2 digits (or more) in base 10 if:

  1. End with an even number
  2. If the unit digit is multiple of 4, the number of tens needs to be even
  3. If the digit of the units is not multiple of 4, then the digit of the dozens needs to be odd

So we have the following expression to recognize multiples of 4:

[02468][048]|[13579][26]

To recognize multiples of 400, just by 00 in the end:

([02468][048]|[13579][26])00

To be multiple of 4 but not 100, it is basically that first expression, but the 0 of the tens is a special case because it only accepts 4 or 8:

[2468][048]|[13579][26]|0[48]

To ignore the first digits:

..([2468][048]|[13579][26]|0[48])

Putting it all together:

([02468][048]|[13579][26])00|..([2468][048]|[13579][26]|0[48])

7

Well, regex is not a good alternative to this problem. Because this is a mathematical problem and regex don’t know how to do math, they just evaluate repetitions in strings.

However, as a curiosity exercise, we will use regex anyway.

First, let’s start with something that recognizes a number from 01 to 28 (days they have in all months):

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

Something that recognizes a number from 01 to 12:

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

Bringing the two together:

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

For months with 30 or 31 days:

(?:(?:29|30)\/(?:01|0[3-9]|1[0-2]))|(?:31\/(?:01|03|05|07|08|10|12))

Something that recognizes numbers of years from 1583:

(?:[2-9][0-9]{3}|1[6-9][0-9]{2}|159[0-9]|158[3-9])

Putting it all together:

(?:(?:0[1-9]|1[0-9]|2[0-8])\/(?:0[1-9]|1[0-2])|(?:(?:29|30)\/(?:01|0[3-9]|1[0-2]))|(?:31\/(?:01|03|05|07|08|10|12)))\/(?:[2-9][0-9]{3}|1[6-9][0-9]{2}|159[0-9]|158[3-9])

That was the easy part. It recognizes every day other than February 29th.

On the days that are February 29th, the day and the month are easy:

29\/02

The last two digits of years that are always leap:

(?:04|08|[2468][048]|[13579][26])

Thus, those years not completed in 00 are leap:

(?:[2-9](?:04|08|[2468][048]|[13579][26])|1[6-9](?:04|08|[2468][048]|[13579][26])|159(?:2|6)|158(?:4|8))

These are the years since 1583 ended in 00 and leap:

(?:16|[2468][048]|[3579][26])00

Combining this, you get to all 29 Februaries since 1583:

29\/02\/(?:(?:[2-9](?:04|08|[2468][048]|[13579][26])|1[6-9](?:04|08|[2468][048]|[13579][26])|159(?:2|6)|158(?:4|8))|(?:16|[2468][048]|[3579][26])00)

Combining:

(?:(?:0[1-9]|1[0-9]|2[0-8])\/(?:0[1-9]|1[0-2])|(?:(?:29|30)\/(?:01|0[3-9]|1[0-2]))|(?:31\/(?:01|03|05|07|08|10|12)))\/(?:[2-9][0-9]{3}|1[6-9][0-9]{2}|159[0-9]|158[3-9])|29\/02\/(?:(?:[2-9](?:04|08|[2468][048]|[13579][26])|1[6-9](?:04|08|[2468][048]|[13579][26])|159(?:2|6)|158(?:4|8))|(?:16|[2468][048]|[3579][26])00)
  • I proposed a simpler way to detect leap years. Fewer things to type, I think will give a simpler expression to your answer, which is much more complete than mine

  • 1

    @Jeffersonquesado Thanks. I applied the simplification.

  • @Jefferson Quesado I took a look at this link that was passed in the above comments: https://stackoverflow.com/questions/24319295/date-leap-year-validation-regex gave a mechida na regex trying to understand it and I got the following result: Std::regex Pattern(" (?: d{4}[-. /](?:(?:(?:(?:0[13578]|1[02]|Jan|Mar|May|Jul|Aug|Oct|Dec)*-. /)|(?:(?:0[469]|11|Apr|Jun|Sep|Nov)*-. /)|(?:0[2]|Feb*-. /)))|(?:(?:\d{2}(?: 0[48]|[2468][048]|[13579][26]))|(?: (02468][048])|[13579][26])00)[-. /]Feb[*-. /]29)$");

  • with the above regex I can validate the formats: 1904-Feb-29, 1904Feb29, 1904.Feb.29, 1904/Feb/29 but I’m trying to make sure that the same makes 1904/02/29 still could not so that it works I will base myself on their explanations to do the opposite: 29/02/1904, 29-02-1904,29.02.1904,29021904...

  • @dark777 What formats do you want anyway? This one only accepts DD/MM/YYYY, where all of them are numerical. Building a regex for the other formats, or even accepting all formats, is not very difficult, but you have to say which formats matter.

  • Indifferent because my intention was: dd/mm/yyyy in the above regex I added other formats to be validated as dd-mm-yyyy etc. A regex above it validates yyyy/mm/dd bearing the format would be indifferent if the person used 1904/02/29; 1904/Feb/29; 1904-February-29 1904-02-29; 1904-Feb-29; 1904-February-29 now I have to understand how to do the opposite since 29/02/1904; 29/Feb/1904; 29/February/1904;29-02-1904; 29-Feb-1904; 29-February-1904

  • Now I have studied them and made this regex Pattern("(?: (?: (?: 0[1-9]|1[0-9]|2[0-8])*-. /|(?:(?:29|30)*-. /)|(?:31*-. /))*-. /|(?:(?:(29[-. /]02[-. /]:?))(?:(?:\d{2}(?: 0[48]|[2468][04 8]|[13579][26]))|(?: (?: [02468][048])|[13 579][26])00)))$");

  • @dark A date of type "11/10-2017" is valid?

  • I didn’t realize that I’m touching it to modify.

Show 4 more comments

Browser other questions tagged

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