Convert seconds to compound duration
You are encouraged to solve this task according to the task description, using any language you may know.
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).
Ada
<lang Ada>with Ada.Text_IO;
procedure Convert is
type Time is range 0 .. 10_000*356*20*60*60; -- at most 10_000 years subtype Valid_Duration is Time range 1 .. 10_000*356*20*60*60; type Units is (WK, D, HR, MIN, SEC); package IO renames Ada.Text_IO; Divide_By: constant array(Units) of Time := (1_000*53, 7, 24, 60, 60); Split: array(Units) of Time; No_Comma: Units; X: Time; Test_Cases: array(Positive range <>) of Valid_Duration := (6, 60, 3659, 7_259, 86_400, 6_000_000, 6_001_200, 6_001_230, 600_000_000);
begin
for Test_Case of Test_Cases loop IO.Put(Time'Image(Test_Case) & " SECONDS ="); X := Test_Case;
-- split X up into weeks, days, ..., seconds No_Comma := Units'First; for Unit in reverse Units loop -- Unit = SEC, ..., WK (in that order)
Split(Unit) := X mod Divide_By(Unit); X := X / Divide_By(Unit); if Unit > No_Comma and Split(Unit)>0 then No_Comma := Unit; end if;
end loop;
-- ouput weeks, days, ..., seconds for Unit in Units loop -- Unit = WK, .., SEC (in that order)
if Split(Unit) > 0 then IO.Put(Time'Image(Split(Unit)) & " " & Units'Image(Unit) & (if No_Comma > Unit then "," else "")); end if;
end loop;
IO.New_Line; end loop;
end Convert;</lang>
- Output:
6 SECONDS = 6 SEC 60 SECONDS = 1 MIN 3659 SECONDS = 1 HR, 59 SEC 7259 SECONDS = 2 HR, 59 SEC 86400 SECONDS = 1 D 6000000 SECONDS = 9 WK, 6 D, 10 HR, 40 MIN 6001200 SECONDS = 9 WK, 6 D, 11 HR 6001230 SECONDS = 9 WK, 6 D, 11 HR, 30 SEC 600000000 SECONDS = 992 WK, 10 HR, 40 MIN
AWK
<lang AWK>
- syntax: GAWK -f CONVERT_SECONDS_TO_COMPOUND_DURATION.AWK
BEGIN {
n = split("7259 86400 6000000 0 1 60 3600 604799 604800 694861",arr," ") for (i=1; i<=n; i++) { printf("%9s %s\n",arr[i],howlong(arr[i])) } exit(0)
} function howlong(seconds, n_day,n_hour,n_min,n_sec,n_week,str,x) {
if (seconds >= (x = 60*60*24*7)) { n_week = int(seconds / x) seconds = seconds % x } if (seconds >= (x = 60*60*24)) { n_day = int(seconds / x) seconds = seconds % x } if (seconds >= (x = 60*60)) { n_hour = int(seconds / x) seconds = seconds % x } if (seconds >= (x = 60)) { n_min = int(seconds / x) seconds = seconds % x } n_sec = int(seconds) str = (n_week > 0) ? (str n_week " wk, ") : str str = (n_day > 0) ? (str n_day " d, ") : str str = (n_hour > 0) ? (str n_hour " hr, ") : str str = (n_min > 0) ? (str n_min " min, ") : str str = (n_sec > 0) ? (str n_sec " sec") : str sub(/, $/,"",str) return(str)
} </lang>
Output:
7259 2 hr, 59 sec 86400 1 d 6000000 9 wk, 6 d, 10 hr, 40 min 0 1 1 sec 60 1 min 3600 1 hr 604799 6 d, 23 hr, 59 min, 59 sec 604800 1 wk 694861 1 wk, 1 d, 1 hr, 1 min, 1 sec
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
C
<lang c>
- include <inttypes.h> /* requires c99 */
- include <stdbool.h> /* requires c99 */
- include <stdio.h>
- include <stdlib.h>
- define N_EL 5
uintmax_t sec_to_week(uintmax_t); uintmax_t sec_to_day(uintmax_t); uintmax_t sec_to_hour(uintmax_t); uintmax_t sec_to_min(uintmax_t);
uintmax_t week_to_sec(uintmax_t); uintmax_t day_to_sec(uintmax_t); uintmax_t hour_to_sec(uintmax_t); uintmax_t min_to_sec(uintmax_t);
char *format_sec(uintmax_t);
/* the primary function */
int main(int argc, char *argv[])
{
uintmax_t input; char *a; if(argc<2) { printf("usage: %s #seconds\n", argv[0]); return 1; } input = strtoumax(argv[1],(void *)0, 10 /*base 10*/); if(input<1) { printf("Bad input: %s\n", argv[1]); printf("usage: %s #seconds\n", argv[0]); return 1; } printf("Number entered: %" PRIuMAX "\n", input); a = format_sec(input); printf(a); free(a); return 0;
}
/* note: must free memory
* after using this function */
char *format_sec(uintmax_t input) {
int i; bool first; uintmax_t weeks, days, hours, mins; /*seconds kept in input*/ char *retval; FILE *stream; size_t size; uintmax_t *traverse[N_EL]={&weeks,&days, &hours,&mins,&input}; char *labels[N_EL]={"wk","d","hr","min","sec"};
weeks = sec_to_week(input); input = input - week_to_sec(weeks);
days = sec_to_day(input); input = input - day_to_sec(days);
hours = sec_to_hour(input); input = input - hour_to_sec(hours);
mins = sec_to_min(input); input = input - min_to_sec(mins); /* input now has the remaining seconds */
/* open stream */ stream = open_memstream(&retval,&size); if(stream == 0) { fprintf(stderr,"Unable to allocate memory"); return 0; }
/* populate stream */ first = true; for(i=0;i<N_EL;i++) { if ( *(traverse[i]) != 0 ) { if(!first) { fprintf(stream,", %" PRIuMAX " %s", *(traverse[i]), labels[i]); } else { fprintf(stream,"%" PRIuMAX " %s", *(traverse[i]), labels[i]); } fflush(stream); first=false; } } fprintf(stream,"\n"); fclose(stream); return retval;
}
uintmax_t sec_to_week(uintmax_t seconds) {
return sec_to_day(seconds)/7;
}
uintmax_t sec_to_day(uintmax_t seconds) {
return sec_to_hour(seconds)/24;
}
uintmax_t sec_to_hour(uintmax_t seconds) {
return sec_to_min(seconds)/60;
}
uintmax_t sec_to_min(uintmax_t seconds) {
return seconds/60;
}
uintmax_t week_to_sec(uintmax_t weeks) {
return day_to_sec(weeks*7);
}
uintmax_t day_to_sec(uintmax_t days) {
return hour_to_sec(days*24);
}
uintmax_t hour_to_sec(uintmax_t hours) {
return min_to_sec(hours*60);
}
uintmax_t min_to_sec(uintmax_t minutes) {
return minutes*60;
} </lang>
- Output:
Number entered: 7259 2 hr, 59 sec Number entered: 86400 1 d Number entered: 6000000 9 wk, 6 d, 10 hr, 40 min
C++
<lang cpp>
- include <iostream>
- include <vector>
using entry = std::pair<int, const char*>;
void print(const std::vector<entry>& entries, std::ostream& out = std::cout) {
bool first = true; for(const auto& e: entries) { if(!first) out << ", "; first = false; out << e.first << " " << e.second; } out << '\n';
}
std::vector<entry> convert(int seconds) {
static const entry time_table[] = { {7*24*60*60, "wk"}, {24*60*60, "d"}, {60*60, "hr"}, {60, "min"}, {1, "sec"} }; std::vector<entry> result; for(const auto& e: time_table) { int time = seconds / e.first; if(time != 0) result.emplace_back(time, e.second); seconds %= e.first; } return result;
}
int main() {
std::cout << " 7259 sec is "; print(convert( 7259)); std::cout << " 86400 sec is "; print(convert( 86400)); std::cout << "6000000 sec is "; print(convert(6000000));
}</lang>
- Output:
7259 sec is 2 hr, 59 sec 86400 sec is 1 d 6000000 sec is 9 wk, 6 d, 10 hr, 40 min
Erlang
Function mapaccumr/3 is adapted from here.
Function intercalate/2 is copied from a Tim Fletcher's GitHub repository.
<lang erlang> -module(convert_seconds).
-export([test/0]).
test() -> lists:map(fun convert/1, [7259, 86400, 6000000]), ok.
convert(Seconds) -> io:format( "~7s seconds = ~s\n", [integer_to_list(Seconds), compoundDuration(Seconds)] ).
% Compound duration of t seconds. The argument is assumed to be positive. compoundDuration(Seconds) -> intercalate( ", ", lists:map( fun({D,L}) -> io_lib:format("~p ~s",[D, L]) end, compdurs(Seconds) ) ).
% Time broken down into non-zero durations and their labels. compdurs(T) -> Ds = reduceBy( T, lists:map( fun(Dl) -> element(1,Dl) end, tl(durLabs()) ) ), lists:filter( fun(Dl) -> element(1,Dl) /= 0 end, lists:zip( Ds, lists:map( fun(Dl) -> element(2,Dl) end, durLabs() ) ) ).
% Duration/label pairs. durLabs() -> [ {undefined, "wk"}, {7, "d"}, {24, "hr"}, {60, "min"}, {60, "sec"} ].
reduceBy(N, Xs) -> {N_, Ys} = mapaccumr(fun quotRem/2, N, Xs), [N_ | Ys].
quotRem(X1, X2) -> {X1 div X2, X1 rem X2}.
% ************************************************** % Adapted from http://lpaste.net/edit/47875 % **************************************************
mapaccuml(_,I,[]) -> {I, []}; mapaccuml(F,I,[H|T]) -> {Accum, NH} = F(I,H), {FAccum, NT} = mapaccuml(F,Accum,T), {FAccum, [NH | NT]}.
mapaccumr(_,I,[]) -> {I, []}; mapaccumr(F,I,L) -> {Acc, Ys} = mapaccuml(F,I,lists:reverse(L)), {Acc, lists:reverse(Ys)}.
% **************************************************
% **************************************************
% Copied from https://github.com/tim/erlang-oauth/blob/master/src/oauth.erl
% **************************************************
intercalate(Sep, Xs) ->
lists:concat(intersperse(Sep, Xs)).
intersperse(_, []) ->
[];
intersperse(_, [X]) ->
[X];
intersperse(Sep, [X | Xs]) ->
[X, Sep | intersperse(Sep, Xs)].
% ************************************************** </lang>
Output:
7259 seconds = 2 hr, 59 sec 86400 seconds = 1 d 6000000 seconds = 9 wk, 6 d, 10 hr, 40 min
Haskell
<lang haskell>import Control.Monad (forM_) import Data.List (intercalate, mapAccumR) import System.Environment (getArgs) import Text.Printf (printf) import Text.Read (readMaybe)
reduceBy :: Integral a => a -> [a] -> [a] n `reduceBy` xs = n' : ys where (n', ys) = mapAccumR quotRem n xs
-- Duration/label pairs. durLabs :: [(Integer, String)] durLabs = [(undefined, "wk"), (7, "d"), (24, "hr"), (60, "min"), (60, "sec")]
-- Time broken down into non-zero durations and their labels. compdurs :: Integer -> [(Integer, String)] compdurs t = let ds = t `reduceBy` (map fst $ tail durLabs)
in filter ((/=0) . fst) $ zip ds (map snd durLabs)
-- Compound duration of t seconds. The argument is assumed to be positive. compoundDuration :: Integer -> String compoundDuration = intercalate ", " . map (uncurry $ printf "%d %s") . compdurs
main :: IO () main = do
args <- getArgs forM_ args $ \arg -> case readMaybe arg of Just n -> printf "%7d seconds = %s\n" n (compoundDuration n) Nothing -> putStrLn $ "Invalid number of seconds: " ++ arg</lang>
- Output:
7259 seconds = 2 hr, 59 sec 86400 seconds = 1 d 6000000 seconds = 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>
Mathematica
<lang Mathematica> compoundDuration[x_Integer] :=
StringJoin @@ (Riffle[ ToString /@ ((({Floor[x/604800], Mod[x, 604800]} /. {a_, b_} -> {a, Floor[b/86400], Mod[b, 86400]}) /. {a__, b_} -> {a, Floor[b/3600], Mod[b, 3600]}) /. {a__, b_} -> {a, Floor[b/60], Mod[b, 60]}), {" wk, ", " d, ", " hr, ", " min, ", " sec"}] //. {a___, "0", b_, c___} -> {a, c})
Grid[Table[{n, "secs =",
compoundDuration[n]}, {n, {7259, 86400, 6000000}}], Alignment -> {Left, Baseline}]
</lang>
- Output:
7259 secs = 2 hr, 59 sec 86400 secs = 1 d, 6000000 secs = 9 wk, 6 d, 10 hr, 40 min,
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
PL/I
<lang PL/I> /* Convert seconds to Compound Duration (weeks, days, hours, minutes, seconds). */
cvt: procedure options (main); /* 5 August 2015 */
declare interval float (15); declare (unit, i, q controlled) fixed binary; declare done bit (1) static initial ('0'b); declare name (5) character (4) varying static initial (' wk', ' d', ' hr', ' min', ' sec' );
get (interval); put edit (interval, ' seconds = ') (f(10), a); if interval = 0 then do; put skip list ('0 sec'); stop; end;
do unit = 60, 60, 24, 7; allocate q; q = mod(interval, unit); interval = interval / unit; end; allocate q; q = interval; do i = 1 to 5; if q > 0 then do; if done then put edit (', ') (a); put edit (trim(q), name(i)) (a, a); done = '1'b; end; if i < 5 then free q; end;
end cvt; </lang> Results:
65 seconds = 1 min, 5 sec 3750 seconds = 1 hr, 2 min, 30 sec 1483506 seconds = 2 wk, 3 d, 4 hr, 5 min, 6 sec 60 seconds = 1 min 3604 seconds = 1 hr, 4 sec 100000000 seconds = 165 wk, 2 d, 9 hr, 46 min, 40 sec 987654321 seconds = 1633 wk, 4 hr, 25 min, 21 sec 86400 seconds = 1 d 86403 seconds = 1 d, 3 sec 3 more to come.
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>
Racket
<lang racket>#lang racket/base (require racket/string
racket/list)
(define (seconds->compound-durations s)
(define-values (w d.h.m.s) (for/fold ((prev-q s) (rs (list))) ((Q (in-list (list 60 60 24 7)))) (define-values (q r) (quotient/remainder prev-q Q)) (values q (cons r rs)))) (cons w d.h.m.s))
(define (maybe-suffix v n)
(and (positive? v) (format "~a ~a" v n)))
(define (seconds->compound-duration-string s)
(string-join (filter-map maybe-suffix (seconds->compound-durations s) '("wk" "d" "hr" "min" "sec")) ", "))
(module+ test
(require rackunit) (check-equal? (seconds->compound-durations 7259) (list 0 0 2 0 59)) (check-equal? (seconds->compound-durations 86400) (list 0 1 0 0 0)) (check-equal? (seconds->compound-durations 6000000) (list 9 6 10 40 0)) (check-equal? (seconds->compound-duration-string 7259) "2 hr, 59 sec") (check-equal? (seconds->compound-duration-string 86400) "1 d") (check-equal? (seconds->compound-duration-string 6000000) "9 wk, 6 d, 10 hr, 40 min"))
- Tim Brown 2015-07-21</lang>
- Output:
All tests pass... there is no output.
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' Call test 123.50 ,'2 min, 3.5 sec' Call test 123.00 ,'2 min, 3 sec' Call test 0.00 ,'0 sec' 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
- /
If s=0 Then Return '0 sec' 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/1) '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 2 min, 3.5 sec 2 min, 3 sec 0 sec
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
Rust
<lang rust>fn seconds_to_compound(secs: u32) -> String {
let part = |comps: &mut String, c: &str, one: u32, secs: &mut u32| { if *secs >= one { let div = *secs / one; comps.push_str(&(div.to_string() + c)); *secs -= one * div; if *secs > 0 { comps.push_str(", "); } } };
let mut secs = secs; let mut comps = String::new(); part(&mut comps, "w", 60 * 60 * 24 * 7, &mut secs); part(&mut comps, "d", 60 * 60 * 24, &mut secs); part(&mut comps, "h", 60 * 60, &mut secs); part(&mut comps, "m", 60, &mut secs); part(&mut comps, "s", 1, &mut secs); comps
}</lang>
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
VBScript
<lang vb> Function compound_duration(n) Do Until n = 0 If n >= 604800 Then wk = Int(n/604800) n = n-(604800*wk) compound_duration = compound_duration & wk & " wk" End If If n >= 86400 Then d = Int(n/86400) n = n-(86400*d) If wk > 0 Then compound_duration = compound_duration & ", " End If compound_duration = compound_duration & d & " d" End If If n >= 3600 Then hr = Int(n/3600) n = n-(3600*hr) If d > 0 Then compound_duration = compound_duration & ", " End If compound_duration = compound_duration & hr & " hr" End If If n >= 60 Then min = Int(n/60) n = n-(60*min) If hr > 0 Then compound_duration = compound_duration & ", " End If compound_duration = compound_duration & min & " min" End If If n > 0 Then If min > 0 Then compound_duration = compound_duration & ", " End If compound_duration = compound_duration & ", " & n & " sec" n = 0 End If Loop End Function
'validating the function WScript.StdOut.WriteLine compound_duration(7259) WScript.StdOut.WriteLine compound_duration(86400) WScript.StdOut.WriteLine compound_duration(6000000) </lang>
- Output:
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min
zkl
<lang zkl>fcn toWDHMS(sec){ //-->(wk,d,h,m,s)
r,b:=List(),0; foreach u in (T(60,60,24,7)){ sec,b=sec.divr(u); // aka divmod r.append(b); } r.append(sec).reverse()
}</lang> Or, if you like to be concise: <lang zkl>fcn toWDHMS(sec){ //-->(wk,d,h,m,s)
T(60,60,24,7).reduce(fcn(n,u,r){ n,u=n.divr(u); r.append(u); n }, sec,r:=List()):r.append(_).reverse();
}</lang> were the ":" op takes the left result and stuffs it into the "_" position. <lang zkl>units:=T(" wk"," d"," hr"," min"," sec"); foreach s in (T(7259,86400,6000000)){
toWDHMS(s).zip(units).pump(List,fcn([(t,u)]){ t and String(t,u) or "" }) .filter().concat(", ").println();
}</lang>
- Output:
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min