diff --git a/src/lib/utils/parse-uptime.ts b/src/lib/utils/parse-uptime.ts new file mode 100644 index 0000000..57dc49a --- /dev/null +++ b/src/lib/utils/parse-uptime.ts @@ -0,0 +1,30 @@ +/** + * Parse a human-readable relative time ("2 hours", "45 seconds", "Less than a + * second") into seconds, for sorting containers by uptime. Pure; unit-tested. + * + * Only the literal "less than a second" collapses to 1s - a value like "45 seconds" + * must keep its real magnitude so sub-minute uptimes sort against each other (#1395). + */ +export function parseTimeStringToSeconds(timeStr: string): number { + const str = timeStr.toLowerCase(); + + if (str.includes('less than a second')) return 1; + if (str.includes('less than a minute') || str.includes('about a minute')) return 60; + + const match = str.match(/(\d+)\s*(second|minute|hour|day|week|month|year)/); + if (!match) return 0; + + const value = parseInt(match[1], 10); + const unit = match[2]; + + switch (unit) { + case 'second': return value; + case 'minute': return value * 60; + case 'hour': return value * 3600; + case 'day': return value * 86400; + case 'week': return value * 604800; + case 'month': return value * 2592000; + case 'year': return value * 31536000; + default: return 0; + } +} diff --git a/src/routes/containers/+page.svelte b/src/routes/containers/+page.svelte index 9e96814..4549339 100644 --- a/src/routes/containers/+page.svelte +++ b/src/routes/containers/+page.svelte @@ -92,6 +92,7 @@ import { canAccess } from '$lib/stores/auth'; import { vulnerabilityCriteriaIcons } from '$lib/utils/update-steps'; import { compareIps } from '$lib/utils/ip'; + import { parseTimeStringToSeconds } from '$lib/utils/parse-uptime'; import { formatHostPortUrl } from '$lib/utils/url'; import { parseCustomUrl } from '$lib/utils/custom-url'; import { extractTraefikUrls } from '$lib/utils/traefik-urls'; @@ -1321,31 +1322,6 @@ return -Infinity; } - function parseTimeStringToSeconds(timeStr: string): number { - // Parse strings like "2 hours", "3 days", "About a minute", "Less than a second" - const str = timeStr.toLowerCase(); - - if (str.includes('less than a second')) return 1; - if (str.includes('less than a minute') || str.includes('about a minute')) return 60; - - const match = str.match(/(\d+)\s*(second|minute|hour|day|week|month|year)/); - if (!match) return 0; - - const value = parseInt(match[1], 10); - const unit = match[2]; - - switch (unit) { - case 'second': return value; - case 'minute': return value * 60; - case 'hour': return value * 3600; - case 'day': return value * 86400; - case 'week': return value * 604800; - case 'month': return value * 2592000; - case 'year': return value * 31536000; - default: return 0; - } - } - function getHealthVariant(health?: string): 'default' | 'destructive' | 'secondary' | 'outline' { switch (health) { case 'healthy': @@ -2447,24 +2423,26 @@ {/if} {/if} - - {#if currentTerminalContainerId} - {@const activeTerminal = activeTerminals.find(t => t.containerId === currentTerminalContainerId)} - {#if activeTerminal} -