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

by husker (Chaplain)
 on Aug 31, 2000 at 00:09 UTC 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;

```
Replies are listed 'Best First'.
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).

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;
}

{

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);
\$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.

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.

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)

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

- tye (but my friends call me "Tye")
Does that me my entry needs "use integer" as well?
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;
}

Create A New User
Node Status?
node history
Node Type: snippet [id://30392]
help
Chatterbox?
 [LanX]: typo sorry meant protlose [LanX]: ;) [LanX]: see protwin situation [QM]: Anything like a qualtoo? [QM]: er, quatloo

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (9)
As of 2017-08-17 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
Who is your favorite scientist and why?

Results (286 votes). Check out past polls.

Notices?