Search code examples
perlmathtimestrftimeseconds

why POSIX qw{strftime}; is giving me a houre on "0" seconds?


i was playing around with POSIX qw{strftime}; to use as uptime for a program and i found something i didn't know why it happend. I don't use time that much, this was the reason i was messing with it (learning to fill "time" :P) the math gives me | uptime: 01:00:00 where i should be 00:00:00 I put in the line $stop_secs = $stop_secs - "3600"; and "fixed" it, but I wonder where that extra hour came from.

i have no clue why, can anyone explain this or why this happens? thanks.

#!/usr/bin/perl
#
use strict;
use warnings;
use POSIX qw{strftime};


my $start_secs = time();
my $start_time = strftime '%H:%M:%S', localtime $start_secs; # all is: "%Y-%m-%d %H:%M:%S"

my $stop_secs = time();
$stop_secs = $stop_secs - "3600";  #<--- this fix the 1h "bug"? 

my $diff_secs = $stop_secs - $start_secs;
my $diff_time = strftime '%H:%M:%S', localtime $diff_secs;

print " 
    | Time started: $start_time
    | uptime:   $diff_time

\n";

Solution

  • localtime($seconds) returns the information about the local date-time corresponding to $seconds seconds past Midnight, Jan 1st, 1970, UTC (1970-01-01T00:00:00Z), ignoring leap seconds.

    So let's say that a machine uses Europe/Paris as its time zone. When it was 0 seconds past 1970-01-01T00:00:00Z, the local time would have been 1970-01-01T01:00:00+01:00. On such a machine, you therefore get 01:00:00.

    #!/usr/bin/perl
    use feature qw( say );
    use POSIX qw( strftime );
    say strftime("%Y-%m-%dT%H:%M:%S%z", localtime(0)) =~ s/(?=..\z)/:/r;
    say strftime("           %H:%M:%S", localtime(0));
    
    $ TZ=Europe/Paris ./a
    1970-01-01T01:00:00+01:00
               01:00:00
    
    $ TZ=America/Montreal ./a
    1969-12-31T19:00:00-05:00
               19:00:00
    

    You shouldn't be using localtime. You could use gmtime to some extent.

    #!/usr/bin/perl
    use feature qw( say );
    use POSIX qw( strftime );
    say strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(0));
    say strftime("           %H:%M:%S", gmtime(0));
    
    $ TZ=Europe/Paris ./b
    1970-01-01T00:00:00Z
               00:00:00
    
    $ TZ=America/Montreal ./b
    1970-01-01T00:00:00Z
               00:00:00
    

    But really, you shouldn't even be using strftime since that's for date-times, not durations. It will work, but only an extent.

    • strftime("%H:%M:%S", gmtime($secs)) will work for durations up to but not including 24*60*60 seconds.
    • strftime("%d:%H:%M:%S", gmtime($secs)) will work for durations up to but not including 31*24*60*60 seconds, if you're ok with representing a day as having 24 hours.