tags:

views:

111

answers:

4

strftime(), as per cpan.org:

print strftime($template, @lt);

I just can't figure the right Perl code recipe for this one. It keeps reporting an error where I call strftime():

...
use Date::Format;
...
sub parse_date {
 if ($_[0]) {
  $_[0] =~ /(\d{4})/;
  my $y = $1;
  $_[0] =~ s/\d{4}//;
  $_[0] =~ /(\d\d)\D(\d\d)/;
  return [$2,$1,$y];
  }
 return [7,7,2010];
 }

foreach my $groupnode ($groupnodes->get_nodelist) {
    my $groupname = $xp->find('name/text()', $groupnode);
    my $entrynodes = $xp->find('entry', $groupnode);
    for my $entrynode ($entrynodes->get_nodelist) {
        ...
        my $date_added = parse_date($xp->find('date_added/text()', $entrynode));
        ...
        $groups{$groupname}{$entryname} = {...,'date_added'=>$date_added,...};
        ...
        }
    }
...

my $imday = $maxmonth <= 12 ? 0 : 1;
...

while (my ($groupname, $entries) = each %groups) {
    ...
    while (my ($entryname, $details) = each %$entries) {
        ...
        my $d = @{$details->{'date_added'}};
        $writer->dataElement("creation", strftime($date_template, (0,0,12,@$d[0^$imday],@$d[1^$imday]-1,@$d[2],0,0,0)));
        }
    ...
    }
...

If I use () to pass the required array by strftime(), I get: Type of arg 2 to Date::Format::strftime must be array (not list) at ./blah.pl line 87, near "))"

If I use [] to pass the required array, I get: Type of arg 2 to Date::Format::strftime must be array (not anonymous list ([])) at ./blah.pl line 87, near "])"

How can I pass an array on the fly to a sub in Perl? This can easily be done with PHP, Python, JS, etc. But I just can't figure it with Perl.

EDIT: I reduced the code to these few lines, and I still got the exact same problem:

#!/usr/bin/perl

use warnings;
use strict;
use Date::Format;

my @d = [7,13,2010];
my $imday = 1;
print strftime( q"%Y-%m-%dT12:00:00", (0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0));
A: 

I looked again and I see the real problem in this code:

my $d = @{$details->{'date_added'}};
    $writer->dataElement("creation", strftime($date_template, (0,0,12,@$d[0^$imday],@$d[1^$imday]-1,@$d[2],0,0,0)));

Specifically @{$details->{'date_added'}} is a dereference. But you're assigning it to a scalar variable and you don't need to dereference in the line below it:

my @d = @{$details->{'date_added'}};
    $writer->dataElement("creation", strftime($date_template, (0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0)));

I've created a regular array for your reference @d and just accessed it as a regular array ( $d[ ... ] instead of @$d[ ... ] )

Cfreak
+2  A: 

Normally, it is easy to pass arrays "on-the-fly" to Perl subroutines. But Date::Format::strftime is a special case with a special prototype ($\@;$) that doesn't allow "list" arguments or "list assignment" arguments:

strftime($format, (0,0,12,13,7-1,2010-1900));      # not ok
strftime($format, @a=(0,0,12,13,7-1,2010-1900));   # not ok

The workaround is that you must call strftime with an array variable.

my @time = (0,0,12,13,7-1,2010-1900);   # note: @array = ( ... ), not [ ... ]
strftime($format, @time);
mobrule
Works! Thanks. I wish I could make sense of "($\@;$)"
R. Hill
@R.Hill: see [perldoc perlsub](http://perldoc.perl.org/perlsub.html) for details of function prototype specifications.
Ether
Incidentally, this is the single most hideous use of Perl prototypes I've ever seen. :)
fennec
+4  A: 

Where an array is required and you have an ad hoc list, you need to actually create an array. It doesn't need to be a separate variable, you can do just:

strftime(
    $date_template,
    @{ [0,0,12,$d[0^$imday],$d[1^$imday],$d[2],0,0,0] }
);

I have no clue why Date::Format would subject you to this hideousness and not just expect multiple scalar parameters; seems senseless (and contrary to how other modules implement strftime). Graham Barr usually designs better interfaces than this. Maybe it dates from when prototypes still seemed like a cool idea for general purposes.

ysth
This works too! Better, since it avoids the need of a placeholder variable
R. Hill
+3  A: 

To use a list as an anonymous array for, say, string interpolation, you could write

print "@{[1, 2, 3]}\n";

to get

1 2 3

The same technique provides a workaround to Date::Format::strftime's funky prototype:

print strftime(q"%Y-%m-%dT12:00:00",
               @{[0,0,12,$d[0^$imday],$d[1^$imday]-1,$d[2],0,0,0]});

Output:

1900-24709920-00T12:00:00
Greg Bacon