[BUG] /insights "Messages by Time of Day" chart applies a hardcoded +8 (PT) offset to already-local data

Open 💬 0 comments Opened Jun 22, 2026 by LSAndrad

Preflight

  • The previously-reported issue #23269 ("Insights report hardcodes PT timezone instead of using system timezone") was closed as not planned (stale) and locked, so I cannot comment there. The bot itself asks to file a new issue referencing it. This report adds the concrete root cause that #23269 lacked.
  • This is a single bug report.
  • Reproduced on the latest version (2.1.186).

What's Wrong?

The /insights "User Messages by Time of Day" chart shows wrong buckets for any user not in Pacific Time. For me (UTC-3, America/Sao_Paulo) it claimed most of my activity was at night, which is false — I work mornings/afternoons.

Everything below is reproduced from the generated report.html that /insights writes to ~/.claude/usage-data/, plus observable behavior. The root cause is a double, contradictory timezone assumption between how the raw data is bucketed and how the chart's timezone selector interprets it:

1. The raw histogram is already in the user's local timezone. This is directly observable: a message I sent at 18:32 BRT (stored in my session transcript as 2026-06-22T21:32:00Z) lands in the 18:00 bucket of rawHourCounts — i.e. the hour is bucketed in my local TZ (UTC-3), not in UTC (would be 21) and not in PT (would be 13). So rawHourCounts is already local.

2. But the chart selector assumes the raw data is in PT (UTC-8). The custom-offset handler in the generated report.html does:

document.getElementById('custom-offset').addEventListener('change', function() {
  const parsed = parseInt(this.value, 10);
  if (isNaN(parsed)) return;
  updateHourHistogram(parsed + 8);   // <-- assumes raw is PT; adds +8
});

And updateHourHistogram(offsetFromPT) shifts every hour by offsetFromPT:

const newHour = (parseInt(hour) + offsetFromPT + 24) % 24;

The preset <option> values in the same report.html encode the same PT anchor: PT=0, ET=3, London=8, CET=9, Tokyo=17 — i.e. value = utcOffset - (-8).

So when I (correctly) enter my real offset -3, it computes updateHourHistogram(-3 + 8 = +5) and shifts my already-local data forward by 5 hours, pushing morning/afternoon activity into evening/night.

The two premises are incompatible: the data is bucketed in local time, but the UI math is anchored to PT. They only agree for a user physically in PT.

What Should Happen?

The chart should display the user's actual local time with no shift, OR the selector should be consistent with how the data was bucketed. Concretely, either:

  • bucket rawHourCounts in UTC and let the selector apply the true offset, or
  • keep local-time bucketing and make the default/"no shift" option mean "my local time" instead of anchoring to PT (drop the +8).

Steps to Reproduce

  1. Set system timezone to anything other than PT (mine: America/Sao_Paulo, UTC-3).
  2. Run /insights.
  3. Open the generated report and look at "User Messages by Time of Day".
  4. In the timezone selector, enter your real UTC offset as a Custom offset (e.g. -3).
  5. The buckets are shifted by (yourOffset - (-8)) hours — for UTC-3 that's +5h — inflating Night/Evening.

Verification with my real data (rawHourCounts taken verbatim from the generated report.html):

rawHourCounts = {"0":3,"2":5,"7":6,"8":77,"9":112,"10":198,"11":88,"12":13,
                 "13":35,"14":78,"15":100,"16":132,"17":89,"18":78,"19":28,
                 "20":14,"21":70,"22":62,"23":26}
  • Custom offset -3updateHourHistogram(5) → Morning 5, Afternoon 494, Evening 512, Night 203 (what the UI shows me — wrong).
  • No shift (preset "PT", or custom -8updateHourHistogram(0)) → Morning 481, Afternoon 447, Evening 278, Night 8 (my actual local activity — correct).

The "magic" value that fixes it is -8 (cancels the hardcoded +8), not my real offset -3 — which is the bug.

Is this a regression?

I don't know — the time-of-day chart appears to have shipped with this behavior. Same as #23269 (reported Feb 2026, MST user).

Claude Code Version

2.1.186 (Claude Code).

Platform

Anthropic API

Operating System

Other Linux (Ubuntu-based, kernel 6.17, America/Sao_Paulo UTC-3)

Additional Information

Related: #23269 (closed as stale/locked, never fixed). This report supersedes it with the concrete root cause.

View original on GitHub ↗