Convert seconds to compound duration: Difference between revisions
m Added Sidef |
mNo edit summary |
||
Line 1,028: | Line 1,028: | ||
0.00 seconds: 0 sec |
0.00 seconds: 0 sec |
||
</pre> |
</pre> |
||
=={{header|Ring}}== |
|||
<lang ring> |
|||
sec = 6000000 |
|||
week = floor(sec/60/60/24/7) |
|||
if week > 0 see sec |
|||
see " seconds is " + week + " week" + nl ok |
|||
day = floor(sec/60/60/24) |
|||
if day > 0 see sec |
|||
see " seconds is " + day + " day" + nl ok |
|||
hour = floor(sec/60/60) |
|||
if hour > 0 see sec |
|||
see " seconds is " + hour + " hour" + nl ok |
|||
minute = floor(sec/60) |
|||
if hour > 0 see sec |
|||
see " seconds is " + minute + " minute" + nl ok |
|||
</lang> |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
<lang ruby>MINUTE = 60 |
<lang ruby>MINUTE = 60 |
Revision as of 08:38, 7 March 2016
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
AutoHotkey
<lang AutoHotkey>duration(n){ sec:=1, min:=60*sec, hr:=60*min, day:=24*hr, wk:=7*day w :=n//wk , n:=Mod(n,wk) d :=n//day , n:=Mod(n,day) h :=n//hr , n:=Mod(n,hr) m :=n//min , n:=Mod(n,min) s :=n return trim((w?w " wk, ":"") (d?d " d, ":"") (h?h " hr, ":"") (m?m " min, ":"") (s?s " sec":""),", ") }</lang> Examples:<lang AutoHotkey>data= ( 7259 86400 6000000 )
loop, parse, data, `n, `r res .= A_LoopField "`t: " duration(A_LoopField) "`n" MsgBox % res return</lang>
Outputs:
7259 : 2 hr, 59 sec 86400 : 1 d 6000000 : 9 wk, 6 d, 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
Befunge
The value to convert is read from stdin, and the corresponding compound duration is written to stdout.
<lang befunge>&>:"<"%\"O{rq"**+\"<"/:"<"%\"r<|":*+*5-\v v-7*"l~"/7\"d"\%7:/*83\+*:"xD"\%*83:/"<"< > \:! #v_v#-#<",",#$48*#<,#<.#<>#_:"~"%,v ^_@#:$$< > .02g92p ^ ^!:/"~"<</lang>
- Output:
7259 2 hr, 59 sec 86400 1 d 6000000 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 csharp>using System; using System.Collections.Generic; using System.Linq;
namespace ConvertSecondsToCompoundDuration {
class Program { static void Main( string[] args ) { foreach ( string arg in args ) { int duration ; bool isValid = int.TryParse( arg , out duration ) ;
if ( !isValid ) { Console.Error.WriteLine( "ERROR: Not an integer: {0}" , arg ) ; } if ( duration < 0 ) { Console.Error.WriteLine( "ERROR: duration must be non-negative" , arg ) ; } Console.WriteLine(); Console.WriteLine( "{0:#,##0} seconds ==> {1}" , duration , FormatAsDuration(duration) ) ; } return ; } private static string FormatAsDuration( int duration ) { if ( duration < 0 ) throw new ArgumentOutOfRangeException("duration") ; return string.Join( ", " , GetDurationParts(duration) ) ; } private static IEnumerable<string> GetDurationParts( int duration ) { var parts = new[] { new { Name="wk" , Length = 7*24*60*60*1 , } , new { Name="d" , Length = 24*60*60*1 , } , new { Name="h" , Length = 60*60*1 , } , new { Name="m" , Length = 60*1 , } , new { Name="s" , Length = 1 , } , } ; foreach ( var part in parts ) { int n = Math.DivRem( duration , part.Length , out duration ) ; if ( n > 0 ) yield return string.Format( "{0} {1}" , n , part.Name ) ; } } }
}</lang>
- Output:
7,259 seconds ==> 2 h, 59 s 86,400 seconds ==> 1 d 6,000,000 seconds ==> 9 wk, 6 d, 10 h, 40 m
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>
Java
<lang java>public class CompoundDuration {
public static void main(String[] args) { compound(7259); compound(86400); compound(6000_000); }
private static void compound(long seconds) { StringBuilder sb = new StringBuilder();
seconds = addUnit(sb, seconds, 604800, " wk, "); seconds = addUnit(sb, seconds, 86400, " d, "); seconds = addUnit(sb, seconds, 3600, " hr, "); seconds = addUnit(sb, seconds, 60, " min, "); addUnit(sb, seconds, 1, " sec, ");
sb.setLength(sb.length() > 2 ? sb.length() - 2 : 0);
System.out.println(sb); }
private static long addUnit(StringBuilder sb, long sec, long unit, String s) { long n; if ((n = sec / unit) > 0) { sb.append(n).append(s); sec %= (n * unit); } return sec; }
}</lang>
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min
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
Ring
<lang ring> sec = 6000000 week = floor(sec/60/60/24/7) if week > 0 see sec
see " seconds is " + week + " week" + nl ok
day = floor(sec/60/60/24) if day > 0 see sec
see " seconds is " + day + " day" + nl ok
hour = floor(sec/60/60) if hour > 0 see sec
see " seconds is " + hour + " hour" + nl ok
minute = floor(sec/60) if hour > 0 see sec
see " seconds is " + minute + " minute" + nl ok
</lang>
Ruby
<lang ruby>MINUTE = 60 HOUR = MINUTE*60 DAY = HOUR*24 WEEK = DAY*7
def sec_to_str(sec)
w, rem = sec.divmod(WEEK) d, rem = rem.divmod(DAY) h, rem = rem.divmod(HOUR) m, s = rem.divmod(MINUTE) units = ["#{w} wk", "#{d} d", "#{h} h", "#{m} min", "#{s} sec"] units.reject{|str| str.start_with?("0")}.join(", ")
end
[7259, 86400, 6000000].each{|t| puts "#{t}\t: #{sec_to_str(t)}"}</lang> Output:
7259 : 2 h, 59 sec 86400 : 1 d 6000000 : 9 wk, 6 d, 10 h, 40 min
Rust
This solution deviates from the prompt a bit in order to make it more general. The benefit of doing it this way is that any values can be filled in for days, hours, minutes and seconds and the `balance` method will do the balancing accordingly. Also, rather than converting the value into a String, it simply implements the `Display` trait. <lang rust>use std::fmt;
struct CompoundTime {
w: usize, d: usize, h: usize, m: usize, s: usize,
}
macro_rules! reduce {
($s: ident, $(($from: ident, $to: ident, $factor: expr)),+) => {{ $( $s.$to += $s.$from / $factor; $s.$from %= $factor; )+ }}
}
impl CompoundTime {
#[inline] fn new(w: usize, d: usize, h: usize, m: usize, s: usize) -> Self{ CompoundTime { w: w, d: d, h: h, m: m, s: s, } }
#[inline] fn balance(&mut self) { reduce!(self, (s, m, 60), (m, h, 60), (h, d, 24), (d, w, 7)); }
}
impl fmt::Display for CompoundTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}w {}d {}h {}m {}s", self.w, self.d, self.h, self.m, self.s) }
}
fn main() {
let mut ct = CompoundTime::new(0,3,182,345,2412); println!("Before: {}", ct); ct.balance(); println!("After: {}", ct);
}</lang>
Sidef
<lang ruby>func polymod(n, *divs) {
gather { divs.each { |i| var m = take(n % i) (n -= m) /= i } take(n) }
}
func compound_duration(seconds) {
(polymod(seconds, 60, 60, 24, 7) ~Z <sec min hr d wk>).grep { |a| a[0] > 0 }.reverse.map{.join(' ')}.join(', ')
}
[7259, 86400, 6000000].each { |s|
say "#{'%7d' % s} sec = #{compound_duration(s)}"
}</lang>
- Output:
7259 sec = 2 hr, 59 sec 86400 sec = 1 d 6000000 sec = 9 wk, 6 d, 10 hr, 40 min
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
uBasic/4tH
Since uBasic/4tH is integer-only, it is hard to return a string. However, it is capable to transform an integer value as required. <lang>Proc _CompoundDuration(7259) Proc _CompoundDuration(86400) Proc _CompoundDuration(6000000)
End
_CompoundDuration Param(1) ' Print compound seconds
a@ = FUNC(_Compound(a@, 604800, _wk)) a@ = FUNC(_Compound(a@, 86400, _d)) a@ = FUNC(_Compound(a@, 3600, _hr)) a@ = FUNC(_Compound(a@, 60, _min))
If a@ > 0 Then Print a@;" sec"; ' Still seconds left to print? Print
Return
_Compound Param(3)
Local(1)
d@ = a@/b@ ' Get main component a@ = a@%b@ ' Leave the rest
If d@ > 0 Then ' Print the component Print d@; Proc c@
If a@ > 0 Then Print ", "; ' If something follows, take EndIf ' care of the comma EndIf
Return (a@)
_wk Print " wk"; : Return
_d Print " d"; : Return
_hr Print " hr"; : Return
_min Print " min"; : Return</lang>
- Output:
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min 0 OK, 0:94
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