Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Convert seconds into a formatted ddd:hh:mm:ss string

by husker (Chaplain)
on Aug 31, 2000 at 00:09 UTC ( #30392=snippet: print w/ replies, xml ) Need Help??

Description: Converts seconds into a formatted ddd:hh:mm:ss string. Nothing tricky, but I needed it. Critique welcome.

##
##  We assume $in_sec is a positive integer number of seconds.
##  Convert this to a 0-trimmed string of ddd:hh:mm:ss, 
##  or "days:hours:minutes:seconds". 
##

    my ($in_sec, $out_str, $out_m, $out_d, $out_h, $out_s);

    $in_sec=pop @ARGV;
    $hold_sec = $in_sec; 

    $out_d = int($in_sec / (3600*24));
    $in_sec = $in_sec % (3600*24);

    $out_h = int ($in_sec / 3600);
    $in_sec = $in_sec % 3600;

    $out_m = int ($in_sec / 60);
    $in_sec = $in_sec % 60;

    $out_s = $in_sec;

    if ($out_d > 0) {
        $out_str = sprintf "%u = %u:%0.2u:%0.2u:%0.2u\n", $hold_sec, $
+out_d, $out_h, $out_m, $out_s;
    } elsif ($out_h > 0) {
        $out_str = sprintf "%u = %0.2u:%0.2u:%0.2u\n", $hold_sec, $out
+_h, $out_m, $out_s;
    } elsif ($out_m > 0) {
        $out_str = sprintf "%u = %0.2u:%0.2u\n", $hold_sec, $out_m, $o
+ut_s;
    } else {
        $out_str = sprintf "%u = :%0.2u\n", $hold_sec, $out_s;
    }

     print $out_str;

Comment on Convert seconds into a formatted ddd:hh:mm:ss string
Download Code
RE: Convert seconds into a formatted ddd:hh:mm:ss string
by Adam (Vicar) on Aug 31, 2000 at 00:34 UTC
    sub Sec2String { use integer; my $sec = shift; return "$sec" if $sec < 60; my $min = $sec / 60, $sec %= 60; $sec = "0$sec" if $sec < 10; return "$min:$sec" if $min < 60; my $hrs = $min / 60, $min %= 60; $min = "0$min" if $min < 10; return "$hrs:$min:$sec" if $hrs < 24 my $days = $hrs / 24, $hrs %= 24; $hrs = "0$hrs" if $hrs < 10; return "$days:$hrs:$min:$sec"; }
    Although personally I would not bother with the first three return statements.
RE: Convert seconds into a formatted ddd:hh:mm:ss string
by BlaisePascal (Monk) on Aug 31, 2000 at 01:35 UTC
    my entry..
    sub sec2string { my $sec = shift; my @str; for $d (60, 60, 24) { my $m = $sec % $d; $sec /= $d; $m = $m < 10 ? '0'.$m; unshift @str,$m; } return "$sec:" . join ":",@str; }
RE: Convert seconds into a formatted ddd:hh:mm:ss string
by fundflow (Chaplain) on Aug 31, 2000 at 01:54 UTC
    This should work as well:
    $T=shift; @out=reverse($T%60, ($T/=60) % 60, ($T/=60) % 24, ($T/=24) ); $out=sprintf "%03d:%02d:%02d:%02d", @out; $out=~s/^0+:|00://g; print "$out\n";


    Cheers.
      Your divisions need to be integer divs, and they arn't. Other then that, I like it.

      To make the divs integer you'll need to add the line use integer; as I did. Unfortunately you can't really mix /= and int().

      Update:
      Tye is right, while you are dealing with floats, they won't have any negative effect, and the printf will convert them to ints correctly. So its all good. ++ for fundflow.

        In Perl, printf plus "%d" implies int().

                - tye (but my friends call me "Tye")
        Does that me my entry needs "use integer" as well?
        If you want to be on the (numerically-) safe side, you can do this:
        $s=$t % 60; $t=($t-$s)/60; $m=$t % 60; $t=($t-$m)/60; $h=$t % 24; $d=($t-$h)/24; $out=sprintf "%03d:%02d:%02d:%02d\n", $d,$h,$m,$s; $out=~s/^0+:|00://g;

        Which is using only integers (assuming $t is integral)
RE: Convert seconds into a formatted ddd:hh:mm:ss string
by ncw (Friar) on Aug 31, 2000 at 14:20 UTC
    You could do this with an amusing (ab)use of gmtime thus :-
    $string = join ":", map { sprintf "%02d", $_ } (gmtime($seconds))[7, +2,1,0];
    Or perhaps slightly more sanely as
    $string = sprintf "%02d:%02d:%02d:%02d", (gmtime($seconds))[7,2,1,0] +;
    If you want to strip the string then run this on it
    $string =~ s/\G00://g;
    This regexp idea came from merlyn via Effective Perl
      Yes yes yes. This is what I was getting at when I said earlier (in the chatterbox) that the code looked "far too regular". ncw, you've done a nice job abstracting the problem.

      Also, the use of sprintf there to add the leading 0 is much more perl-ish than the few odd snippets I've seen in this thread of:

      $var = "0$var" if $var < 10;
      Maybe it's because I first saw that weird-looking code in a Matt Wright script, and it's now tainted me. It scares me to think what other horrors people have now learned from Matt Wright scripts. {grin}

      I think for me the problem is that the non-sprintf solution does not scale well. For example, what if you wanted three digits? Do you add another test?

      $var = $var < 10 ? "00$var" : $var < 100 ? "0$var" : $var;
      No, if I saw code like that in a code review, I'd flag that immediately with "this must be changed before deployment: unmaintainable".

      So, get cozy with sprintf: it solves many problems (including the rounding problem discussed a few days ago).

      -- Randal L. Schwartz, Perl hacker

RE: Convert seconds into a formatted ddd:hh:mm:ss string
by husker (Chaplain) on Sep 01, 2000 at 16:57 UTC
    OK these were all great suggestions ... so great that I implemented them all and compared them with regards to actual output, and also benchmarked them. The results were interesting! First, let me post the code I used, since I had to slightly modify a few of the methods to get closer to the output I was wanting. However, I didn't modify them beyond the scope of their original intent.
    #!/opt/perl5/bin/perl -w use strict; sub best_int { use integer; my $s = shift; return sprintf ":%02d", $s if $s < 60; my $m = $s / 60; $s = $s % 60; return sprintf "%02d:%02d", $m, $s if $m < 60; my $h = $m / 60; $m %= 60; return sprintf "%02d:%02d:%02d", $h, $m, $s if $h < 24; my $d = $h / 24; $h %= 24; return sprintf "%d:%02d:%02d:%02d", $d, $h, $m, $s; } sub best { my $s = shift; return sprintf ":%02d", $s if $s < 60; my $m = $s / 60; $s = $s % 60; return sprintf "%02d:%02d", $m, $s if $m < 60; my $h = $m / 60; $m %= 60; return sprintf "%02d:%02d:%02d", $h, $m, $s if $h < 24; my $d = $h / 24; $h %= 24; return sprintf "%d:%02d:%02d:%02d", $d, $h, $m, $s; } sub husker { my ($out_m, $hold_sec, $out_str, $in_sec, $out_d, $out_h, $out_s); $in_sec=shift; $hold_sec = $in_sec; $out_d = $in_sec / 86400; $in_sec = $in_sec % 86400; $out_h = $in_sec / 3600; $in_sec = $in_sec % 3600; $out_m = $in_sec / 60; $in_sec = $in_sec % 60; $out_s = $in_sec; if ($out_d > 0) { $out_str = sprintf "%0.f:%0.2u:%0.2u:%0.2u", $out_d, $out_h, $ +out_m, $out_s; } elsif ($out_h > 0) { $out_str = sprintf " %0.2u:%0.2u:%0.2u", $out_h, $out_m, $out_ +s; } elsif ($out_m > 0) { $out_str = sprintf "%0.2u:%0.2u", $out_m, $out_s; } else { $out_str = sprintf ":%0.2u", $out_s; } return $out_str; } sub adam { use integer; my $sec = shift; return ":0$sec" if $sec < 10; return ":$sec" if $sec < 60; my $min = $sec / 60, $sec %= 60; $sec = "0$sec" if $sec < 10; return "$min:$sec" if $min < 60; my $hr = $min / 60, $min %= 60; $min = "0$min" if $min < 10; return "$hr:$min:$sec" if $hr < 24; my $day = $hr / 24, $hr %= 24; $hr = "0$hr" if $hr < 10; return "$day:$hr:$min:$sec"; } sub blaise { my $sec = shift; my @str; use integer; for my $d (60, 60, 24) { my $m = $sec % $d; $sec /= $d; $m = '0'.$m if $m < 10; unshift @str, $m; } return "$sec:".join ":",@str; } sub fund { my $t = shift; my @out=reverse ($t%60, ($t/=60)%60, ($t/=60)%24, ($t/=24) ); my $out=sprintf "%03d:%02d:%02d:%02d", @out; $out=~s/^0+:|00://g; return $out; } sub ncw { my $seconds = shift; my $string = join ":", map { sprintf "%02d", $_} (gmtime($seconds) +)[7,2,1,0]; $string=~s/\G00://g; return $string; } my ($s, $i, $x, $m, $a, $r, $f, $b, $n); for $s (1,5,100,10000,1000000,100000000, 3000000000) { $i=best_int($s); $m=husker($s); $a=adam($s); $b=blaise($s); $f=fund ($s); $n=ncw($s); $x=best($s); write(); } format STDOUT_TOP= seconds best_int best husker <P> ------------- ------------- -------------- -------------<P> + -------------- -------------- -------------- -------------- +- . format STDOUT = @############ @>>>>>>>>>>>> @>>>>>>>>>>>>> @>>>>>>>>>>>>> +@>>>>>>>>>>>>> @>>>>>>>>>>>>> @>>>>>>>>>>>>> @>>>>>>>>>>>>>> $s,$i,$x,$m,$a,$b,$f,$n .
    OK there it is. I used "best" and "best_int" as the methods I've decided best fits my needs. So here are the results of running this code:

    Seconds best_int best husker adam Blaise fund ncw
    1 :01 :01

    :01

    :01 0:00:00:01 01 01
    5 :05 :05 :05 :05 0:00:00:05 05 05
    100 01:40 01:40 01:40 1:40 0:00:01:40 01:40 01:40
    10000 02:46:40 02:46:40 02:46:40 2:46:40 0:02:46:40 02:46:40 02:46:40
    1000000 11:13:46:40 11:13:46:40 11:13:46:40 11:13:46:10 11:13:46:10 11:13:46:10 11:13:46:10
    100000000 1157:09:46:40 1157:09:46:40 1157:09:46:40 1157:09:46:40 1157:09:46:40 1157:09:46:40 61:09:46:40
    300000000 :-1294967296 34722:05:20:00

    :4294967280

    :03000000000 -14988:0-1:0-8 34722:05:20:00 352:22:51:44
    As you can see, using "use integer;" causes problems when you overflow the MAXINT for integer math (which on my platform at least is a bit over 2 billion). Up to that point, all the algorithms get the correct answers, except for ncw's, and I think that's because he's "(ab)using gmtime()" as he puts it. :) Whether gmtime() itself is puking, or the subscripts he's using on the array aren't correct for those larger values I leave as an exercise for the reader.

    All the other differences are formatting. The formatting I was aiming for are displayed in "best". Agreeing with merlyn about using sprintf, it looks like that's really the only way to get exactly the formatting I'm wanting. Using regexp's and substitution just doesn't catch it (but then I'm no regexp expert). However, this is minor, because like I said the most important thing is that everyone was getting the right answer until the inputs became very large, and knowing what we know about integers, they fail in ways we expect.

    OK let's look at performance. There are some interesting comments to be made about how these perform I think. To be thorough, I did multiple benchmarks.. The benchmark platform is an HP9000 Model H50, which is really an old dog .. it's a 96-Mhz PA-RISC 7100. Perl is version 5.005_03.

    Benchmark: timing 100000 iterations of adam, best, best_int, blaise, f +und, husker, ncw... TIME converted is 1 seconds... adam: 2 wallclock secs ( 2.52 usr + -0.01 sys = 2.51 CPU) best: 4 wallclock secs ( 3.61 usr + 0.00 sys = 3.61 CPU) best_int: 3 wallclock secs ( 3.26 usr + 0.01 sys = 3.27 CPU) blaise: 22 wallclock secs (20.81 usr + 0.03 sys = 20.84 CPU) fund: 15 wallclock secs (14.84 usr + 0.02 sys = 14.86 CPU) husker: 14 wallclock secs (12.82 usr + 0.01 sys = 12.83 CPU) ncw: 21 wallclock secs (20.94 usr + 0.03 sys = 20.97 CPU) TIME converted is 5 seconds... adam: 3 wallclock secs ( 2.53 usr + 0.00 sys = 2.53 CPU) best: 4 wallclock secs ( 3.61 usr + 0.01 sys = 3.62 CPU) best_int: 3 wallclock secs ( 3.27 usr + 0.01 sys = 3.28 CPU) blaise: 21 wallclock secs (20.74 usr + 0.03 sys = 20.77 CPU) fund: 15 wallclock secs (14.75 usr + 0.02 sys = 14.77 CPU) husker: 18 wallclock secs (12.93 usr + 0.03 sys = 12.96 CPU) ncw: 22 wallclock secs (20.95 usr + 0.03 sys = 20.98 CPU) TIME converted is 100 seconds... adam: 6 wallclock secs ( 5.91 usr + 0.01 sys = 5.92 CPU) best: 7 wallclock secs ( 6.22 usr + 0.02 sys = 6.24 CPU) best_int: 5 wallclock secs ( 5.67 usr + 0.00 sys = 5.67 CPU) blaise: 21 wallclock secs (19.98 usr + 0.01 sys = 19.99 CPU) fund: 15 wallclock secs (14.22 usr + 0.00 sys = 14.22 CPU) husker: 13 wallclock secs (12.97 usr + 0.00 sys = 12.97 CPU) ncw: 22 wallclock secs (20.91 usr + 0.04 sys = 20.95 CPU) TIME converted is 10000 seconds... adam: 9 wallclock secs ( 8.76 usr + 0.02 sys = 8.78 CPU) best: 9 wallclock secs ( 8.64 usr + 0.01 sys = 8.65 CPU) best_int: 8 wallclock secs ( 7.90 usr + 0.01 sys = 7.91 CPU) blaise: 20 wallclock secs (19.19 usr + 0.01 sys = 19.20 CPU) fund: 14 wallclock secs (13.60 usr + 0.02 sys = 13.62 CPU) husker: 13 wallclock secs (13.09 usr + 0.02 sys = 13.11 CPU) ncw: 21 wallclock secs (20.39 usr + 0.03 sys = 20.42 CPU) TIME converted is 1000000 seconds... adam: 13 wallclock secs (12.06 usr + 0.02 sys = 12.08 CPU) best: 11 wallclock secs (10.74 usr + 0.00 sys = 10.74 CPU) best_int: 10 wallclock secs ( 9.87 usr + 0.01 sys = 9.88 CPU) blaise: 19 wallclock secs (18.79 usr + 0.01 sys = 18.80 CPU) fund: 18 wallclock secs (12.27 usr + 0.03 sys = 12.30 CPU) husker: 13 wallclock secs (13.34 usr + 0.01 sys = 13.35 CPU) ncw: 19 wallclock secs (18.63 usr + 0.01 sys = 18.64 CPU) TIME converted is 100000000 seconds... adam: 14 wallclock secs (13.20 usr + 0.02 sys = 13.22 CPU) best: 11 wallclock secs (10.92 usr + 0.02 sys = 10.94 CPU) best_int: 11 wallclock secs (10.05 usr + 0.01 sys = 10.06 CPU) blaise: 19 wallclock secs (19.32 usr + 0.03 sys = 19.35 CPU) fund: 13 wallclock secs (12.24 usr + 0.02 sys = 12.26 CPU) husker: 14 wallclock secs (13.38 usr + 0.02 sys = 13.40 CPU) TIME converted is 3000000000 seconds... best: 11 wallclock secs (10.71 usr + 0.03 sys = 10.74 CPU) fund: 13 wallclock secs (12.07 usr + 0.02 sys = 12.09 CPU)
    What do we notice?

    fundflow's algorithm actually seems to get FASTER for larger numbers. Might that be because the regex substitution at the end has less work to do with a longer output string...?

    What can we learn from these results?

    Some algorithms do the same amount of work regardless of the TIME value, while others quit once they know they have the final answer. While the former algorithms are "purer" from an algorithmic viewpoint, the latter algorithms are more effecient, certainly.

    Integer math is faster than floating point math, with the tradeoff being precision.

    Using sprintf to truncate floating point numbers seems to be less efficient that doing integer math in the first place. Further, using sprintf seems to give you more control than over regexp evaluation, or building your own strings by hand, and doesn't seem to be a big performance hit over those two methods, if it all.

    Anyway, this has been a long post. I hope you enjoyed it. :) Wait until you see my next submission! LOL

    Lastly, thanks to everyone who submitted code. It's a learning experience for me all the time.

Back to Snippets Section

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (5)
As of 2014-09-23 00:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (209 votes), past polls