Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Assigning a parsed date to a variable

by Anonymous Monk
on Mar 30, 2017 at 18:34 UTC ( #1186530=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks!

I am just trying to find a less code way to do this parsing using regular expressions.
In the first one I am getting a "1", why it cant be done this way.
It works on the second code, unless there is a more efficient way of doing it.

#!/usr/bin/perl use strict; use warnings; my $adate = "2017-01-29 11:30:07.370"; # more direct way, but returning a "1". my $a_new_datetime = ( $adate =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\ +d{2})(.*)/$2-$3-$1 $4:$5/ ); print "\n 1 - $a_new_datetime\n\n"; my $new_datetime = $adate; $new_datetime =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.*)/$2-$3- +$1 $4:$5/; print " 2 - $new_datetime\n\n";

Thanks for looking at it!

Replies are listed 'Best First'.
Re: Assigning a parsed date to a variable
by kennethk (Abbot) on Mar 30, 2017 at 18:46 UTC
    A substitution regular expression normally changes the string to edit and returns the number of changes. Thus you actually changed your target string for your second case. To get what you meant to get, you would use the code
    #!/usr/bin/perl use strict; use warnings; my $adate = "2017-01-29 11:30:07.370"; # more direct way, but returning a "1". my $a_new_datetime = $adate; $a_new_datetime =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.*)/$2-$ +3-$1 $4:$5/; print "\n 1 - $a_new_datetime\n\n"; my $new_datetime = $adate; $new_datetime =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.*)/$2-$3- +$1 $4:$5/; print " 2 - $new_datetime\n\n";
    Alternatively, you can use the r modifier (s/_PATTERN_/_REPLACEMENT_/msixpodualngcer in perlop)to shift the behavior to return a value without editing the original string:
    #!/usr/bin/perl use strict; use warnings; my $adate = "2017-01-29 11:30:07.370"; # more direct way, but returning a "1". ; printf "\n 1 - %s\n\n", $adate =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):( +\d{2})(.*)/$2-$3-$1 $4:$5/r; printf " 2 - %s\n\n", $adate =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d +{2})(.*)/$2-$3-$1 $4:$5/r;

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Assigning a parsed date to a variable
by AnomalousMonk (Bishop) on Mar 30, 2017 at 19:30 UTC

    The  s/// substitution  /r modifier is only available from Perl version 5.14 onwards. kennethk has shown how to use the  /r modifier, but if you have a Perl version prior to 5.14 and wish to preserve the original string, use method B below:

    c:\@Work\Perl>perl -wMstrict -le "my $adate = '2017-01-29 11:30:07.370'; ;; my $A_new = $adate =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.*)/ +$2-$3-$1 $4:$5/r; print qq{A: new '$A_new' old '$adate'}; ;; (my $B_new = $adate) =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.* +)/$2-$3-$1 $4:$5/; print qq{B: new '$B_new' old '$adate'}; " A: new '01-29-2017 11:30' old '2017-01-29 11:30:07.370' B: new '01-29-2017 11:30' old '2017-01-29 11:30:07.370'
    In both cases, the original string, $adate in this case, is left unchanged. If you do not mind changing this variable, just use a normal  $string =~ s/// substitution.


    Give a man a fish:  <%-{-{-{-<

      This line was what I was trying to remember:
      (my $B_new = $adate) =~ s/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2})(.*)/$2-$3-$1 $4:$5/;
      Thank you all!
        The difference is context. In scalar context, a regex usually returns the number of matches or the number of substitutions, which is not what you want here. Adding parentheses creates a list context in which a regex will usually return something closer to what you want here.
Re: Assigning a parsed date to a variable
by AppleFritter (Vicar) on Mar 30, 2017 at 21:38 UTC

    Have you considered turning to CPAN instead of rolling your own regular expressions to parse dates/times? My immediate instinct was to reach for e.g. Time::ParseDate, or one of the DateTime::Format::* family.

    DateTime itself should then be able to format the result in any way you want. For instance:

    #!/usr/bin/perl use Modern::Perl '2015'; use DateTime; use Time::ParseDate; my $adate = "2017-01-29 11:30:07.370"; my $epoch_seconds = parsedate($adate, ZONE => "UTC"); my $dt = DateTime->from_epoch(epoch => $epoch_seconds); say $dt->strftime("%m-%d-%Y %H:%M");
Re: Assigning a parsed date to a variable
by 1nickt (Abbot) on Mar 30, 2017 at 21:47 UTC

    Hi, you said:

    unless there is a more efficient way of doing it

    Unless you are working on your regexp-fu, I would suggest not using a regexp to parse dates. Efficiency extends to future readability of your code.

    Just use DateTime::Format::Strptime to make a parser with the format you want and eval to see if the string parses into a DateTime object.

    Here's an SSCCE that demonstrates:

    use strict; use warnings; use feature qw/ say state /; use DateTime::Format::Strptime; for ( <DATA> ) { chomp; say sprintf '%-24s: %s', $_, validate( $_ ) ? 'OK' : 'Not OK'; } sub validate { state $parser = DateTime::Format::Strptime->new( pattern => '%F %T.%3N', on_error => 'croak', ); return eval { $parser->parse_datetime( @_ ); 1 }; } __DATA__ 2017-01-29 11:30:07.370 2017-01-29 11:30:07.000 2017-01-32 11:30:07.370 2017-01-29 11:30:07 foo bar
    Edit: Or, declare the parser without the on_error attribute, and then you can use the DateTime object, if you can parse a date from your string. Presumably you want to use the date after you've found it.
    my $str = '2017-01-29 11:30:07.370'; $parser = DateTime::Format::Strptime->new( pattern => '%F %T.%3N' ); if ( my $dt = $parser->parse_datetime( $str ) ) { say "Interestingly, $str falls on a " . $dt->day_name; } else { say "$str is not a valid date"; }

    Hope this helps!


    The way forward always starts with a minimal test.

      TIMTOWTDI, of course ;-)

      use Date::Parse; my $adate = "2017-01-29 11:30:07.370" my $new_datetime = str2time $adate; print $new_datetime,$/; __END__ 1485685807.37
      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2021-12-01 10:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    R or B?



    Results (3 votes). Check out past polls.

    Notices?