in reply to Convert seconds into a formatted ddd:hh:mm:ss string

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.