|
| 1 | +/** |
| 2 | + * Time and duration utility functions for formatters. |
| 3 | + * |
| 4 | + * Extracted to break the circular import between `human.ts` and `trace.ts`: |
| 5 | + * both modules need these utilities but neither should depend on the other. |
| 6 | + */ |
| 7 | + |
| 8 | +import type { TraceSpan } from "../../types/index.js"; |
| 9 | +import { colorTag } from "./markdown.js"; |
| 10 | + |
| 11 | +/** |
| 12 | + * Format a date string as a relative time label. |
| 13 | + * |
| 14 | + * - Under 60 minutes: "5m ago" |
| 15 | + * - Under 24 hours: "3h ago" |
| 16 | + * - Under 3 days: "2d ago" |
| 17 | + * - Otherwise: short date like "Jan 18" |
| 18 | + * |
| 19 | + * Returns a muted "—" when the input is undefined. |
| 20 | + * |
| 21 | + * @param dateString - ISO date string or undefined |
| 22 | + * @returns Human-readable relative time string |
| 23 | + */ |
| 24 | +export function formatRelativeTime(dateString: string | undefined): string { |
| 25 | + if (!dateString) { |
| 26 | + return colorTag("muted", "—"); |
| 27 | + } |
| 28 | + |
| 29 | + const date = new Date(dateString); |
| 30 | + const now = Date.now(); |
| 31 | + const diffMs = now - date.getTime(); |
| 32 | + const diffMins = Math.floor(diffMs / 60_000); |
| 33 | + const diffHours = Math.floor(diffMs / 3_600_000); |
| 34 | + const diffDays = Math.floor(diffMs / 86_400_000); |
| 35 | + |
| 36 | + let text: string; |
| 37 | + if (diffMins < 60) { |
| 38 | + text = `${diffMins}m ago`; |
| 39 | + } else if (diffHours < 24) { |
| 40 | + text = `${diffHours}h ago`; |
| 41 | + } else if (diffDays < 3) { |
| 42 | + text = `${diffDays}d ago`; |
| 43 | + } else { |
| 44 | + // Short date: "Jan 18" |
| 45 | + text = date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); |
| 46 | + } |
| 47 | + |
| 48 | + return text; |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Compute the duration of a span in milliseconds. |
| 53 | + * Prefers the API-provided `duration` field, falls back to timestamp arithmetic. |
| 54 | + * |
| 55 | + * @returns Duration in milliseconds, or undefined if not computable |
| 56 | + */ |
| 57 | +export function computeSpanDurationMs(span: TraceSpan): number | undefined { |
| 58 | + if (span.duration !== undefined && Number.isFinite(span.duration)) { |
| 59 | + return span.duration; |
| 60 | + } |
| 61 | + const endTs = span.end_timestamp || span.timestamp; |
| 62 | + if (endTs !== undefined && Number.isFinite(endTs)) { |
| 63 | + const ms = (endTs - span.start_timestamp) * 1000; |
| 64 | + return ms >= 0 ? ms : undefined; |
| 65 | + } |
| 66 | + return; |
| 67 | +} |
0 commit comments