Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

date format string -> Reg Ex solution

by markjugg (Curate)
on Apr 23, 2003 at 15:10 UTC ( [id://252577]=perlquestion: print w/replies, xml ) Need Help??

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

Hello,

I'm working on Data::FormValidator again. This time I'm working on adding some date validation to the core module. I'd like to make my solution scalable so it's easy to validate dates in other formats besides the ones I prefer myself.

My plan is to parse a string with a regular expression, and then feed it to Date::Calc's check_time() and check_date() functions.

I realize Date::Manip is out there as a solution that will parse most any string, but's such a beast to load.

I'm thinking of an interface like this

valid_date($your_date, 'MM/DD/YYYY'),

What I'm hoping to find is that there is easy way to start with this input and end up with ($mm,$dd,$yyyy), given the possibilities for a variety of different date formats.

As an alternative, we could use a solution that hard codes a few of the popular date formats, so the interface would look more like this:

valid_date($your_date, 'US'),

Thanks for any suggestions!

-mark

Replies are listed 'Best First'.
Re: date format string -> Reg Ex solution
by Koschei (Monk) on Apr 23, 2003 at 22:18 UTC
    If you can handle slightly different syntax, might I recommend Time::Piece->strptime?

    It uses format strings that look much like strftime's formats, but it uses them to parse rather than format.

Re: date format string -> Reg Ex solution
by jaldhar (Vicar) on Apr 23, 2003 at 15:29 UTC

    Hi Mark,

    The DateTime project is working on unifying the chaos of perl date and time modules. You should definitely look into that before coming up with your own ad-hoc solution.

    --
    જલધર

      right ! Murat
Re: date format string -> Reg Ex solution
by dragonchild (Archbishop) on Apr 23, 2003 at 15:15 UTC
    1. Develop Date::Manip::Lite if the original is too big.
    2. Use Regexp::Common
    3. Rebuild the wheel.
    I don't see too many other options ...

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: date format string -> Reg Ex solution
by Corion (Patriarch) on Apr 23, 2003 at 15:16 UTC

    I did a small date parser for very simple in Python, and it has a very simplicistic syntax. The letters Y,M and D are used to designate the numbers which should be extracted. Any other character must appear verbatim in the string, and this is validated against.

    Some examples :
    Date( key, 'YYMMDD' ) # Date must be 6 digits Date( key, 'YY-MM-DD' ) # Date must match /\d\d-\d\d-\d\d/ Date( key, 'DD.MM.YYYY' ) # Date in german format, 4 digit year

    The implementation is very simple: As all strings are of fixed length, I extract the numbers from the left and right offsets of each letter (Y,M,D). After the extraction, I replace Y,M,D by "x", and then check that the string matches the string where the numbers also have been replaced by "x".

    For my purposes (parsing various lists), this has proven good enough, as I don't have to deal with weekdays etc., that is, variable text except for digits.

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
Re: date format string -> Reg Ex solution
by hardburn (Abbot) on Apr 23, 2003 at 15:32 UTC

    There are the new DateTime::Format modules, but I don't see a way they can currently be used to dispatch a given date string to the right module. You could keep a list of regexen that each DateTime::Format module matches against and then send the date string to the first module that matches. You'll want to talk to the people on the datetime@perl.org mailing list first, though.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    Note: All code is untested, unless otherwise stated

Re: date format string -> Reg Ex solution
by Jenda (Abbot) on Apr 23, 2003 at 17:28 UTC
    use strict; sub prepareFormat { my $format = shift(); my ($i, @order) = 0; $format =~ s/([YMDhms]+)(\?)?/ $order[$i++] = substr($1,0,1); '('. +('\d' x length($1))."$2)"/ge; $format = qr/^(?:$format)$/; return [$format, \@order]; } sub parseDate { my ($format, $date) = @_; my @data = ($date =~ $format->[0]) or return; my %result; for(my $i = 0; $i <= $#data; $i++) { $result{$format->[1]->[$i]} ||= $data[$i]; } return map $result{$_}, qw(Y M D h m s); } #my $format = prepareFormat ('YYYY/MM/DD'); #my $format = prepareFormat ('YYYY-DD-MM|YYYY-DD-M|YYYY-D-MM|YYYY-D-M' +); #my $format = prepareFormat ('YYYY-DD?-MM?'); #my $format = prepareFormat ('YYYY-DD?-MM? hh?:mm?'); #my $format = prepareFormat ('YYYY-DD?-MM? hh?:mm?(?::ss?)?'); my $format = prepareFormat ('YYYY-DD?-MM?(?: hh?:mm?(?::ss?)?)?'); while (<>) { chomp; my ($year, $month, $day, $hour, $min, $sec) = parseDate($format, $ +_) or print "\tNot in the right format!\n" and next; print "Year: $year, Month: $month, Day: $day, Hour: $hour, Min: $m +in, Sec: $sec\n"; }

    The prepareFormat() returns a reference to an array containing the regexp and the mapping between the capturing parenthesis and the parts of the date, the parseDate() uses this structure to match the date and extract the data. It then returns (year,month,day,hour,min,sec).

    As you can see from the examples you may accept&parse several different datetime formats at once.

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

      Incredible.

      This is exactly the kind of thing I was looking for. It doesn't handle AM/PM, which important in my case. Below is my hacked version that supports AM/PM by using "pp" to designate this. This creates an exceptional case in the script, because before only numbers were accepted, so instead of just \d, I'm now matching against \dPpAaMm

      # test like this: perl this_script.pl '02/04/2003 06:30 PM' use strict; sub prepareFormat { my $format = shift; # TODO: check that only valid characters appear in the format # The logic should be: for any character A-Z in the format string, # die if it's not one of: Y M D h m s p my ($i, @order) = 0; my $num_chars = 'YMDhms'; $format =~ s/([$num_chars]+)(\?)?/ $order[$i++] = substr($1,0,1); +'('.('\d' x length($1))."$2)"/ge; # use "p" for AM/PM $format =~ s/pp/ $order[$i++] = substr('p',0,1); "(AM|PM)"/e; $format = qr/^(?:$format)$/; return [$format, \@order]; } sub parseDate { my ($format, $date) = @_; my @data = ($date =~ $format->[0]) or return; my %result; for(my $i = 0; $i <= $#data; $i++) { $result{$format->[1]->[$i]} ||= $data[$i]; } $result{h} += 12 if ($result{p} eq 'PM' and $result{h} != 12); $result{h} = 0 if ($result{p} eq 'AM' and $result{h} == 12); return map $result{$_}, qw(Y M D h m s); } #my $format = prepareFormat ('YYYY/MM/DD'); #my $format = prepareFormat ('YYYY-DD-MM|YYYY-DD-M|YYYY-D-MM|YYYY-D-M' +); #my $format = prepareFormat ('YYYY-DD?-MM?'); #my $format = prepareFormat ('YYYY-DD?-MM? hh?:mm?'); #my $format = prepareFormat ('YYYY-DD?-MM? hh?:mm?(?::ss?)?'); #my $format = prepareFormat ('YYYY-DD?-MM?(?: hh?:mm?(?::ss?)?)?'); my $format = prepareFormat ('MM/DD/YYYY hh:mm pp'); my ($year, $month, $day, $hour, $min, $sec) = parseDate($format, $ +ARGV[0]) or print "\tNot in the right format!\n" and exit; print "Year: $year, Month: $month, Day: $day, Hour: $hour, Min: $m +in, Sec: $sec\n";

      -mark

        First a tiny nit. substr('p',0,1); is better written as 'p' ;-)

        Next there is a potential problem. Your code works correctly only if the AM/PM is the last thing in the string. This may seem to be an unimportant restriction, but imagine the format was 'YYYY/MM/DD hh:mm pp|DD.MM.YYYY hh:mm pp'. In this case the indexes in @order will be incorrect!

        The solution is to process the 'pp' at the same time as the other characters:

        use strict; sub prepareFormat { my $format = shift; # TODO: check that only valid characters appear in the format # The logic should be: for any character A-Z in the format string, # die if it's not one of: Y M D h m s p my ($i, @order) = 0; my $num_chars = 'YMDhms'; $format =~ s{([$num_chars]+|pp)(\?)?}{ $order[$i++] = substr($1,0,1); if ($1 eq 'pp') { "(AM|PM|am|pm)" } else { '('.('\d' x length($1))."$2)" } }ge; $format = qr/^(?:$format)$/; return [$format, \@order]; } sub parseDate { my ($format, $date) = @_; my @data = ($date =~ $format->[0]) or return; my %result; for(my $i = 0; $i <= $#data; $i++) { $result{$format->[1]->[$i]} ||= $data[$i]; } $result{h} += 12 if (uc($result{p}) eq 'PM' and $result{h} != 12); $result{h} = 0 if (uc($result{p}) eq 'AM' and $result{h} == 12); return map $result{$_}, qw(Y M D h m s); } ...

        Jenda
        Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
           -- Rick Osborne

        Edit by castaway: Closed small tag in signature

Re: date format string -> Reg Ex solution
by adamk (Chaplain) on Apr 24, 2003 at 08:58 UTC
    Perhaps you could look at writing something like Validate::Net, which is a slowly growing module for validate internet related stuff...

    Or for another idea, do some very basic date parsing yourself, with a couple of simple regexs, and if it doesn't fit your format, THEN load Date::Manip using something like Class::Autouse.

    Just some suggestions

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://252577]
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-26 07:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found