Convert seconds to compound duration

From Rosetta Code
Task
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 not 1 min, 70 sec or 130 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>

  1. 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>

  1. include <inttypes.h> /* requires c99 */
  2. include <stdbool.h> /* requires c99 */
  3. include <stdio.h>
  4. include <stdlib.h>
  1. 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++

Works with: C++11

<lang cpp>

  1. include <iostream>
  2. 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

Translation of: Haskell

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

Works with: jq version 1.4

<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