How Claude HUD Calculates Session Duration and Tracks Elapsed Time

Claude HUD calculates session duration by extracting the first timestamp from the JSONL transcript, computing the delta against the current time via an injectable now callback, and rendering a human-readable string in the terminal status line.

Claude HUD is a terminal heads-up display for Claude sessions that provides real-time metadata about your conversation. Understanding how it tracks session duration requires examining the transcript parsing logic, the duration calculation algorithm, and the rendering pipeline implemented in the jarrodwatts/claude-hud repository.

Extracting the Session Start from Transcript Data

The duration tracking begins in src/transcript.ts within the processEntry function. As the JSONL transcript streams in, each line is parsed and examined. The system identifies the session start by capturing the first entry that contains a valid timestamp field.

When processEntry encounters the first timestamped entry, it converts the timestamp to a Date object and assigns it to result.sessionStart. This marks the exact moment the Claude session began according to the transcript metadata.

// src/transcript.ts - lines 84-88
const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
if (!result.sessionStart && entry.timestamp) {
  result.sessionStart = timestamp;   // First timestamp becomes session start
}

Computing Elapsed Time with formatSessionDuration

Once the session start time is established, the actual duration calculation occurs in src/index.ts through the formatSessionDuration function. This utility accepts the stored start Date and an optional now callback function, defaulting to Date.now() for production use.

The function calculates the delta between the session start and current time, then converts milliseconds into a human-readable format. For sessions under one minute, it returns <1m. For shorter sessions under an hour, it displays minutes only. Longer sessions show hours and remaining minutes (e.g., 2h 15m).

The injectable now parameter serves dual purposes: it enables deterministic unit testing by allowing fixed timestamps, and it supports alternative time sources if needed for specific deployment environments.

// src/index.ts - lines 96-109
export function formatSessionDuration(
  sessionStart?: Date,
  now: () => number = () => Date.now()
): string {
  if (!sessionStart) return '';
  const ms   = now() - sessionStart.getTime();
  const mins = Math.floor(ms / 60000);
  if (mins < 1) return '<1m';
  if (mins < 60) return `${mins}m`;
  const hours = Math.floor(mins / 60);
  const remainingMins = mins % 60;
  return `${hours}h ${remainingMins}m`;
}

Rendering the Duration in the Terminal Interface

The final step occurs in src/render/session-line.ts within the renderSessionLine function. After the duration string is computed and stored in the RenderContext (assigned in src/index.ts lines 74-76), the rendering pipeline checks the display configuration.

If display.showDuration is enabled (which it is by default unless explicitly disabled), the function appends the formatted duration to the status line components. The duration appears prefixed with a clock emoji and wrapped in dim styling to maintain the HUD's aesthetic hierarchy.

// src/render/session-line.ts - lines 200-202
if (display?.showDuration !== false && ctx.sessionDuration) {
  parts.push(dim(`⏱️  ${ctx.sessionDuration}`));
}

Practical Implementation Examples

Direct Duration Calculation

To compute a session duration manually using the same logic as Claude HUD:

import { formatSessionDuration } from './index';

// Session started at 2024-12-01T10:00:00Z
const start = new Date('2024-12-01T10:00:00Z');

// Calculate against current time
const duration = formatSessionDuration(start);
console.log(`Session elapsed: ${duration}`); // e.g., "1h 23m"

Testing with Fixed Time

For deterministic testing, inject a custom now callback:

const fakeNow = () => new Date('2024-12-01T12:15:00Z').getTime();
const duration = formatSessionDuration(start, fakeNow);
console.log(duration); // → "2h 15m"

HUD Output Example

When rendered in the terminal, the duration appears integrated into the status line:


[Opus] │ my-project git:(main*) ⏱️  2h 15m

Summary

  • Transcript Parsing: Claude HUD identifies the session start by extracting the first timestamp from the JSONL transcript in src/transcript.ts.
  • Duration Calculation: The formatSessionDuration function in src/index.ts computes elapsed time using an injectable now callback, supporting both production use and deterministic testing.
  • Human-Readable Format: Durations display as <1m, 45m, or 2h 15m based on elapsed time thresholds.
  • Terminal Rendering: The renderSessionLine function in src/render/session-line.ts conditionally appends the duration to the HUD when display.showDuration is enabled.

Frequently Asked Questions

How does Claude HUD determine when a session started?

Claude HUD determines the session start by parsing the JSONL transcript stream and capturing the first entry containing a timestamp field. This occurs in the processEntry function within src/transcript.ts, which converts the timestamp to a Date object and stores it as result.sessionStart.

Can I customize how the session duration is displayed?

The display format is controlled by the formatSessionDuration function in src/index.ts, which automatically selects between <1m, minutes-only, or hours-and-minutes formats based on elapsed time. While the formatting logic is hardcoded, you can disable the duration display entirely by setting display.showDuration to false in the configuration, which prevents renderSessionLine from appending the duration to the HUD.

Why does formatSessionDuration accept a now callback parameter?

The now callback parameter enables dependency injection for time retrieval, serving two primary purposes. First, it allows deterministic unit testing by letting tests inject fixed timestamps rather than relying on the actual system clock. Second, it provides flexibility for alternative time sources in specialized deployment environments. The parameter defaults to () => Date.now() for standard production usage.

Where is the session duration actually rendered in the terminal?

The session duration is rendered in src/render/session-line.ts within the renderSessionLine function. After checking that display.showDuration is not disabled and that ctx.sessionDuration contains a value, the function appends the formatted duration string—prefixed with a clock emoji (⏱️) and wrapped in dim styling—to the array of status line components before final output.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →