# How Claude HUD Calculates Session Duration and Tracks Elapsed Time

> Discover how Claude HUD calculates session duration and tracks elapsed time using timestamps and a flexible now callback for precise terminal status line rendering.

- Repository: [Jarrod Watts/claude-hud](https://github.com/jarrodwatts/claude-hud)
- Tags: internals
- Published: 2026-03-18

---

**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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.

```typescript
// 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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.

```typescript
// 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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.

```typescript
// 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:

```typescript
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:

```typescript
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`](https://github.com/jarrodwatts/claude-hud/blob/main/src/transcript.ts).
- **Duration Calculation**: The `formatSessionDuration` function in [`src/index.ts`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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`](https://github.com/jarrodwatts/claude-hud/blob/main/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.