Task

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.
This is detailed below (e.g., "2 hr, 59 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 minute = 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).




program convertSecondsToCompoundDuration(output);
 
const
	suffixUnitWeek    = 'wk';
	suffixUnitDay     = 'd';
	suffixUnitHour    = 'hr';  { Use `'h'` to be SI-compatible. }
	suffixUnitMinute  = 'min';
	suffixUnitSecond  = 'sec'; { NB: Only `'s'` is SI-approved. }
 
	suffixSeparator   = ' ';  { A non-breaking space would be appropriate. }
	quantitySeparator = ', ';
 
	{ Maximum expected length of `string` “12345 wk, 6 d, 7 hr, 8 min, 9 sec” }
	timeSpanPrintedMaximumLength = 4 * length(quantitySeparator) +
		20 + length(suffixUnitWeek) + 1 + length(suffixUnitDay) +
		2 + length(suffixUnitHour) + 2 + length(suffixUnitMinute) +
		2 + length(suffixUnitSecond) + 5 * length(suffixSeparator);
 
	{ Units of time expressed in seconds. }
	minute = 60;
	hour   = 60 * minute;
	day    = 24 * hour;
	week   =  7 * day;
 
type
	wholeNumber   = 0..maxInt;
	naturalNumber = 1..maxInt;
 
	canonicalTimeSpan = record
			weeks:   wholeNumber;
			days:    0..6;
			hours:   0..23;
			minutes: 0..59;
			seconds: 0..59;
		end;
 
	stringFitForTimeSpan = string(timeSpanPrintedMaximumLength);
 
{
	\brief turns a time span expressed in seconds into a `canonicalTimeSpan`
	\param duration the non-negative duration expressed in seconds
	\return a `canonicalTimeSpan` representing \param duration seconds
}
function getCanonicalTimeSpan(duration: wholeNumber): canonicalTimeSpan;
	{ Perform `div`ision and update `duration`. }
	function split(protected unit: naturalNumber): wholeNumber;
	begin
		split := duration div unit;
		duration := duration mod unit
	end;
var
	result: canonicalTimeSpan;
begin
	with result do
	begin
		weeks   := split(week);
		days    := split(day);
		hours   := split(hour);
		minutes := split(minute);
		seconds := duration
	end;
	{ In Pascal there needs to be _exactly_ one assignment to the }
	{ result variable bearing the same name as of the `function`. }
	getCanonicalTimeSpan := result
end;
 
{
	\brief turns a non-trivial duration into a string
	\param n a positive duration expressed in seconds
	\return \param n expressed in some human-readable form
}
function timeSpanString(protected n: naturalNumber): stringFitForTimeSpan;
const
	qs = quantitySeparator;
var
	result: stringFitForTimeSpan;
begin
	with getCanonicalTimeSpan(n) do
	begin
		{ `:1` specifies the minimum-width. Omitting it would cause }
		{ the compiler to insert a vendor-defined default, e. g. 20. }
		writeStr(result, weeks:1, suffixSeparator, suffixUnitWeek);
		{ For strings, `:n` specifies the _exact_ width (padded with spaces). }
		writeStr(result, result:ord(weeks > 0) * length(result));
 
		if days > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				days:1, suffixSeparator, suffixUnitDay);
		end;
		if hours > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				hours:1, suffixSeparator, suffixUnitHour);
		end;
		if minutes > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				minutes:1, suffixSeparator, suffixUnitMinute);
		end;
		if seconds > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				seconds:1, suffixSeparator, suffixUnitSecond);
		end
	end;
	timeSpanString := result
end;
 
{ === MAIN ============================================================= }
begin
	writeLn(   7259, ' seconds are “', timeSpanString(7259), '”');
	writeLn(  86400, ' seconds are “', timeSpanString(86400), '”');
	writeLn(6000000, ' seconds are “', timeSpanString(6000000), '”')
end.