115 lines
3.5 KiB
ObjectPascal
115 lines
3.5 KiB
ObjectPascal
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.
|