tags:

views:

279

answers:

5

Anyone have a good regex for Age verification?

I have a date field where I am validating that what the user enters is a date.

basically I wanted to figure out if that date is valid, and then further refine the date to be within x number of years

Perhaps this may be too complicated to tack onto whatever I have too far, but I figured it wouldn't be.

^([1][012]|[0]?[1-9])[/-]([3][01]|[12]\d|[0]?[1-9])[/-](\d{4}|\d{2})$
+1  A: 
<label><input type="checkbox" name="old_enough" value="you betcha!" /> I am old enough to visit this website.</label>

Regex: ^you betcha!$

This is not a task for regular expressions.

edit: Regarding your edit... Why try something like

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

when you can just do

if($age >= $minimum) { // verified! }

If you're getting a birthday, you could do something like this first to convert the birthday into timestamps (PHP used here):

$minimum = strtotime('-13 years');
$age = strtotime('YYYY-MM-DD');
ceejayoz
I can do it that way.. we had spent a little time making this validation structure for the fields that just needed a regex and you could modify that if we had add'l requirements. I just wanted to keep it clean.
KevinDeus
unfortunately ^you betcha!$ won't work as we need the user's birthdate for legal purposes.
KevinDeus
Using a regex for this sort of thing is the definition of **un**clean.
ceejayoz
depends on your situation..
KevinDeus
No, it doesn't. There are some things regular expressions simply are not suited for (and as regular expressions are not Turing complete, there are some things they *cannot* do).
ceejayoz
A: 

To check if an age is between 1-109

([1-9][0-9]?)¦(10[1-9])
Patrice Pezillier
110-year-olds need not apply.
Welbog
age is dependent on the current date. This appears to check for a number.
KevinDeus
This is why someone said "what's the specification?" in the comments. You haven't told us what data the user is being required to enter - are they checking a box, entering a birthday, or entering a numeric number of years?
ceejayoz
Is age more than but a number these days?
Johnsyweb
+1  A: 

You are making a mistake a lot of people seem to make (look at the activity in this tag!): you have a goal (in this case, age verification), and have decided that it must be accomplished with regexen. I'm not really sure why people do this—it's not like regexen are a magic panacea that will solve all your problems. The better question to ask is either: (a) "How should I go about performing age validation? I was thinking maybe a regex, but I'm not sure how to write one," or (b) "I'm using technology XYZ, and I need to perform age validation; because XYZ is structured in way PQR, I need to use a regex for this, but I'm not sure how to write one."

So if you're in situation (a), I would say this: why use a regex? I'm not sure how you're getting your input, but there seem to be two cases. If you're getting a single integer value for how old they are, just read it in from wherever you're getting it, convert it into an integer (e.g., via Java's Integer.parseInt(myStringInput)), and then check that the integer is in range with something like return (18 < age) ? true : false. If you're getting their birth date (as "years, months, and days" implies), then: read in the years, months, and days; convert them to integers; use them to create an instance of a Date class; and use its methods for measuring distances between times (again, this depends on your language).

If, on the other hand, you're in situation (b)… ugh. Yes, it would be heinous. If you're in the single-integer situation, it should be doable. If you want to match, say, ages greater than or equal to 18, you should do something like ^(1[8-9]|[2-9]\d|\d{3,})$: either 18, 19, any two-digit age above the teens, or three-or-more digit ages. If you want to match under-18s, then you want ^(\d|1[0-7])$. If you're in the birthdate situation, though… again, ugh. I'm not sure how I'd approach it. I hope you aren't. It's definitely doable; I think the right approach is to do something like the above. Match all the years that are before now-18; if the year is now-18, do something similar for months; and if the month is the same, do something similar for the days. But I hope you aren't in this situation.

Edit 1: You said you had written some sort of validation structure for the fields which assumed regexen. While I see why that would make you want to use them, it's going to limit what you can validate (and definitely what you can validate sanely). Perhaps it makes more sense to pass a callback function instead, which would let you deal with regexen or things like this.

