export type FormatDurationSecondsOptions = {
decimals?: number;
unit?: "s" | "seconds" ;
};
export type FormatDurationCompactOptions = {
/** Add space between units: "2m 5s" instead of "2m5s". Default: false */
spaced?: boolean ;
};
export function formatDurationSeconds(
ms: number,
options: FormatDurationSecondsOptions = {},
): string {
if (!Number.isFinite(ms)) {
return "unknown" ;
}
const decimals = options.decimals ?? 1 ;
const unit = options.unit ?? "s" ;
const seconds = Math.max(0 , ms) / 1000 ;
const fixed = seconds.toFixed(Math.max(0 , decimals));
const trimmed = fixed.replace(/\.0 +$/, "" ).replace(/(\.\d*[1 -9 ])0 +$/, "$1" );
return unit === "seconds" ? `${trimmed} seconds` : `${trimmed}s`;
}
/** Precise decimal-seconds output: "500ms" or "1.23s". Input is milliseconds. */
export function formatDurationPrecise(
ms: number,
options: FormatDurationSecondsOptions = {},
): string {
if (!Number.isFinite(ms)) {
return "unknown" ;
}
if (ms < 1000 ) {
return `${Math.max(0 , Math.round(ms))}ms`;
}
return formatDurationSeconds(ms, {
decimals: options.decimals ?? 2 ,
unit: options.unit ?? "s" ,
});
}
/**
* Compact compound duration : " 500 ms " , " 45 s " , " 2 m5s " , " 1 h30m " .
* With ` spaced ` : " 45 s " , " 2 m 5 s " , " 1 h 30 m " .
* Omits trailing zero components : " 1 m " not " 1 m 0 s " , " 2 h " not " 2 h 0 m " .
* Returns undefined for null / undefined / non - finite / non - positive input .
*/
export function formatDurationCompact(
ms?: number | null ,
options?: FormatDurationCompactOptions,
): string | undefined {
if (ms == null || !Number.isFinite(ms) || ms <= 0 ) {
return undefined;
}
if (ms < 1000 ) {
return `${Math.round(ms)}ms`;
}
const sep = options?.spaced ? " " : "" ;
const totalSeconds = Math.round(ms / 1000 );
const hours = Math.floor(totalSeconds / 3600 );
const minutes = Math.floor((totalSeconds % 3600 ) / 60 );
const seconds = totalSeconds % 60 ;
if (hours >= 24 ) {
const days = Math.floor(hours / 24 );
const remainingHours = hours % 24 ;
return remainingHours > 0 ? `${days}d${sep}${remainingHours}h` : `${days}d`;
}
if (hours > 0 ) {
return minutes > 0 ? `${hours}h${sep}${minutes}m` : `${hours}h`;
}
if (minutes > 0 ) {
return seconds > 0 ? `${minutes}m${sep}${seconds}s` : `${minutes}m`;
}
return `${seconds}s`;
}
/**
* Rounded single - unit duration for display : " 500 ms " , " 5 s " , " 3 m " , " 2 h " , " 5 d " .
* Returns fallback string for null / undefined / non - finite input .
*/
export function formatDurationHuman(ms?: number | null , fallback = "n/a" ): string {
if (ms == null || !Number.isFinite(ms) || ms < 0 ) {
return fallback;
}
if (ms < 1000 ) {
return `${Math.round(ms)}ms`;
}
const sec = Math.round(ms / 1000 );
if (sec < 60 ) {
return `${sec}s`;
}
const min = Math.round(sec / 60 );
if (min < 60 ) {
return `${min}m`;
}
const hr = Math.round(min / 60 );
if (hr < 24 ) {
return `${hr}h`;
}
const day = Math.round(hr / 24 );
return `${day}d`;
}
Messung V0.5 in Prozent C=99 H=93 G=95
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland