// Browser-safe. No node:* imports, no DOM. Imported by both server and browser. const DASHES = /[‒–—−-]/; // -, –, —, minus sign // Matches only the non-ASCII members of DASHES — used to fold a leading minus/en/em dash down to // an ASCII hyphen before sign detection, so a string like "−0:30" (U+2212, the character every // signed number in this app's own reference data — SYMPTOM_FIXES, cultivar profiles — is written // with) parses instead of silently returning null. const NON_ASCII_DASHES = /[‒–—−]/g; /** "8:45" -> 525, "-0:20" -> -20, "+0:15" -> 15, "0" -> 0, "90" -> 90 (bare seconds). null on junk. */ export function parseDuration(str) { if (str === null || str === undefined) return null; const s = String(str).trim().replace(NON_ASCII_DASHES, "-"); if (s === "") return null; const sign = s.startsWith("-") ? -1 : 1; const body = s.replace(/^[+-]/, ""); const m = body.match(/^(\d+):(\d{1,2})$/); if (m) return sign * (Number(m[1]) * 60 + Number(m[2])); if (/^\d+(\.\d+)?$/.test(body)) return sign * Math.round(Number(body)); return null; } /** "7:30-9:00" / "7:30–9:00" -> midpoint seconds. Single value passes through parseDuration. */ export function parseRangeMidpoint(str) { if (str === null || str === undefined) return null; const s = String(str).trim(); if (s === "") return null; const parts = s.split(DASHES).map((p) => p.trim()).filter(Boolean); if (parts.length >= 2) { const a = parseDuration(parts[0]); const b = parseDuration(parts[parts.length - 1]); if (a === null || b === null) return null; return Math.round((a + b) / 2); } return parseDuration(s); } /** 525 -> "8:45". Negative seconds -> "-0:20". null -> "". */ export function formatDuration(totalSeconds) { if (totalSeconds === null || totalSeconds === undefined || Number.isNaN(totalSeconds)) return ""; const sign = totalSeconds < 0 ? "-" : ""; const abs = Math.abs(Math.round(totalSeconds)); const m = Math.floor(abs / 60); const s = abs % 60; return `${sign}${m}:${String(s).padStart(2, "0")}`; } /** Same as formatDuration but always carries an explicit sign, and "0" for zero. */ export function formatSigned(totalSeconds) { if (totalSeconds === null || totalSeconds === undefined || Number.isNaN(totalSeconds)) return ""; if (totalSeconds === 0) return "0"; const out = formatDuration(Math.abs(totalSeconds)); return totalSeconds < 0 ? `-${out}` : `+${out}`; }