Re: Simple Date Validation
by Corion (Patriarch) on May 11, 2007 at 15:36 UTC
|
Looking at the Time::Local documentation, I see the following:
The timelocal() and timegm() functions perform range checking on the input $sec, $min, $hour, $mday, and $mon values by default.
So, I would just exploit that and hand the parts of the date to, say, timelocal, and see if it accepts these values as valid or not:
my ($sec,$min,$hour) = qw( 0 0 12 );
my ($mday, $mon, $year) = qw(11 5 2007);
$mon--; # because that's how unix/C treat the month value
eval {
my $dummy = timelocal($sec,$min,$hour,$mday,$mon,$year);
};
if (my $err = $@) {
print "This is an invalid date.";
} else {
print "Yay";
};
Splitting up $userdate into the parts making up the day, month and year is left as an exercise to the reader. | [reply] [d/l] [select] |
|
How do I go about handing a date that is in this format: MM/DD/YYYY into that type of validation?
| [reply] |
|
$userdate = "12/31/2005";
my ($mon, $mday, $year) = split /\//, $userdate;
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] |
|
Just split it.
Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!
| [reply] |
|
unfortunately timelocal does not validate dates properly...
# perl -e 'use Time::Local; print timelocal(0,0,0,31,02,2007);'
1175295600
#
| [reply] |
|
Q:\>perl -MTime::Local -e "print timelocal(0,0,0,31,01,2007)"
Day '31' out of range 1..28 at -e line 1
| [reply] [d/l] |
Re: Simple Date Validation
by shmem (Chancellor) on May 11, 2007 at 16:07 UTC
|
What does validate mean in this context? Do you want to check whether the submitted date conforms to some format? Or do you want to check whether the submitted date is in some valid range?
It all depends on the format used in $userdate. Is it YYYY-MM-DD ? DD.MM.YY ? MM-DD-YYYY ? Jan 11 05 ? Does it contain time?
Without some restrition on the accepted format, there's no way to tell whether 01.02.03 is valid, and which part of that string means year, month and day respectively.
If you have some pre-defined format, a regular expression would suffice to check whether $userdate complies. Then you know which fields mean what. To convert e.g. Jan 12 2007 to a unix timestamp with Time::Local
and validate there's no day or month overflow (e.g. Apr 31 2007):
use Time::Local;
my $userdate = "Jan 12 2007";
my @months = qw (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov
+ Dec);
@monthname {@months} = 0 .. 11;
my ($m, $day, $year) = split /\s+/, $userdate;
defined $monthname {$m} or die "no valid month\n";
my $month = $monthname {$m};
for ($day, $year) { /^\d+$/ or die "$_ invalid\n" }
my $timestamp = eval { local $SIG{__DIE__}; timelocal (0, 0, 0, $day,
+$month, $year) };
if ($@) {
warn "invalid date: $@\n"
} else {
my @ltime = localtime ($timestamp);
printf "date: %02d %s %04d\n", $ltime [3], $months [$ltime[4]], $l
+time [5] + 1900;
}
print "done\n";
__END__
That properly warns of e.g. "Feb 31 2007" being an invalid date, but reports "29 Feb 2004" as being correct.
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] [select] |
Re: Simple Date Validation
by johngg (Canon) on May 11, 2007 at 15:44 UTC
|
I have the following which I converted from C a long time ago when I was very new to Perl. It was set up for the UK so the Julian/Gregorian change is in 1752.
# ------
sub isLeap
# ------
{
my $year = shift
or croak "isleap(): no year supplied\n";
croak "isLeap(): year not numeric\n"
unless $year =~ /^\d+$/;
return 0 if $year % 4;
return 1 if $year < 1753;
return 1 if $year % 100;
return 1 unless $year % 400;
return 0;
}
# -------
sub valDate
# -------
{
my($year, $month, $day) = @_;
return 0 unless
$year =~ /^\d+$/
and $month =~ /^\d+$/
and $day =~ /^\d+$/;
my $daysinm = [
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]];
return 0 if $year < 1 or $year > 9999;
return 0 if $month < 1 or $month > 12;
return 0 if $day < 1 or
$day > $daysinm->[isLeap($year)]->[$month - 1];
return 0 if $year == 1752 and $month == 9 and
($day > 2 and $day < 14);
return 1;
}
It works for any year from 1 to 9999 (not y10k compliant I'm afraid ;-) I hope this is of use.
Cheers, JohnGG | [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
return 0 if $year % 4;
so your amendment, and not $year %4, is superfluous.
Cheers, JohnGG | [reply] [d/l] [select] |
Re: Simple Date Validation
by starX (Chaplain) on May 11, 2007 at 15:27 UTC
|
Someone else here will probably be able to offer a more elegant solution, but I've done something like:
my $time; # Scalar to store the time in appropriate format for compari
+sons with the database time stamp.
my ($year, $month, $day, $hour, $minute, $second); # for assembling ti
+me stamp.
my @localtime = localtime; # Buffer to store time returned from localt
+ime function
$year = 1900 + $localtime[5];
$month = $localtime[4] + 1; # because localtime starts counting months
+ at 0
$month = sprintf("%02d", $month); # force 2 digit format.
$day = sprintf("%02d", $localtime[3]);
$hour = sprintf("%02d", $localtime[2]);
$minute = sprintf("%02d", $localtime[1]);
$second = sprintf("%02d", $localtime[0]);
$time = $year.$month.$day.$hour.$minute.$second;
And then compared the time string against what I was trying to validate. This node is where I was using the code snips above, and it was fairly specific to the format I was validating against, but I don't see any reason why you couldn't do a similar comparison between $userDate and $time if you've got your $time string formated according to your needs. | [reply] [d/l] |
Re: Simple Date Validation
by 2xlp (Sexton) on May 11, 2007 at 19:34 UTC
|
I'd learn how to install modules.
CPAN Modules are neat -- you can install them server side if you have root privs, or you can just install them into your local directory
I'd strongly suggest running the cpan shell ( $perl -MCPAN -e shell ) , then upgrading cpan ( install Bundle::CPAN ) and following the instructions to restart the shell and install modules away. Once you get comfortable with that, you can su/sudo to root and install system-wide -- or not.
Date::Calc is really the best thing you can use for this situation. It's fast and takes into consideration all of the crazy issues that other people have encountered.
| [reply] |
Re: Simple Date Validation
by RL (Monk) on May 11, 2007 at 17:37 UTC
|
Some code I tend to use.
Problem I see with users providing dates: No way to make sure how they write it into the input field. It might be valid even if it's not the required format.
It's even possible a script might be used in Europe, where time format is different to US :)
examples of valid dates as of real life as I see them:
1.12.2006
01.12.2006
01.12.06
12/1/2006
12/01/06
right
sub parse_datestring {
# parses a datestring
# - allows to provide a format of how to interprete day,month,year
+ in the datestring.
# default format is: DD/MM/YY
# -> calling this method with format=>'d.m.y' computes fa
+ster than format=>'dd.mm.yy' !
# - any non-digit character in the datestring is considered a deli
+miter!
# - 2-digit years will be considered 1930-2029
# - by default the date calculated from the datestring will be val
+idated
# -> validate=>'0' disables validation
# - return value is an array of ($day, $month, $year)
# -> $day and $month are returned 2-digit
# -> $year is returned 4 digit
# ===> in case of invalid date ALL of the above will be unde
+f
# ===> in case of omitted day or month, the appropriate arra
+y elements will be undef
# - in case of errors such as invalid date $errstr is set and migh
+t be retrieved by $handle->errstr();
# - example call:
# my ($day,$mon,$year) = $handle->parse_datestring( 'datestring
+' => '02.02.02',
# 'format' =>
+ 'd.m.y', # optional
# 'validate'
+=> 0, # optional
# )
#
my $self = shift;
my %classargs = (@_);
# reset $errstr
$errstr = '';
my ($d,$m,$y);
# make sure 'datestring' is not ommited!
if (! $classargs{'datestring'}) {
$errstr = 'ERROR parsing datestring: \'datestring\' is missing
+';
}
# make sure delimiter becomes '/'
$classargs{'datestring'} =~ s/\D+/\//gi;
# handle input like '06.2004' or '6.06' - assuming month of year
if ($classargs{'datestring'} =~ /^(\d{1,2})\/(\d{2,4})$/gi ) {
$m = $1;
$y = $2;
}
# handle input like '2004' - assuming year
elsif ($classargs{'datestring'} =~ /^\d{2,4}$/gi ) {
$y = $classargs{'datestring'};
}
# handle all other input - requesting days (possibly regardless of
+ the year like '06.08'
# need to reorder according to format and delimiter
else {
if (! $classargs{'format'}) { # assume default format DD/MM/Y
+Y(YY)
($d, $m, $y) = split(/\//, $classargs{'datestring'});
}
else { # parse with provided formatting information
my (%lookup,$first,$second,$third); #%position);
# make sure delimiter becomes '/'
$classargs{'format'} =~ s/[^dmyDMY]+/\//gi;right
# need lower case
$classargs{'format'} = lc ($classargs{'format'});
if (length($classargs{'format'}) >5) {
$classargs{'format'} =~ s/([dmyDMY])+/$1/g;
}
($first,$second,$third) = split(/\//, $classargs{'format'}
+);
($lookup{$first}, $lookup{$second}, $lookup{$third})
= split(/\//, $classargs{'datestring'
+});
$d = $lookup{'d'};
$m = $lookup{'m'};
$y = $lookup{'y'};
}
}
if (length($d) == 1) { $d = '0'.$d; }
if (length($m) == 1) { $m = '0'.$m; }
if (length($y) == 2) {
if ($y >= 30) { $y += 1900; }
else { $y += 2000; }
}
# validate date
# NOTE: validate only if $m or $d are available!
unless ($classargs{'validate'} eq '0') {
my $m_index = int($m)-1;
# validate month
if ($m) {
if (($m_index <0) || ($m_index >11)) {
$errstr = "ERROR parsing datestring: not a valid month
+";
}
}
if ($d) {
# validate day
my @days_per_month = (31,28,31,30,31,30,31,31,30,31,30,31)
+;
# check for leap-year
if ( ((($y % 4) == 0) && (($y % 100) != 0))
|| (($y % 400) == 0)
) {
$days_per_month[1] ++;
}
# check day
my $d_integer = int($d);
if ( ($d_integer < 1) || ($d_integer > $days_per_month[$m_
+index]) ) {
$errstr = "ERROR parsing datestring: not a valid day i
+n month $m";
}
}
}
return ($d, $m, $y);
}
If others see assumtions being wrong or any other chance for improvement I'll appreciate that very much as well.
Hope it helps to find a solution to your problem.
RL
update
s/right/write/ | [reply] [d/l] |
Re: Simple Date Validation
by Trihedralguy (Pilgrim) on May 11, 2007 at 16:21 UTC
|
This is what I have so far if ($dated =~ /^(?:[01]\d\/[0-3]\d\/(?:19|20)\d\d)?$/)
{
my ($sec,$min,$hour) = qw( 0 0 12 );
my ($mday, $mon, $year) = split /\//, $dated;
$mon--; # because that's how unix/C treat the month value
eval {
my $dummy = timelocal($sec,$min,$hour,$mday,$mon,$year);
};
if (my $err = $@) {
$passfail = 0;
$errmsg = 'Unable to use date format. Please use "MM/DD/YYYY"
+or MM-DD-YYYY for the Date Delivered Field';
$errtitle = 'Validation Error';
$seconds = 7;
} else {
};
}
else
{
#User entered an invaild date.
$passfail = 0;
$errmsg = 'Unable to use date format. Please use "MM/DD/YYYY" or M
+M-DD-YYYY for the Date Delivered Field';
$errtitle = 'Validation Error';
$seconds = 7;
}
if ($dreceived =~ /^(?:[01]\d\/[0-3]\d\/(?:19|20)\d\d)?$/)
{
}
else
{
#User entered an invaild date.
$passfail = 0;
$errmsg = 'Unable to use date format. Please use "MM/DD/YYYY" or M
+M-DD-YYYY on the Date Received field';
$errtitle = 'Validation Error';
$seconds = 7;
}
But its still validating dates like: 15/10/2007 | [reply] [d/l] |
|
But its still validating dates like: 15/10/2007
Of course, since you
my ($mday, $mon, $year) = split /\//, $dated;
which means you treat $dated as DD/MM/YYYY.
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] [select] |
Re: Simple Date Validation
by Anonymous Monk on May 12, 2007 at 05:15 UTC
|
| [reply] |
Re: Simple Date Validation
by CountZero (Bishop) on May 13, 2007 at 06:24 UTC
|
If you want to validate dates only, the examples and suggestions given above are OK, but if you include the time part as well, things get all of a sudden much more complicated as at some moments second(s) have been added to make up for the Earth's irregular motion (look at it as some sort of Julian/Gregorian jump, but for seconds only) and if you wish to check for that, it gets very complicated!
CountZero "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law
| [reply] |
Re: Simple Date Validation
by chrism01 (Friar) on May 13, 2007 at 23:03 UTC
|
This depends on whether the original date input method is via a (eg Web) GUI, but if it is, it's much easier/safer to just offer drop down lists for date components (using names for mths). This sidesteps the problem and avoids the American vs European issue of MM-DD vs DD-MM.
You can even have mth names lists in the various langs you expect. | [reply] |
Re: Simple Date Validation
by Anonymous Monk on May 13, 2007 at 06:02 UTC
|
perl -MCPAN -e 'install Module::With::Dependencies' | [reply] |