Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

Formatting elapsed time

by grinder (Bishop)
on Sep 06, 2001 at 16:37 UTC ( #110550=snippet: print w/replies, xml ) Need Help??

The following snippet turns a number of seconds to a compact string representing the equivalent amount of seconds in weeks, days, hours, minutes and seconds. This is useful for reporting elapsed time in an easily-graspable human-readable format (just how many weeks or days 123 456 789 seconds anyway)?

Here are some examples


The code has a rather pleasing symmetry.

sub wdhms {
    my( $weeks, $days, $hours, $minutes, $seconds, $sign, $res ) = qw/
+0 0 0 0 0/;

    $seconds = shift;
    $sign    = $seconds == abs $seconds ? '' : '-';
    $seconds = abs $seconds;

    ($seconds, $minutes) = ($seconds % 60, int($seconds / 60)) if $sec
    ($minutes, $hours  ) = ($minutes % 60, int($minutes / 60)) if $min
    ($hours,   $days   ) = ($hours   % 24, int($hours   / 24)) if $hou
    ($days,    $weeks  ) = ($days    %  7, int($days    /  7)) if $day

    $res = sprintf '%ds',     $seconds;
    $res = sprintf "%dm$res", $minutes if $minutes or $hours or $days 
+or $weeks;
    $res = sprintf "%dh$res", $hours   if             $hours or $days 
+or $weeks;
    $res = sprintf "%dd$res", $days    if                       $days 
+or $weeks;
    $res = sprintf "%dw$res", $weeks   if                             
+   $weeks;

    return "$sign$res";
Replies are listed 'Best First'.
Re: Formatting elapsed time
by Hofmator (Curate) on Sep 06, 2001 at 17:34 UTC

    Am I overlooking something or could your sprintf statements be written much simpler like this?

    $res .= sprintf "%dw", $weeks if $weeks; $res .= sprintf "%dd", $days if $days; $res .= sprintf "%dh", $hours if $hours; $res .= sprintf "%dm", $minutes if $minutes; $res .= sprintf '%ds', $seconds;

    Update: That was changed a little bit too much, as grinder correctly remarked ... nevertheless, my main concern were those ugly string interpolations, and I think this works:

    $res .= sprintf "%dw", $weeks if $we +eks; $res .= sprintf "%dd", $days if $days or $we +eks; $res .= sprintf "%dh", $hours if $hours or $days or $we +eks; $res .= sprintf "%dm", $minutes if $minutes or $hours or $days or $we +eks; $res .= sprintf '%ds', $seconds;

    -- Hofmator

      Yes they could, but it would change the behaviour of the routine. For instance, 86401 would produce 1d1s instead of 1d0h0m1s.

      Which may be what you want (ultra-compact!), but it wasn't what I needed.

      update: re your update: you're quite right. Adding the $res into the format string is a bit too clever by half. I completely overlooked .= . It's feedback like this that make Perl Monks such a useful site.

      g r i n d e r
Re: Formatting elapsed time
by m-rau (Scribe) on Feb 10, 2005 at 17:48 UTC
    The following code is a bit more complex. Sorry. But it produced output reflecting the amount of seconds. If something runs short, it says 3 seconds. If something takes longer, it says 1 hour, 3 seconds. If it takes even longer, the script can say 1 year, 3 monhts, 12 weeks, 4 days, 1 hour, 3 minutes, 20 seconds.
    The code is implemented as a runtime tracker.
    #!/usr/bin/perl my $t0; BEGIN { $t0 = time; } END { my $d = time() - $t0; my @int = ( [ 'second', 1 ], [ 'minute', 60 ], [ 'hour', 60*60 ], [ 'day', 60*60*24 ], [ 'week', 60*60*24*7 ], [ 'month', 60*60*24*30.5 ], [ 'year', 60*60*24*30.5*12 ] ); my $i = $#int; my @r; while ( ($i>=0) && ($d) ) { if ($d / $int[$i] -> [1] >= 1) { push @r, sprintf "%d %s%s", $d / $int[$i] -> [1], $int[$i]->[0], ( sprintf "%d", $d / $int[$i] -> [1] ) > 1 ? 's' : ''; } $d %= $int[$i] -> [1]; $i--; } my $runtime = join ", ", @r if @r; warn sprintf "RUNTIME %s\n", $runtime; } my $runTime = rand( 10 ); printf "Runtime is %d\n", $runTime; sleep( $runTime );
      Well, hopefully it takes those 12 weeks and changes that into 2 more months :-)

      As a very minor nit, the definitions of year and month may need to be adjusted depending on just how precise one wants to be. There are several definitions/equivalencies for the exact length of a year located here.

      For example, using the current Sidereal year's approximation as 365.2564, a month is more accurately said to be 365.2564/12 or 30.438 rather than 30.5, and a year based on 30.5 * 12 is 366 days long...


Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: snippet [id://110550]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (1)
As of 2022-09-30 19:48 GMT
Find Nodes?
    Voting Booth?
    I prefer my indexes to start at:

    Results (126 votes). Check out past polls.