skip to Main Content

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.

2

Answers


  1. 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:
    printf " '%s' n", $dt;   # using printf correctly
    print " '$dt' n";        # more simple
    print qq( "$dt" n);      # different delimiter
    use feature 'say';
    say " '$dt' ";            # say includes newline at the end
    
    Login or Signup to reply.
  2. 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 );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search