Reputation: 14913
I am getting a date field from the database in one of my variables, at the moment I am using the following code to check if the date is in "yyyy-mm-dd" format
if ( $dat =~ /\d{3,}-\d\d-\d\d/ )
My question, is there a better way to accomplish this.
Many Thanks
Upvotes: 3
Views: 11923
Reputation: 1452
I think using a regex without outer check is much to complicated! I use a little sub to get it:
sub check_date {
my $date_string = shift;
# Check the string fromat and get year, month and day out of it.
# Best to use a regex.
return 0 unless $date_string =~ m/^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
# 31. in a month with 30 days
return 0 if ($3 >= 31 and ($2 == 4 or $2 == 6 or $2 == 9 or $2 == 11));
# February 30. or 31.
return 0 if ($3 >= 30 and $2 == 2);
# February 29. in not a leap year.
return 0 if ($2 == 2 and $3 == 29
and not ($1 % 4 == 0 and ($1 % 100 != 0 or $1 % 400 == 0)));
# Date is valid
return 1;
}
I got the idea (and most of the code) from regular-expressions.info. There are other examples too.
Upvotes: 0
Reputation: 3153
The best and lightweight solution is using Date::Calc's check_date sub routine, here's an example:
use strict;
use warnings
use Date::Calc qw[check_date];
## string in YYYY-MM-DD format, you can have any format
## you like, just parse it
my @dt_dob = unpack("A4xA2xA2",$str_dob_date);
unless(check_date(@dt_dob)) {
warn "Oops! invalid date!";
}
I hope that was helpful :-)
Upvotes: 2
Reputation: 532
As noted by others, if this is a date field from a database, it should be coming in a well-defined format, so you can use a simple regex, such as that given by toolkit.
But that has the disadvantage that it will accept invalid dates, such as 2009-02-30. Again, if you're handling dates that successfully made it into a date-typed field in a DB, you should be safe.
A more robust approach would be to use one of the many Date/Time modules from CPAN. Probably Date::Manip would be a good choice, and in particular check out the ParseDate() function.
http://metacpan.org/pod/Date::Manip
Upvotes: 4
Reputation:
I would very strongly recommend AGAINST writing your own regular expression to do this. Date/time parsing is simple, but there are some tricky aspects, and this is a problem that has been solved hundreds of times. No need for you to design, write, and debug yet another solution.
If you want a regular expression, the best solution is probably to use my Regexp::Common::time plugin for the Regexp::Common module. You can specify simple or complex, rigid or fuzzy date/time matching, and it has a very extensive test suite.
If you just want to parse specific date formats, you may be better off using one of the many parsing/formatting plugins for Dave Rolsky's excellent DateTime module.
If you want to validate the date/time values after you have matched them, I would suggest my Time::Normalize module.
Hope this helps.
Upvotes: 0
Reputation: 2859
The OWASP Validation Regex Repository's version of dates in US format with support for leap years:
^(?:(?:(?:0?[13578]|1[02])(/|-|.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(/|-|.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:0?2(/|-|.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:(?:0?[1-9])|(?:1[0-2]))(/|-|.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$
The Regular Expression Library contains a simpler version along the lines of the other suggestions, which is translated to your problem:
^\d{4}-\d{1,2}-\d{1,2}$
Upvotes: 6
Reputation: 27248
Well you can start with:
/\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|30|31)/
Upvotes: 1
Reputation: 50237
How about
/\d{2}\d{2}?-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
Upvotes: 3
Reputation: 50349
\d could match number characters from other languages. And is YYY really a valid year? If it must be four digits, dash, two digits, dash, two digits, I'd prefer /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/
or /^[12][0-9]{3}-[0-9]{2}-[0-9]{2}$/
. Be aware of space characters around the string you're matching.
Of course, this doesn't check the reasonableness of the characters that are there, except for the first character in the second example. If that's required, you'll do well to just pass it to a date parsing module and then check its output for logical results.
Upvotes: 2