http://www.perlmonks.org?node_id=954253

madhum21 has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Am Madhu and am new to Perl programming. I was trying to write a simple script to validate valid date. Though it works for some input data, it again fails for some data as below.

#!/usr/bin/perl print ("Enter the date in dd-mm-yy format : ") ; $date = <STDIN> ; chomp ($date) ; while ($date ne "") { if ($date =~ /[1-31]-[1-12]-[1-99]/) { print("You have entered valid data \n") ; } else { print ("You have entered invalid data \n") ; } $date = <STDIN> ; chomp($date) ; }

The above script works fine for input data like 2-2-2 but fails for 3-3-3. Please let me understand, if I am doing something wrong here. Thanks Madhu

Replies are listed 'Best First'.
Re: Script to validate date fails
by moritz (Cardinal) on Feb 16, 2012 at 14:59 UTC

    You need to understand that [...] in a regex is a character class, which matches just a single character. If you write [1-31], it can match the digits 1, 2 or 3, but only one of them. See perlretut for details.

    The best thing to do is to first validate the syntax, with something like /\d\d?-\d\d?\d\d?/, and then check the ranges in perl code later.

Re: Script to validate date fails
by runrig (Abbot) on Feb 16, 2012 at 15:09 UTC
    Besides the error in your regex (already pointed out), validating dates would be better done by a date library, like DateTime, Time::Piece, or Time::Local.
Re: Script to validate date fails
by Your Mother (Archbishop) on Feb 16, 2012 at 15:46 UTC

    Adding to what runrig said, Date::Calc has the fastest date validation.

    use strict; use warnings; use Date::Calc "check_date"; print "Enter the date in yyyy-mm-dd format : "; # More "native" format +. while ( my $date = <STDIN> ) { last if $date =~ /^$/; my @date = $date =~ /(\d+)/g; # Allows any separator and mushy inp +ut. if ( eval { check_date( @date ) } ) { print "You have entered valid data\n"; } else { print "You have entered invalid data\n"; } print "Enter the date in yyyy-mm-dd format : "; } __END__ Enter the date in yyyy-mm-dd format : ohai You have entered invalid data Enter the date in yyyy-mm-dd format : 2012-2-29 You have entered valid data Enter the date in yyyy-mm-dd format : 2011-2-29 You have entered invalid data Enter the date in yyyy-mm-dd format :
      if ( eval { check_date( @date ) } )

      Why eval?

      if ( check_date( @date ) )

      works just as well.

      Update: Oops, no it doesn't! My version dies nastily if fed random alphanumerics or whatever.

Re: Script to validate date fails
by toolic (Bishop) on Feb 16, 2012 at 16:34 UTC
    Basic debugging checklist, Tip #9: Demystify regular expressions by installing and using the CPAN module YAPE::Regex::Explain
    The regular expression: (?-imsx:[1-31]-[1-12]-[1-99]) matches as follows: NODE EXPLANATION ---------------------------------------------------------------------- (?-imsx: group, but do not capture (case-sensitive) (with ^ and $ matching normally) (with . not matching \n) (matching whitespace and # normally): ---------------------------------------------------------------------- [1-31] any character of: '1' to '3', '1' ---------------------------------------------------------------------- - '-' ---------------------------------------------------------------------- [1-12] any character of: '1' to '1', '2' ---------------------------------------------------------------------- - '-' ---------------------------------------------------------------------- [1-99] any character of: '1' to '9', '9' ---------------------------------------------------------------------- ) end of grouping ----------------------------------------------------------------------

    BTW, "validate valid date" has a nice ring to it :)

      Thanks all. Thanks for all the help

      BTW, "validate valid date" has a nice ring to it :)

      Good point. If we only need to validate valid dates, then the following should work:

      print("Enter the date in dd-mm-yy format: "); while(<STDIN>) { print("You have entered valid data\n"); }
Re: Script to validate date fails
by brx (Pilgrim) on Feb 16, 2012 at 15:49 UTC
    $date =~ /[1-31]-[1-12]-[1-99]/; doesn't do what you want.

    [...] is used for class of characters : you must list characters one-by-one "abcd" or a range like "a-k" or "0-6" (see http://perldoc.perl.org/perlrecharclass.html => 1) Bracketed Character Classes -- 2) Character Ranges )

    Here is a "DIY" validation :

    $date =~ /(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-(\d?\d)$/

    (0?[1-9]|[12][0-9]|3[01]) means...

    accepts :
    1 to 9 with an optional 0 prefix => 01,02,03...09 and 1,2,3...9
    *OR*
    (1 or 2) before (0 to 9) => 10,11,...,29
    *OR*
    30 or 31
    

    ...but it's not the good way to do it (what about 29-02-01 for example?) It's better to use a module which knows the calendar.

    Last thing : year with 2 digits ? not good ! :)

    My first try :

    #!/usr/bin/perl use DateTime; print ("Enter the date in dd-mm-yyyy format : ") ; $date = <STDIN> ; chomp ($date) ; while ($date ne "") { my ($d,$m,$y) = split '-',$date; warn "/!\\ wrong year format? $y\n" if $y!~/^\d{4}$/ ; if ( eval {DateTime->new(year=> $y,month=> $m, day=> $d)} +) { print("You have entered valid data \n") ; } else { print ("You have entered invalid data \n") ; } $date = <STDIN> ; chomp($date) ; }