Antal S-Z
+1 for a callback option.
ceejayoz
Antal, your comments are helpful and I agree completely with your points on where the work would be put to better use, but going outside the scope of the question leads to a lot more areas than is needed. It might make more sense to use a different method, and that is an option, but without going into the whole project architecture, does it invalidate the ability to come up with a regex?
KevinDeus
Does it make it *impossible*? No, because you have a finite number of strings to match (assuming you restrict yourself to four-digit years), so you could (but wouldn't) build it with a gigantic `|` statement. Does it make it terrible to write? Yes, it really, really does. And like someone else said, note that it is *impossible* to use regexen to validate certain other classes of input!
Antal S-Z
+1  A: 

If you can't use another method, could you at least calculate the number of days between your target birth date and present then just verify like this? it would simplify having to dynamically generate the top end of the date ranges such as [0-minmonthfirstdigit][0-minmonthseconddigit] etc...

Laramie
+3  A: 

I'm a big fan of regexes, and of course it's possible to check a date against a "legal range" using regexes.

BUT:

Matching a valid date (without fancy stuff like checking for leap years) is fairly trivial but tiresome by regex - a tool for constructing regexes like RegexMagic will do this for you quickly:

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

is what RegexMagic generates for MM/DD/YYYY date validation without imposing limits on date ranges (i.e. 02/31/2020 won't match, 01/31/4500 will).

This is already ugly and probably quite hard to figure out if you're the one inheriting this code.

The second problem, namely validating against a predefined date range is even uglier. It can be done, but

  1. you'd need to change your regex every single day - after all, every day, someone turns 18, don't they? and

  2. the regex would get even more unwieldy. To allow anything from 06/10/1992 until 06/10/2010, you get

this monster of a regex.

0?6[/.-](?:30|[12][0-9])[/.-]1992|(?:(?:0?9|11)[/.-](?:30|[12][0-9]|0?[1-9])|
(?:0?[78]|1[02])[/.-](?:3[01]|[12][0-9]|0?[1-9]))[/.-]1992|0?6[/.-](?:10|0?[1-9])
[/.-]2010|(?:0?2[/.-](?:[12][0-9]|0?[1-9])|0?4[/.-](?:30|[12][0-9]|0?[1-9])|0?
[135][/.-](?:3[01]|[12][0-9]|0?[1-9]))[/.-]2010|(?:0?2[/.-](?:[12][0-9]|0?[1-9])|
(?:0?[469]|11)[/.-](?:30|[12][0-9]|0?[1-9])|(?:0?[13578]|1[02])[/.-](?:3[01]|
[12][0-9]|0?[1-9]))[/.-](?:200[0-9]|199[3-9])

(linebreaks included for "clarity").

Tomorrow, you'll need

0?6[/.-](?:30|2[0-9]|1[1-9])[/.-]1992|(?:(?:0?9|11)[/.-](?:30|[12][0-9]|0?[1-9])|
(?:0?[78]|1[02])[/.-](?:3[01]|[12][0-9]|0?[1-9]))[/.-]1992|0?6[/.-](?:1[01]|0?[1-9])
[/.-]2010|(?:0?2[/.-](?:[12][0-9]|0?[1-9])|0?4[/.-](?:30|[12][0-9]|0?[1-9])|0?
[135][/.-](?:3[01]|[12][0-9]|0?[1-9]))[/.-]2010|(?:0?2[/.-](?:[12][0-9]|0?[1-9])|
(?:0?[469]|11)[/.-](?:30|[12][0-9]|0?[1-9])|(?:0?[13578]|1[02])[/.-](?:3[01]|
[12][0-9]|0?[1-9]))[/.-](?:200[0-9]|199[3-9])

Notice the subtle difference?

In short, as all the others have pointed out: This is not something you want to use a regular expression for. Even if you can.

Tim Pietzcker
I was thinking about how to approach this algorithmically, and I completely forgot the "December 42nd" issue, much less the "June 31st" one. Damn… it's worse than I expected.
Antal S-Z