Portal Community

Timing Fields

FieldWhen AvailableFormatExample
StartedAfter WorkflowExecutionStartedLocal time: HH:MM:SS14:03:27
CompletedAfter terminal eventLocal time: HH:MM:SS14:05:52
Duration (running)During executionLive counter: Xm Ys2m 25s
Duration (final)After terminal eventFixed: Xm Ys (or Xh Ym Zs)2m 25s

Human-Friendly Duration Formatting

// formatDuration utility
export function formatDuration(ms: number): string {
  if (ms < 1000) return `${ms}ms`;
  const s = Math.floor(ms / 1000);
  if (s < 60) return `${s}s`;
  const m = Math.floor(s / 60);
  const rem = s % 60;
  if (m < 60) return rem > 0 ? `${m}m ${rem}s` : `${m}m`;
  const h = Math.floor(m / 60);
  const remM = m % 60;
  return remM > 0 ? `${h}h ${remM}m` : `${h}h`;
}

// Examples:
// 450ms     → "450ms"
// 5000ms    → "5s"
// 83000ms   → "1m 23s"
// 3660000ms → "1h 1m"

Live Counter Implementation

// Inside ExecutionStatusTabContent
const startedAt = useFlowObserverPanelStore(s => s.executionStartedAt);
const status = useFlowObserverPanelStore(s => s.executionStatus);

const [elapsed, setElapsed] = useState(0);

useEffect(() => {
  if (status !== 'running' || !startedAt) return;

  const interval = setInterval(() => {
    setElapsed(Date.now() - new Date(startedAt).getTime());
  }, 1000);

  return () => clearInterval(interval);
}, [status, startedAt]);
Duration Source The final totalDurationMs value comes from the WorkflowExecutionCompleted event payload — it is measured server-side and is the authoritative duration. The client-side live counter is only used while the execution is running and is replaced by the server value on completion.