http://www.perlmonks.org?node_id=1083793

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

Hello,

I have inherited some code that appends datestamps to file names. It is generating different results in perl 5.8.8 and perl 5.10.1 and I need some help trying to understand why.

Here's a snippet that shows the behavior:
#!/usr/bin/perl my @tm=localtime(time); $year=@tm[5]+1900; $mon=@tm[4]+1; $dom=@tm[3]; if ($dom<10) { $dom="0$dom"; } $dow=@tm[6]; $dstamp=sprintf("%04.0d%02.0d%02.0d",$year,$mon,$dom); $hour=@tm[2]; $min=@tm[1]; $sec=@tm[0]; if ($hour<10) { $hour="0$hour"; } if ($min<10) { $min="0$min"; } if ($sec<10) { $sec="0$sec"; } $dstime=$tm[8]; # is it currently daylight savings time. $tstamp=sprintf("%02.0d%02.0d%02.0d",$hour,$min,$sec); print "year is $year\n"; print "mon is $mon\n"; print "dom is $dom\n"; print "hour is $hour\n"; print "min is $min\n"; print "sec is $sec\n"; print "tstamp is $tstamp\n"; print "dstamp is $dstamp\n";
Here's the output running perl 5.8.8:
year is 2014 mon is 4 dom is 11 hour is 16 min is 18 sec is 06 tstamp is 161806 dstamp is 20140411
And perl 5.10.1
year is 2014 mon is 4 dom is 11 hour is 16 min is 20 sec is 08 tstamp is 1620 8 dstamp is 2014 411
The perl 5.10.1 output is putting spaces instead of zeros when the individual components of the timestamp are less than ten (like with the seconds field of the tstamp value).

Due to legacy systems, this code needs to continue to run on both versions of perl.

Any help understanding what caused the output to change or how to get the same output from both perl versions would be appreciated.

Thank you,
Tim

Replies are listed 'Best First'.
Re: output format changed perl5.8 --> perl 5.10?
by toolic (Bishop) on Apr 25, 2014 at 14:49 UTC
    I think the minimal changes to your code to use a 0 instead of a space are shown by the 2 lines denoted with "CHANGED":
    #!/usr/bin/perl my @tm=localtime(time); $year=@tm[5]+1900; $mon=@tm[4]+1; $dom=@tm[3]; if ($dom<10) { $dom="0$dom"; } $dow=@tm[6]; $dstamp=sprintf("%04d%02d%02d",$year,$mon,$dom); # CHANGED $hour=@tm[2]; $min=@tm[1]; $sec=@tm[0]; if ($hour<10) { $hour="0$hour"; } if ($min<10) { $min="0$min"; } if ($sec<10) { $sec="0$sec"; } $dstime=$tm[8]; # is it currently daylight savings time. $tstamp=sprintf("%02d%02d%02d",$hour,$min,$sec); # CHANGED print "year is $year\n"; print "mon is $mon\n"; print "dom is $dom\n"; print "hour is $hour\n"; print "min is $min\n"; print "sec is $sec\n"; print "tstamp is $tstamp\n"; print "dstamp is $dstamp\n";

    The code could use a lot of cleaning up.

Re: output format changed perl5.8 --> perl 5.10?
by mr_mischief (Monsignor) on Apr 25, 2014 at 15:43 UTC

    Precision in an integer makes not so much sense. It gets repurposed for a slightly different meaning. From the perldoc on sprintf one can find this:

    For integer conversions, specifying a precision imp +lies that the output of the number itself should be zero +-padded to this width, where the 0 flag is ignored: printf '<%.6d>', 1; # prints "<000001>" printf '<%+.6d>', 1; # prints "<+000001>" printf '<%-10.6d>', 1; # prints "<000001 >" printf '<%10.6d>', 1; # prints "< 000001>" printf '<%010.6d>', 1; # prints "< 000001>" printf '<%+10.6d>', 1; # prints "< +000001>" printf '<%.6x>', 1; # prints "<000001>" printf '<%#.6x>', 1; # prints "<0x000001>" printf '<%-10.6x>', 1; # prints "<000001 >" printf '<%10.6x>', 1; # prints "< 000001>" printf '<%010.6x>', 1; # prints "< 000001>" printf '<%#10.6x>', 1; # prints "< 0x000001>"

    Either drop the precision portion or set it to 2. There are other options which provide the proper output but maybe shouldn't.

    printf "<%.2d>\n", 5; printf "<%02.2d>\n", 4; printf "<%02d>\n", 3; printf "<%02.0d>\n", 2; printf "<%0.2d>\n", 1; printf "<%1.2d>\n", 0;
    prints:
    <05> <04> <03> < 2> <01> <00>

    The first three options there make sense to me, although the first in an ugly sort of way. The fourth, as you're finding out, is having the precision portion ignored as per the documentation. The fifth and sixth I would consider undocumented behavior and subject to change.

    Update:
    In the Perl 5.8.8 documentation, it says something similar, and I consider the behavior of padding to 2 in the %02.0d case to be a bug in that version.:

    For integer conversions, specifying a precision imp +lies that the output of the number itself should be zero-padded to th +is width: printf ’<%.6x>’, 1; # prints "<000001>" printf ’<%#.6x>’, 1; # prints "<0x000001>" printf ’<%-10.6x>’, 1; # prints "<000001 >"
Re: output format changed perl5.8 --> perl 5.10?
by hippo (Bishop) on Apr 25, 2014 at 14:52 UTC

    Call me old-fashioned, but I always use %i for integers. eg:

    perl -e 'printf "%2.2i\n", 4;'

    which seems to work fine on 5.8.8 and 5.10.1. HTH.

Re: output format changed perl5.8 --> perl 5.10?
by fishmonger (Chaplain) on Apr 25, 2014 at 16:33 UTC

    Do you really need each component of the timestamp in individual vars? Are they being used elsewhere or are they just being used to build up your tstamp and dstamp strings?

    IMO, it would be much cleaner and easier to use the strftime function from the POSIX module.

    #!/usr/bin/perl use strict; use warnings; use POSIX qw(strftime); my $dstamp = strftime("%Y%m%d", localtime(time)); my $tstamp = strftime("%H%M%S", localtime(time)); print "tstamp is $tstamp\n"; print "dstamp is $dstamp\n";

    Which outputs this format:

    tstamp is 092141
    dstamp is 20140425
    
Re: output format changed perl5.8 --> perl 5.10?
by Laurent_R (Canon) on Apr 25, 2014 at 17:31 UTC
    Or you could use this, which I just used yesterday at work where I needed a timestamp to the second for file names:
    my @tm=localtime(time); $tm[5] += 1900; $tm[4] ++; my $timestamp = sprintf "%04d%02d%02d%02d%02d%02d", reverse @tm[0..5]; print $timestamp, "\n"; # prints something like 20140425192252
    Note that I have Perl 5.8 at work, it worked, and I just tried it here at home on 5.14 and it works similarly.

    Update: BTW, just for fun, even this works correctly:

    my $timestamp = sprintf "%04d" . "%02d" x 5, @tm[ reverse 0..5];
    Even though I know it and have known it for quite a bit of time, I am still sometimes amazed by how easy (and fun) it is to write creative Perl code, or improbable code that still works correctly. I have used several dozens programming languages in my life, some of which I really liked, but it is different with Perl: I am really in love with it.

Re: output format changed perl5.8 --> perl 5.10?
by Anonymous Monk on Apr 25, 2014 at 17:26 UTC

    Thank you all for the feedback and solutions on better methods to handle this. I appreciate the info.

    Tim