Convert seconds to compound duration: Difference between revisions
(Tcl implementation added) |
(→{{header|REXX}}: added a 2nd REXX version.) |
||
Line 230: | Line 230: | ||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
===version 1=== |
|||
<lang rexx>/* REXX --------------------------------------------------------------- |
<lang rexx>/* REXX --------------------------------------------------------------- |
||
* Format seconds into a time string |
* Format seconds into a time string |
||
Line 277: | Line 278: | ||
9 wk, 6 d, 10 hr, 40 min</pre> |
9 wk, 6 d, 10 hr, 40 min</pre> |
||
===version 2=== |
|||
This REXX version can also handle fractional (seconds) as well as values of zero (time units). |
|||
<lang rexx>/*rexx program demonstrates how to convert a number of seconds to bigger units*/ |
|||
parse arg @; if @='' then @=7259 86400 6000000 /*Not specified? Use default*/ |
|||
do j=1 for words(@); /* [↓] process each number in the list*/ |
|||
call convSec word(@,j) /*convert a number to bigger time units*/ |
|||
end /*j*/ |
|||
exit /*stick a fork in it, we're all done. */ |
|||
/*─────────────────────────────────CONVSEC subroutine─────────────────────────*/ |
|||
convSec: parse arg x /*obtain a number from the argument. */ |
|||
w=timeU(60*60*24*7, 'wk' ) /*obtain number of weeks (if any). */ |
|||
d=timeU(60*60*24 , 'd' ) /* " " " days " " */ |
|||
h=timeU(60*60 , 'hr' ) /* " " " hours " " */ |
|||
m=timeU(60 , 'min' ) /* " " " minutes " " */ |
|||
s=timeU(1 , 'sec' ) /* " " " seconds " " */ |
|||
if x\==0 then s=word(s 0,1)+x 'sec' /*handle fractional (residual) seconds.*/ |
|||
z=strip(space(w d h m s),,','); if z=='' then z=0 'sec' /*handle zero sec.*/ |
|||
say right(arg(1), 20) 'seconds: ' z |
|||
return |
|||
/*─────────────────────────────────TIMEU subroutine───────────────────────────*/ |
|||
timeU: parse arg u,$; _=x%u; if _==0 then return ''; x=x-_*u; return _ $','</lang> |
|||
'''output''' when using the default inputs: |
|||
<pre> |
|||
7259 seconds: 2 hr, 59 sec |
|||
86400 seconds: 1 d |
|||
6000000 seconds: 9 wk, 6 d, 10 hr, 40 min |
|||
</pre> |
|||
'''output''' when using the inputs: 1800.7 123.50 123.00 0.00 |
|||
<pre> |
|||
1800.7 seconds: 30 min, 0.7 sec |
|||
123.50 seconds: 2 min, 3.50 sec |
|||
123.00 seconds: 2 min, 3 sec |
|||
0.00 seconds: 0 sec |
|||
</pre> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
Revision as of 10:26, 20 June 2015
Write a function or program which
- takes a positive integer representing a duration in seconds as input (e.g.
100
), and - returns a string which shows the same duration decomposed into weeks, days, hours, minutes, and seconds as detailed below (e.g. "
1 min, 40 sec
").
Demonstrate that it passes the following three test-cases:
Test Cases
input number | output string |
---|---|
7259 | 2 hr, 59 sec
|
86400 | 1 d
|
6000000 | 9 wk, 6 d, 10 hr, 40 min
|
Details
- The following five units should be used:
unit suffix used in output conversion week wk
1 week = 7 days day d
1 day = 24 hours hour hr
1 hour = 60 minutes minute min
1 minutes = 60 seconds second sec
- However, only include quantities with non-zero values in the output (e.g. return "
1 d
" and not "0 wk, 1 d, 0 hr, 0 min, 0 sec
"). - Give larger units precedence over smaller ones as much as possible (e.g. return
2 min, 10 sec
and not1 min, 70 sec
or130 sec
) - Mimic the formatting shown in the test-cases (quantities sorted from largest unit to smallest and separated by comma+space; value and unit of each quantity separated by space).
Batch File
<lang dos>@echo off
- The Main Thing...
for %%d in (7259 86400 6000000) do call :duration %%d exit/b 0
- /The Main Thing.
- The Function...
- duration
set output= set /a "wk=%1/604800,rem=%1%%604800" if %wk% neq 0 set "output= %wk% wk,"
set /a "d=%rem%/86400,rem=%rem%%%86400" if %d% neq 0 set "output=%output% %d% d,"
set /a "hr=%rem%/3600,rem=%rem%%%3600" if %hr% neq 0 set "output=%output% %hr% hr,"
set /a "min=%rem%/60,rem=%rem%%%60" if %min% neq 0 set "output=%output% %min% min,"
if %rem% neq 0 set "output=%output% %rem% sec "
if %1 gtr 0 echo %1 sec = %output:~1,-1% goto :EOF
- /The Function.</lang>
- Output:
7259 sec = 2 hr, 59 sec 86400 sec = 1 d 6000000 sec = 9 wk, 6 d, 10 hr, 40 min
J
Implementation:
<lang J>fmtsecs=:3 :0
seq=. 0 7 24 60 60 #: y }: ;: inv,(0~:seq)#(8!:0 seq),. <;.2'wk,d,hr,min,sec,'
)</lang>
Task examples:
<lang J> fmtsecs 7259 2 hr, 59 sec
fmtsecs 86400
1 d
fmtsecs 6000000
9 wk, 6 d, 10 hr, 40 min</lang>
jq
<lang jq>def seconds_to_time_string:
def nonzero(text): floor | if . > 0 then "\(.) \(text)" else empty end; if . == 0 then "0 sec" else [(./60/60/24/7 | nonzero("wk")), (./60/60/24 % 7 | nonzero("d")), (./60/60 % 24 | nonzero("hr")), (./60 % 60 | nonzero("min")), (. % 60 | nonzero("sec"))] | join(", ") end;</lang>
Examples': <lang jq>0, 7259, 86400, 6000000 | "\(.): \(seconds_to_time_string)"</lang>
- Output:
<lang sh>$ jq -r -n -f Convert_seconds_to_compound_duration.jq 0: 0 sec 7259: 2 hr, 59 sec 86400: 1 d 6000000: 9 wk, 6 d, 10 hr, 40 min</lang>
Perl
<lang perl>sub compound_duration {
my $sec = shift; no warnings 'numeric'; return join ', ', grep { $_ > 0 } int($sec/60/60/24/7) . " wk", int($sec/60/60/24) % 7 . " d", int($sec/60/60) % 24 . " hr", int($sec/60) % 60 . " min", int($sec) % 60 . " sec";
}</lang>
Demonstration: <lang perl>for (7259, 86400, 6000000) {
printf "%7d sec = %s\n", $_, compound_duration($_)
}</lang>
- Output:
7259 sec = 2 hr, 59 sec 86400 sec = 1 d 6000000 sec = 9 wk, 6 d, 10 hr, 40 min
Perl 6
The built-in polymod
method (which is a generalization of the divmod
function known from other languages), is a perfect match for a task like this:
<lang perl6>sub compound-duration ($seconds) {
($seconds.polymod(60, 60, 24, 7) Z <sec min hr d wk>)\ .grep(*[0]).reverse.join(", ")
}</lang>
Demonstration: <lang perl6>for 7259, 86400, 6000000 {
say "{.fmt: '%7d'} sec = {compound-duration $_}";
}</lang>
- Output:
7259 sec = 2 hr, 59 sec 86400 sec = 1 d 6000000 sec = 9 wk, 6 d, 10 hr, 40 min
Python
Python: Procedural
<lang python>>>> def duration(seconds): t= [] for dm in (60, 60, 24, 7): seconds, m = divmod(seconds, dm) t.append(m) t.append(seconds) return ', '.join('%d %s' % (num, unit) for num, unit in zip(t[::-1], 'wk d hr min sec'.split()) if num)
>>> for seconds in [7259, 86400, 6000000]: print("%7d sec = %s" % (seconds, duration(seconds)))
7259 sec = 2 hr, 59 sec 86400 sec = 1 d
6000000 sec = 9 wk, 6 d, 10 hr, 40 min >>> </lang>
Python: Functional
<lang python>>>> def duration(seconds, _maxweeks=99999999999):
return ', '.join('%d %s' % (num, unit)
for num, unit in zip([(seconds // d) % m for d, m in ((604800, _maxweeks),
(86400, 7), (3600, 24), (60, 60), (1, 60))],
['wk', 'd', 'hr', 'min', 'sec']) if num)
>>> for seconds in [7259, 86400, 6000000]: print("%7d sec = %s" % (seconds, duration(seconds)))
7259 sec = 2 hr, 59 sec 86400 sec = 1 d
6000000 sec = 9 wk, 6 d, 10 hr, 40 min >>> </lang>
REXX
version 1
<lang rexx>/* REXX ---------------------------------------------------------------
- Format seconds into a time string
- --------------------------------------------------------------------*/
Call test 7259 ,'2 hr, 59 sec' Call test 86400 ,'1 d' Call test 6000000 ,'9 wk, 6 d, 10 hr, 40 min' Exit
test:
Parse arg secs,xres res=sec2ct(secs) Say res If res<>xres Then Say '**ERROR**' Return
sec2ct: Parse Arg s /* m=s%60; s=s//60 h=m%60; m=m//60 d=h%24; h=h//24 w=d%7; d=d//7
- /
Parse Value split(s,60) with m s Parse Value split(m,60) with h m Parse Value split(h,24) with d h Parse Value split(d, 7) with w d ol= If w>0 Then ol=ol w 'wk,' If d>0 Then ol=ol d 'd,' If h>0 Then ol=ol h 'hr,' If m>0 Then ol=ol m 'min,' If s>0 Then ol=ol s 'sec' ol=strip(ol) ol=strip(ol,,',') Return ol
split: Procedure
Parse Arg what,how a=what%how b=what//how Return a b</lang>
- Output:
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min
version 2
This REXX version can also handle fractional (seconds) as well as values of zero (time units). <lang rexx>/*rexx program demonstrates how to convert a number of seconds to bigger units*/ parse arg @; if @= then @=7259 86400 6000000 /*Not specified? Use default*/
do j=1 for words(@); /* [↓] process each number in the list*/ call convSec word(@,j) /*convert a number to bigger time units*/ end /*j*/
exit /*stick a fork in it, we're all done. */ /*─────────────────────────────────CONVSEC subroutine─────────────────────────*/ convSec: parse arg x /*obtain a number from the argument. */ w=timeU(60*60*24*7, 'wk' ) /*obtain number of weeks (if any). */ d=timeU(60*60*24 , 'd' ) /* " " " days " " */ h=timeU(60*60 , 'hr' ) /* " " " hours " " */ m=timeU(60 , 'min' ) /* " " " minutes " " */ s=timeU(1 , 'sec' ) /* " " " seconds " " */ if x\==0 then s=word(s 0,1)+x 'sec' /*handle fractional (residual) seconds.*/ z=strip(space(w d h m s),,','); if z== then z=0 'sec' /*handle zero sec.*/ say right(arg(1), 20) 'seconds: ' z return /*─────────────────────────────────TIMEU subroutine───────────────────────────*/ timeU: parse arg u,$; _=x%u; if _==0 then return ; x=x-_*u; return _ $','</lang> output when using the default inputs:
7259 seconds: 2 hr, 59 sec 86400 seconds: 1 d 6000000 seconds: 9 wk, 6 d, 10 hr, 40 min
output when using the inputs: 1800.7 123.50 123.00 0.00
1800.7 seconds: 30 min, 0.7 sec 123.50 seconds: 2 min, 3.50 sec 123.00 seconds: 2 min, 3 sec 0.00 seconds: 0 sec
Tcl
The data-driven procedure below can be customised to use different breakpoints, simply by editing the dictionary.
<lang Tcl>proc sec2str {i} {
set factors { sec 60 min 60 hr 24 d 7 wk Inf } set result "" foreach {label max} $factors { if {$i >= $max} { set r [expr {$i % $max}] set i [expr {$i / $max}] if {$r} { lappend result "$r $label" } } else { if {$i > 0} { lappend result "$i $label" } break } } join [lreverse $result] ", "
}
proc check {cmd res} {
set r [uplevel 1 $cmd] if {$r eq $res} { puts "Ok! $cmd \t = $res" } else { puts "ERROR: $cmd = $r \t expected $res" }
}
check {sec2str 7259} {2 hr, 59 sec} check {sec2str 86400} {1 d} check {sec2str 6000000} {9 wk, 6 d, 10 hr, 40 min}</lang>
- Output:
Ok! sec2str 7259 = 2 hr, 59 sec Ok! sec2str 86400 = 1 d Ok! sec2str 6000000 = 9 wk, 6 d, 10 hr, 40 min