(
duration: number | string | undefined | null,
options?: { precision?: number }
)
| 160 | * @returns A formatted duration string, or null if input is null/undefined |
| 161 | */ |
| 162 | export function formatDuration( |
| 163 | duration: number | string | undefined | null, |
| 164 | options?: { precision?: number } |
| 165 | ): string | null { |
| 166 | if (duration === undefined || duration === null) { |
| 167 | return null |
| 168 | } |
| 169 | |
| 170 | // Parse string durations (e.g., "500ms", "0.44ms", "1234") |
| 171 | let ms: number |
| 172 | if (typeof duration === 'string') { |
| 173 | ms = Number.parseFloat(duration.replace(/[^0-9.-]/g, '')) |
| 174 | if (!Number.isFinite(ms)) { |
| 175 | return duration |
| 176 | } |
| 177 | } else { |
| 178 | ms = duration |
| 179 | // Handle NaN/Infinity (e.g., cancelled blocks with no end time) |
| 180 | if (!Number.isFinite(ms)) { |
| 181 | return '\u2014' |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | const precision = options?.precision ?? 0 |
| 186 | |
| 187 | if (ms < 1) { |
| 188 | // Zero or near-zero: show "0ms" instead of "0.00ms" |
| 189 | if (ms === 0 || ms < 0.005) { |
| 190 | return '0ms' |
| 191 | } |
| 192 | // Sub-millisecond: show with 2 decimal places |
| 193 | return `${ms.toFixed(2)}ms` |
| 194 | } |
| 195 | |
| 196 | if (ms < 1000) { |
| 197 | // Milliseconds: round to integer |
| 198 | return `${Math.round(ms)}ms` |
| 199 | } |
| 200 | |
| 201 | const seconds = ms / 1000 |
| 202 | if (seconds < 60) { |
| 203 | if (precision > 0) { |
| 204 | // Strip trailing zeros (e.g., "5.00s" -> "5s", "5.10s" -> "5.1s") |
| 205 | return `${seconds.toFixed(precision).replace(/\.?0+$/, '')}s` |
| 206 | } |
| 207 | return `${Math.floor(seconds)}s` |
| 208 | } |
| 209 | |
| 210 | const minutes = Math.floor(seconds / 60) |
| 211 | const remainingSeconds = Math.floor(seconds % 60) |
| 212 | if (minutes < 60) { |
| 213 | return `${minutes}m ${remainingSeconds}s` |
| 214 | } |
| 215 | |
| 216 | const hours = Math.floor(minutes / 60) |
| 217 | const remainingMinutes = minutes % 60 |
| 218 | return `${hours}h ${remainingMinutes}m` |
| 219 | } |
no test coverage detected