Convert seconds to compound duration: Difference between revisions
Line 186: | Line 186: | ||
{{Trans|JavaScript}} |
{{Trans|JavaScript}} |
||
<lang AppleScript> |
<lang AppleScript>on run |
||
on run |
|||
intercalate(linefeed, ¬ |
intercalate(linefeed, ¬ |
||
map(test, ¬ |
map(test, ¬ |
||
Line 199: | Line 198: | ||
-- DURATION STRINGS |
|||
-- angloDuration :: Int -> String |
-- angloDuration :: Int -> String |
||
Line 205: | Line 206: | ||
on formatted(a, lstPair) |
on formatted(a, lstPair) |
||
set q to item 1 of lstPair |
set q to item 1 of lstPair |
||
if q > 0 then |
if q > 0 then |
||
{(q as string) & space & item 2 of lstPair} & a |
{(q as string) & space & item 2 of lstPair} & a |
||
Line 224: | Line 224: | ||
on weekParts(intSeconds) |
on weekParts(intSeconds) |
||
script mf |
script mf |
||
-- partList Record |
-- partList Record -> Int -> Record |
||
on partList(a, x |
on partList(a, x) |
||
set intRest to remaining of a |
set intRest to remaining of a |
||
Revision as of 17:58, 1 August 2016
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
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
ALGOL 68
<lang algol68># MODE to hold the compound duration # MODE DURATION = STRUCT( INT weeks, days, hours, minutes, seconds );
- returns seconds converted to a DURATION #
OP TODURATION = ( LONG INT seconds )DURATION:
BEGIN LONG INT time := seconds; DURATION result := DURATION( 0, 0, 0, 0, 0 ); seconds OF result := SHORTEN ( time MOD 60 ); time OVERAB 60; minutes OF result := SHORTEN ( time MOD 60 ); time OVERAB 60; hours OF result := SHORTEN ( time MOD 24 ); time OVERAB 24; days OF result := SHORTEN ( time MOD 7 ); time OVERAB 7; weeks OF result := SHORTEN time; result END # DURATION # ;# returns seconds converted to a DURATION #
OP TODURATION = ( INT seconds )DURATION: TODURATION LENG seconds;
- returns a readable form of the DURATION #
OP TOSTRING = ( DURATION t )STRING:
BEGIN STRING result := ""; STRING separator := ""; IF weeks OF t /= 0 THEN result +:= separator + whole( weeks OF t, 0 ) + " wk"; separator := ", " FI; IF days OF t /= 0 THEN result +:= separator + whole( days OF t, 0 ) + " d"; separator := ", " FI; IF hours OF t /= 0 THEN result +:= separator + whole( hours OF t, 0 ) + " hr"; separator := ", " FI; IF minutes OF t /= 0 THEN result +:= separator + whole( minutes OF t, 0 ) + " min"; separator := ", " FI; IF seconds OF t /= 0 THEN result +:= separator + whole( seconds OF t, 0 ) + " sec"; separator := ", " FI; IF result = "" THEN # duration is 0 # result := "0 sec" FI; result END # TOSTRING # ;
- test cases #
print( ( TOSTRING TODURATION 7259, newline ) ); print( ( TOSTRING TODURATION 86400, newline ) ); print( ( TOSTRING TODURATION 6000000, newline ) )</lang>
- Output:
2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min
AppleScript
<lang AppleScript>on run
intercalate(linefeed, ¬ map(test, ¬ [7259, 86400, 6000000]))
end run
-- test :: Int -> String on test(intSeconds)
(intSeconds as string) & " -> " & angloDuration(intSeconds)
end test
-- DURATION STRINGS
-- angloDuration :: Int -> String on angloDuration(intSeconds)
script mf on formatted(a, lstPair) set q to item 1 of lstPair if q > 0 then {(q as string) & space & item 2 of lstPair} & a else a end if end formatted end script intercalate(", ", ¬ foldr(mf's formatted, [], ¬ zip(weekParts(intSeconds), ¬ ["wk", "d", "hr", "min", "sec"])))
end angloDuration
-- weekParts :: Int -> [Int]
on weekParts(intSeconds)
script mf -- partList Record -> Int -> Record on partList(a, x) set intRest to remaining of a if x is not missing value then set intMod to intRest mod x set d to x else set intMod to intRest set d to 1 end if {remaining:(intRest - intMod) div d, parts:{intMod} & parts of a} end partList end script parts of foldr(mf's partList, ¬ {remaining:intSeconds, parts:[]}, [missing value, 7, 24, 60, 60])
end weekParts
-- GENERIC FUNCTIONS
-- foldr :: (a -> b -> a) -> a -> [b] -> a on foldr(f, startValue, xs)
set mf to mReturn(f) set v to startValue set lng to length of xs repeat with i from lng to 1 by -1 set v to mf's lambda(v, item i of xs, i, xs) end repeat return v
end foldr
-- map :: (a -> b) -> [a] -> [b] on map(f, xs)
set mf to mReturn(f) set lng to length of xs set lst to {} repeat with i from 1 to lng set end of lst to mf's lambda(item i of xs, i, xs) end repeat return lst
end map
-- zip :: [a] -> [b] -> [(a, b)] on zip(xs, ys)
script mf on pair(x, i) [x, item i of my closure's ys] end pair end script if length of xs = length of ys then map(mClosure(pair of mf, {ys:ys}), xs) else missing value end if
end zip
-- intercalate :: Text -> [Text] -> Text on intercalate(strText, lstText)
set {dlm, my text item delimiters} to {my text item delimiters, strText} set strJoined to lstText as text set my text item delimiters to dlm return strJoined
end intercalate
-- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: Handler -> Script on mReturn(f)
if class of f is script then return f script property lambda : f end script
end mReturn
-- mClosure :: Handler -> Record -> Script on mClosure(f, recBindings)
script property closure : recBindings property lambda : f end script
end mClosure</lang>
7259 -> 2 hr, 59 sec 86400 -> 1 d 6000000 -> 9 wk, 6 d, 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
beeswax
<lang beeswax>#>%f# #>%f# #f%<##>%f# pq":X~7~ :X@~++8~8@:X:X@~-~4~.+~8@T_
## ## #### #`K0[`}`D2[`}BF3< <
>{` wk, `>g?"p{` d, `>g?"p{` hr, `>g?"p{` min, `>g"b{` sec, `b
> d > d > d > d</lang>
- Output:
julia> beeswax("seconds to compound duration.bswx") i7259 2 hr, 59 sec Program finished! julia> beeswax("seconds to compound duration.bswx") i86400 1 d Program finished! julia> beeswax("seconds to compound duration.bswx") i6000000 9 wk, 6 d, 10 hr, 40 min Program finished!
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#
C#: Standard method
<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) ) ; } } 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#: Using the TimeSpan struct and query syntax
<lang csharp>private static string ConvertToCompoundDuration(int seconds) {
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds)); if (seconds == 0) return "0 sec";
TimeSpan span = TimeSpan.FromSeconds(seconds); int[] parts = {span.Days / 7, span.Days % 7, span.Hours, span.Minutes, span.Seconds}; string[] units = {" wk", " d", " hr", " min", " sec"};
return string.Join(", ", from index in Enumerable.Range(0, units.Length) where parts[index] > 0 select parts[index] + units[index]);
}</lang>
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
COBOL
<lang COBOL> identification division.
program-id. fmt-dura. data division. working-storage section. 1 input-seconds pic 9(8). 1 formatted-duration pic x(30) global. 1 fractions. 2 weeks pic z(3)9. 2 days pic z(3)9. 2 hours pic z(3)9. 2 minutes pic z(3)9. 2 seconds pic z(3)9. 1 . 2 weeks-str pic x(4) value "wk". 2 days-str pic x(4) value "d". 2 hours-str pic x(4) value "hr". 2 minutes-str pic x(4) value "min". 2 seconds-str pic x(4) value "sec". 1 work binary global. 2 str-pos pic 9(4). 2 chars-transferred pic 9(4). procedure division. begin. display "Enter duration (seconds): " no advancing accept input-seconds divide input-seconds by 60 giving input-seconds remainder seconds divide input-seconds by 60 giving input-seconds remainder minutes divide input-seconds by 24 giving input-seconds remainder hours divide input-seconds by 7 giving weeks remainder days move 1 to str-pos call "fmt" using weeks weeks-str call "fmt" using days days-str call "fmt" using hours hours-str call "fmt" using minutes minutes-str call "fmt" using seconds seconds-str display formatted-duration stop run .
identification division. program-id. fmt. data division. working-storage section. 77 nothing pic x. linkage section. 1 formatted-value pic x(4). 1 duration-size pic x(4). procedure division using formatted-value duration-size. begin. if function numval (formatted-value) not = 0 perform insert-comma-space unstring formatted-value delimited all space into nothing formatted-duration (str-pos:) count chars-transferred add chars-transferred to str-pos string space delimited size duration-size delimited space into formatted-duration pointer str-pos end-if exit program .
insert-comma-space. if str-pos > 1 move ", " to formatted-duration (str-pos:) add 2 to str-pos end-if . end program fmt. end program fmt-dura.</lang>
Enter duration (seconds): 7259 2 hr, 59 sec Enter duration (seconds): 86400 1 d Enter duration (seconds): 6000000 9 wk, 6 d, 10 hr, 40 min
D
<lang d> import std.stdio, std.conv, std.algorithm;
immutable uint SECSPERWEEK = 604_800; immutable uint SECSPERDAY = 86_400; immutable uint SECSPERHOUR = 3_600; immutable uint SECSPERMIN = 60;
string ConvertSeconds(in uint seconds) {
uint rem = seconds;
uint weeks = rem / SECSPERWEEK; rem %= SECSPERWEEK; uint days = rem / SECSPERDAY; rem %= SECSPERDAY; uint hours = rem / SECSPERHOUR; rem %= SECSPERHOUR; uint mins = rem / SECSPERMIN; rem %= SECSPERMIN;
string formatted = "";
(weeks != 0) ? formatted ~= (weeks.to!string ~ " wk, ") : formatted; (days != 0) ? formatted ~= (days.to!string ~ " d, ") : formatted; (hours != 0) ? formatted ~= (hours.to!string ~ " hr, ") : formatted; (mins != 0) ? formatted ~= (mins.to!string ~ " min, ") : formatted; (rem != 0) ? formatted ~= (rem.to!string ~ " sec") : formatted;
if (formatted.endsWith(", ")) return formatted[0..$-2];
return formatted;
}
void main() {
7_259.ConvertSeconds.writeln; 86_400.ConvertSeconds.writeln; 6_000_000.ConvertSeconds.writeln;
} </lang>
- Output:
2 hr, 59 sec 1 d 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
Fortran
If the PARAMETER statements were replaced (say with suitable DATA statements) then this could be compiled with Fortran 77, if a find-last-non-blank function were to be supplied and a supplementary scratchpad used so that the numbers could be placed without leading spaces (thus "9" and "10", not " 9") because the I0 format specifier is a F90 facility. Earlier Fortran compilers lacking a CHARACTER variable would present further difficulty.
If the time to describe was not an integer but a floating-point value with fractional parts, then there is a complication. The number of seconds can be less than sixty, but, on output, 60 seconds can appear. If the number of seconds was to be written with one decimal digit (say) and the output format was F4.1 for that, then if the value was 59·95 or more, it will be rounded up for output, in this example to 60·0. Various systems make this mistake, as also with latitude and longitude, and it is a general problem. A fixup pass is necessary before generating the output: maintain an array with the integer values of the various units, then (for one decimal digit usage) check that the seconds part is less than 59·95. If not, set it to zero and augment the minutes count. If this is 60 or more, set it to zero and augment the hours count, and so on. Thus the array. <lang Fortran>
SUBROUTINE PROUST(T) !Remembrance of time passed. INTEGER T !The time, in seconds. Positive only, please. INTEGER NTYPES !How many types of time? PARAMETER (NTYPES = 5) !This should do. INTEGER USIZE(NTYPES) !Size of the time unit. CHARACTER*3 UNAME(NTYPES)!Name of the time unit. PARAMETER (USIZE = (/7*24*60*60, 24*60*60, 60*60, 60, 1/)) !The compiler does some arithmetic. PARAMETER (UNAME = (/ "wk", "d", "hr","min","sec"/)) !Approved names, with trailing spaces. CHARACTER*28 TEXT !A scratchpad. INTEGER I,L,N,S !Assistants. S = T !A copy I can mess with. L = 0 !No text has been generated. DO I = 1,NTYPES !Step through the types to do so. N = S/USIZE(I) !Largest first. IF (N.GT.0) THEN !Above the waterline? S = S - N*USIZE(I) !Yes! Remove its contribution. IF (L.GT.0) THEN !Is this the first text to be rolled? L = L + 2 !No. TEXT(L - 1:L) = ", " !Cough forth some punctuation. END IF !Now ready for this count. WRITE (TEXT(L + 1:),1) N,UNAME(I) !Place, with the unit name. 1 FORMAT (I0,1X,A) !I0 means I only: variable-length, no leading spaces. L = LEN_TRIM(TEXT) !Find the last non-blank resulting. END IF !Since I'm not keeping track. END DO !On to the next unit.
Cast forth the result.
WRITE (6,*) T,">",TEXT(1:L),"<" !With annotation. END !Simple enough with integers.
PROGRAM MARCEL !Stir the cup. CALL PROUST(7259) CALL PROUST(7260) CALL PROUST(86400) CALL PROUST(6000000) CALL PROUST(0) CALL PROUST(-666) END
</lang> Output:
7259 >2 hr, 59 sec< 7260 >2 hr, 1 min< 86400 >1 d< 6000000 >9 wk, 6 d, 10 hr, 40 min< 0 >< -666 ><
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=: verb define
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
JavaScript
ES5 Functional
<lang JavaScript>(function () {
'use strict';
// angloDuration :: Int -> String function angloDuration(intSeconds) { return zip( weekParts(intSeconds), ['wk', 'd', 'hr', 'min','sec'] ) .reduce(function (a, x) { return a.concat(x[0] ? ( [(x[0].toString() + ' ' + x[1])] ) : []); }, []) .join(', '); }
// weekParts :: Int -> [Int] function weekParts(intSeconds) {
return [undefined, 7, 24, 60, 60] .reduceRight(function (a, x) { var intRest = a.remaining, intMod = isNaN(x) ? intRest : intRest % x;
return { remaining:(intRest - intMod) / (x || 1), parts: [intMod].concat(a.parts) }; }, { remaining: intSeconds, parts: [] }) .parts } // GENERIC ZIP
// zip :: [a] -> [b] -> [(a,b)] function zip(xs, ys) { return xs.length === ys.length ? ( xs.map(function (x, i) { return [x, ys[i]]; }) ) : undefined; } // TEST
return [7259, 86400, 6000000] .map(function (intSeconds) { return intSeconds.toString() + ' -> ' + angloDuration(intSeconds); }) .join('\n');
})(); </lang>
- Output:
7259 -> 2 hr, 59 sec 86400 -> 1 d 6000000 -> 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>
Lua
<lang Lua>function duration (secs)
local units, dur = {"wk", "d", "hr", "min"}, "" for i, v in ipairs({604800, 86400, 3600, 60}) do if secs >= v then dur = dur .. math.floor(secs / v) .. " " .. units[i] .. ", " secs = secs % v end end if secs == 0 then return dur:sub(1, -3) else return dur .. secs .. " sec" end
end
print(duration(7259)) print(duration(86400)) print(duration(6000000))</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,
PARI/GP
Note: my own string function ssubstr() was used. You can find it here on RosettaCode Wiki.
<lang parigp> \\ Convert seconds to compound duration \\ 4/11/16 aev secs2compdur(secs)={ my(us=[604800,86400,3600,60,1],ut=[" wk, "," d, "," hr, "," min, "," sec"],
cd=[0,0,0,0,0],u,cdt="");
for(i=1,5, u=secs\us[i]; if(u==0, next, cd[i]=u; secs-=us[i]*cd[i])); for(i=1,5, if(cd[i]==0, next, cdt=Str(cdt,cd[i],ut[i]))); if(ssubstr(cdt,#cdt-1,1)==",", cdt=ssubstr(cdt,1,#cdt-2)); return(cdt); }
{\\ Required tests: print(secs2compdur(7259)); print(secs2compdur(86400)); print(secs2compdur(6000000)); } </lang>
- Output:
2 hr, 59 sec 1 d 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
Phix
The existing standard function for this gives the results shown <lang Phix>?elapsed(7259) ?elapsed(86400) ?elapsed(6000000)</lang>
- Output:
"2 hours and 59s" "1 day, 0 hours" "69 days, 10 hours, 40 minutes"
The routine is implemented in builtins\pelapsed.e which you can modify as desired, and is reproduced for reference below <lang Phix>function elapzd(integer v, string d) -- private helper function. formats v and pluralises d by adding an "s", or not.
return sprintf("%d %s%s",{v,d,iff(v=1?"":"s")})
end function
global function elapsed(atom s) -- convert s (in seconds) into an elapsed time string suitable for display. -- limits: a type check error occurs if s exceeds approx 100 billion years. atom m,h,d,y string minus = "", secs, mins
if s<0 then minus = "minus " s = 0-s end if m = floor(s/60) s = remainder(s,60) secs = sprintf(iff(integer(s)?"%ds":"%3.2fs"),s) if m=0 then return sprintf("%s%s",{minus,secs}) end if s = round(s) secs = iff(s=0?"":sprintf(" and %02ds",s)) h = floor(m/60) m = remainder(m,60) if h=0 then return sprintf("%s%s%s",{minus,elapzd(m,"minute"),secs}) end if mins = iff(m=0?"":", "&elapzd(m,"minute")) if h<24 then return sprintf("%s%s%s%s",{minus,elapzd(h,"hour"),mins,secs}) end if d = floor(h/24) h = remainder(h,24) if d<365 then return sprintf("%s%s, %s%s%s",{minus,elapzd(d,"day"),elapzd(h,"hour"),mins,secs}) end if y = floor(d/365) d = remainder(d,365) return sprintf("%s%s, %s, %s%s%s",{minus,elapzd(y,"year"),elapzd(d,"day"),elapzd(h,"hour"),mins,secs})
end function</lang>
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.
PowerShell
<lang PowerShell> function Get-Time {
<# .SYNOPSIS Gets a time string in the form: # wk, # d, # hr, # min, # sec .DESCRIPTION Gets a time string in the form: # wk, # d, # hr, # min, # sec (Values of 0 are not displayed in the string.)
Days, Hours, Minutes or Seconds in any combination may be used as well as Start and End dates.
When used with the -AsObject switch an object containing properties similar to a System.TimeSpan object is returned. .INPUTS DateTime or Int32 .OUTPUTS String or PSCustomObject .EXAMPLE Get-Time -Seconds 7259 .EXAMPLE Get-Time -Days 31 -Hours 4 -Minutes 8 -Seconds 16 .EXAMPLE Get-Time -Days 31 -Hours 4 -Minutes 8 -Seconds 16 -AsObject .EXAMPLE Get-Time -Start 3/10/2016 -End 1/20/2017 .EXAMPLE Get-Time -Start (Get-Date) -End (Get-Date).AddSeconds(6000000) #> [CmdletBinding(DefaultParameterSetName='Date')] Param ( # Start date [Parameter(Mandatory=$false, ParameterSetName='Date', Position=0)] [datetime] $Start = (Get-Date),
# End date [Parameter(Mandatory=$false, ParameterSetName='Date', Position=1)] [datetime] $End = (Get-Date),
# Days in the time span [Parameter(Mandatory=$false, ParameterSetName='Time')] [int] $Days = 0,
# Hours in the time span [Parameter(Mandatory=$false, ParameterSetName='Time')] [int] $Hours = 0,
# Minutes in the time span [Parameter(Mandatory=$false, ParameterSetName='Time')] [int] $Minutes = 0,
# Seconds in the time span [Parameter(Mandatory=$false, ParameterSetName='Time')] [int] $Seconds = 0,
[switch] $AsObject )
Begin { [PSCustomObject]$timeObject = "PSCustomObject" | Select-Object -Property Weeks,RemainingDays, Days,Hours,Minutes,Seconds,Milliseconds,Ticks, TotalDays,TotalHours,TotalMinutes,TotalSeconds,TotalMilliseconds [int]$remainingDays = 0 [int]$weeks = 0
[string[]]$timeString = @() } Process { switch ($PSCmdlet.ParameterSetName) { 'Date' { $timeSpan = New-TimeSpan -Start $Start -End $End } 'Time' { $timeSpan = New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $Seconds } }
$weeks = [System.Math]::DivRem($timeSpan.Days, 7, [ref]$remainingDays)
$timeObject.Weeks = $weeks $timeObject.RemainingDays = $remainingDays $timeObject.Days = $timeSpan.Days $timeObject.Hours = $timeSpan.Hours $timeObject.Minutes = $timeSpan.Minutes $timeObject.Seconds = $timeSpan.Seconds $timeObject.Milliseconds = $timeSpan.Milliseconds $timeObject.Ticks = $timeSpan.Ticks $timeObject.TotalDays = $timeSpan.TotalDays $timeObject.TotalHours = $timeSpan.TotalHours $timeObject.TotalMinutes = $timeSpan.TotalMinutes $timeObject.TotalSeconds = $timeSpan.TotalSeconds $timeObject.TotalMilliseconds = $timeSpan.TotalMilliseconds } End { if ($AsObject) { return $timeObject }
if ($timeObject.Weeks) { $timeString += "$($timeObject.Weeks) wk" } if ($timeObject.RemainingDays) { $timeString += "$($timeObject.RemainingDays) d" } if ($timeObject.Hours) { $timeString += "$($timeObject.Hours) hr" } if ($timeObject.Minutes) { $timeString += "$($timeObject.Minutes) min" } if ($timeObject.Seconds) { $timeString += "$($timeObject.Seconds) sec" }
return ($timeString -join ", ") }
} </lang>
- Output:
PS C:\Scripts> 7259, 86400, 6000000 | ForEach-Object { Get-Time -Seconds $_ } 2 hr, 59 sec 1 d 9 wk, 6 d, 10 hr, 40 min PS C:\Scripts> Get-Time -Start 3/10/2016 -End 1/20/2017 45 wk, 1 d PS C:\Scripts> Get-Time -Seconds 6000000 -AsObject Weeks : 9 RemainingDays : 6 Days : 69 Hours : 10 Minutes : 40 Seconds : 0 Milliseconds : 0 Ticks : 60000000000000 TotalDays : 69.4444444444444 TotalHours : 1666.66666666667 TotalMinutes : 100000 TotalSeconds : 6000000 TotalMilliseconds : 6000000000
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 time units.*/ parse arg @; if @= then @=7259 86400 6000000 /*Not specified? Then use the default.*/
do j=1 for words(@); z=word(@, j) /* [↓] process each number in the list*/ say right(z, 25) 'seconds: ' convSec(z) /*convert a number to bigger time units*/ end /*j*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ 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.*/ $=strip(space(w d h m s),,","); if $== then z=0 "sec" /*handle 0 sec.*/ return $
/*──────────────────────────────────────────────────────────────────────────────────────*/ timeU: parse arg u,$; _=x%u; if _==0 then return ; x=x-_*u; return _ $","</lang> output when using the default (internal) 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 = 6000005 week = floor(sec/60/60/24/7) if week > 0 see sec
see " seconds is " + week + " weeks " ok
day = floor(sec/60/60/24) % 7 if day > 0 see day
see " days " ok
hour = floor(sec/60/60) % 24 if hour > 0 see hour
see " hours " ok
minute = floor(sec/60) % 60 if minute > 0 see minute
see " minutes " ok
second = sec % 60 if second > 0 see second
see " seconds" + 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
ZX Spectrum Basic
<lang zxbasic>10 LET m=60: LET h=60*m: LET d=h*24: LET w=d*7 20 DATA 10,7259,86400,6000000,0,1,60,3600,604799,604800,694861 30 READ n 40 FOR i=1 TO n 50 READ s 60 LET nweek=0: LET nday=0: LET nhour=0: LET nmin=0: LET nsec=0: LET s$="" 70 PRINT s;" = "; 80 IF s>=w THEN LET nweek=INT (s/w): LET s=FN m(s,w) 90 IF s>=d THEN LET nday=INT (s/d): LET s=FN m(s,d) 100 IF s>=h THEN LET nhour=INT (s/h): LET s=FN m(s,h) 110 IF s>=m THEN LET nmin=INT (s/m): LET s=FN m(s,m) 120 LET nsec=INT (s) 130 IF nweek>0 THEN LET s$=s$+STR$ nweek+" wk, " 140 IF nday>0 THEN LET s$=s$+STR$ nday+" d, " 150 IF nhour>0 THEN LET s$=s$+STR$ nhour+" hr, " 160 IF nmin>0 THEN LET s$=s$+STR$ nmin+" min, " 170 IF nsec>0 THEN LET s$=s$+STR$ nsec+" sec" 180 IF s$<>"" THEN IF s$(LEN s$-1)="," THEN LET s$=s$( TO LEN s$-2) 190 PRINT s$ 200 NEXT i 210 STOP 220 DEF FN m(a,b)=a-INT (a/b)*b</lang>
- Programming Tasks
- Solutions by Programming Task
- Ada
- ALGOL 68
- AppleScript
- AutoHotkey
- AWK
- Batch File
- Beeswax
- Befunge
- C
- C sharp
- System
- System.Collections.Generic
- System.Linq
- C++
- COBOL
- D
- Erlang
- Fortran
- Haskell
- J
- Java
- JavaScript
- Jq
- Lua
- Mathematica
- PARI/GP
- Perl
- Perl 6
- Phix
- PL/I
- PowerShell
- Python
- Racket
- REXX
- Ring
- Ruby
- Rust
- Sidef
- Tcl
- UBasic/4tH
- VBScript
- Zkl
- ZX Spectrum Basic