Home > Mobile >  Perl :: Translate String "yyyy.mm.dd.hh.mm.ss" into DateTime object?
Perl :: Translate String "yyyy.mm.dd.hh.mm.ss" into DateTime object?

Time:09-20

newbie Perl guy here. I have a Perl script (v5.26.1) that parses a logfile. For reasons beyond my control, the format of each date in the log is this:

"yyyy.mm.dd.hh.mm.ss"

e.g.:

"2022.09.19.16.35.00"

Note that year is always four digits, while month, day, hour, minute, and second are always two digits. (e.g. Sept is 09)

I need to convert this string into a DateTime object for comparison purposes. I thought this exercise would be a breeze, but five hours and a lot of fruitless Google searches later, and I'm no-where close. Here's my first attempt:

#!/usr/bin/perl
use warnings;
use strict;

sub transStrToDTime
{
    # Example format of a date:  "2022.09.19.16.35.00"
    my $str = @_;           # Only one input argument

    use DateTime qw( );
    my ($y,$m,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/
        or die;
    my $dt = DateTime->new(
       year      => $y,
       month     => $m,
       day       => $d,
       hour      => $h,
       minute    => $m,
       second    => $s,
       time_zone => 'local'
    );
    printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
}

my $str="2022.09.19.16.35.00";
transStrToDTime($str);

Code has a syntax error:

me@ubuntu:/home/me# ./toyPerl.pl
Died at ./toyPerl.pl line 16.
me@ubuntu:/home/me#

Where line 16 is:

    my ($y,$m,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/

Ugggggggghhhhhhhhh... After reading up on this topic and becoming more confused, I decided to do the conversion manually. I figured that all I had to do is:

  • split() the string on .
  • Build a new DateTime object with the string tokens as input:

Here's that attempt:

#!/usr/bin/perl
use warnings;
use strict;

my $str="2022.09.19.16.35.00";
my @spl = split('.', $line);
#Lets look at the tokens before we build the DateTime object:
for(my $i = 0; $i <= $#spl; $i  ){
    print("$i)  $spl[$i] \n");
}

Output:

me@ubuntu:/home/me# ./toyPerl.pl
me@ubuntu:/home/me#

No output... meaning that split() split "2022.09.19.16.35.00" into zero tokens? Is $str not a string, then? So what data type could it be?

#!/usr/bin/perl
use warnings;
use strict;

my $str="2022.09.19.16.35.00";
printf("Verifying that \$str is a string:\n");
printf("---> ${ref($str)}\n");

Output:

me@ubuntu:/home/me# ./toyPerl.pl
Verifying that "2022.09.19.16.35.00" is a string:
Can't use string ("") as a SCALAR ref while "strict refs" in use at ./2222toyPerl.pl line 7.
me@ubuntu:/home/me#

Where line 7 is this line:

printf("---> ${ref($str)}\n");

I'm so confused. The error message seems to say that my string isn't a scalar. But I thought strings were scalar in Perl? ("A scalar is most often either a number or a string.") And why is the string reduced to an empty string ("") in line 7?

Oh man. This post represents half a day's work. Can anyone spot my syntax error in the first method? And why can't I split() string "2022.09.19.16.35.00"? Is it not a string or something? Thank you.

CodePudding user response:

When you put

my $str = @_;

It means you put the array in scalar context, and in scalar context, arrays return their size. What you want is to use list context:

my ($str) = @_;

Or better yet, use the idiomatic shift:

my $str = shift;   # automatically uses @_ inside a subroutine

You are also using two $m variables. Change one of them to something else. Revised code works as expected:

use strict;
use warnings;

sub transStrToDTime {
    my ($str) = @_;           # Only one input argument
    use DateTime qw( );
    my ($y,$M,$d,$h,$m,$s) = $str =~ /^([0-9]{4}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2}).([0-9]{2})\z/
        or die;
    my $dt = DateTime->new(
       year      => $y,
       month     => $M,
       day       => $d,
       hour      => $h,
       minute    => $m,
       second    => $s,
       time_zone => 'local'
    );
    printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
}

my $str="2022.09.19.16.35.00";
transStrToDTime($str);

Output:

=-=-=-=-=-=-=->>> "2022-09-19T16:35:00"

The thing you call a syntax error in the first method is actually not a syntax error. Just your code that says die if the regex match fails. Note that die without a message is not terribly informative. You might want to put something more useful there.

In your second case, when you split on '.', you are using a wildcard character ., not a literal period. Hence the entire string is consumed and nothing is left. You might try split /\./ instead.

In the third case, I don't know what you are doing here: ${ref($str)} You are trying to dereference the return value from ref? The return value from ref is just a string, e.g. ARRAY or SCALAR. That's not how you use ref. If you want to know what a variable contains, instead use Data::Dumper:

use Data::Dumper;
print Dumper $str;
# will print $VAR1 = 1; in your first program ($str is the size of the array @_)

Also, this line:

    printf("=-=-=-=-=-=-=->>> \"$dt\"\n");
  1. Don't use printf when you can use print. printf has a special usage.
  2. Instead of escaping quotes inside the string, consider alternatives such as:
print " '$dt' \n";
print qq( "$dt" \n);
use feature 'say';
say " '$dt' ";

CodePudding user response:

In addition to everything TLP said, you can use

use DateTime::Format::Strptime qw( );

my $format = DateTime::Format::Strptime->new(
   pattern   => '%Y.%m.%d.%H.%M.%S',
   strict    => 1,
   time_zone => 'local',
   on_error  => 'croak',
));

my $dt = $format->parse_datetime( $str );
  • Related