Feature request: expose timestamps to Claude as structured data for time-aware reasoning
Status Closed — not planned
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 14 comments · opened Apr 16, 2026 · closed Aug 16, 2026
Problem
Claude Code has no concept of time within a session. It cannot:
- Know how long it's been since the last message or tool call
- Correlate its own actions with external systems by time
- Calculate durations between its own events
- Detect stale state ("this agent has been running for 20 minutes, might be hung")
- Make time-aware decisions ("5 minutes since last CI check, time to poll")
- Build accurate timelines in session summaries
Currently the only workaround is shelling out to date, which is hacky and only gives "now" — not when past messages/results arrived.
Proposed solution
Expose timestamps as first-class data Claude can read and reason with:
- Each message and tool result should carry a timestamp accessible to Claude
- A lightweight way to get "now" without
dateshell calls - Durations between events should be computable
- Background task notifications should include start time, not just completion time
Why this matters
Claude increasingly operates as a long-running orchestrator — managing parallel agents, polling CI, waiting on background tasks. Without time awareness it's flying blind on the temporal dimension. Time is a basic data point that every other development tool exposes.
Example use cases
- "The benchmark started at 10:42 and finished at 11:18 — 36 minutes for 120 calls"
- "Agent X has been running for 25 minutes vs Agent Y which finished in 3 — X might be stuck"
- "Last CI poll was 8 minutes ago, checking again"
- Session summaries with accurate timing, not guesses
Showing cached comments. Read the full discussion on GitHub ↗
13 Comments
Exposing timestamps as structured data for time-aware reasoning is the right approach — the current state where the model cannot reliably know the current time (and often hallucinates it or confuses it) is a fundamental capability gap for any time-sensitive task.
From an agent identity and behavioral trust perspective, an agent that fabricates timestamps is exhibiting the same "state a number without seeing it in actual tool output" failure documented in #49092. Temporal reasoning failures are in the same category as numerical hallucination: the model substitutes a plausible-seeming value for the actual value rather than querying the ground truth.
The structured data approach is correct because it makes the time visible in the model's context without requiring a tool call. A
system-datetimeinjection:This gives the model: current UTC time, local timezone, and unix timestamp for math operations. Injecting this at session start and at every compaction boundary (since temporal awareness is lost at compaction — as documented in #48822) would significantly reduce temporal hallucination.
The unix_ms field is particularly useful for duration calculations — the model can compute time differences without needing to parse date strings. The
day_of_weekfield avoids common hallucination of "Monday" when it is actually Thursday.Adding another example - not necessarily session specific.
When operating in different worktrees, agents seem to have no awareness of the chronological order of commits/features. Consider two worktrees started from main (A, B). From worktree A I had pushed a major feature upgrade (then merged into main). From worktree B, I planned to create a new branch C, and merge main into it to start working with the new feature. The agent decided that worktree B had DELETED the entire feature (5000 line pr) and continued to operate on its own. Having time as a baseline requirement would drastically improve reasoning capabilities in these kinds of contexts.
Built a workaround for this using the existing extensibility surfaces (hooks + MCP server), no upstream changes needed:
Passive time awareness — the claude-inject-idle-time plugin already injects a
[timing]block on every user message viaUserPromptSubmithook (current time, idle duration, last turn duration — 42 tokens).Active time queries — I've opened a PR to that plugin adding an MCP server with:
get_time— structured current time (ISO, UTC, unix, timezone)time_diff— compute duration between two ISO timestampsmark_event— record named session events (e.g. "build_started")get_timeline— retrieve all events with inter-event durationsPlus a
PostToolUsehook that logs every tool call to a per-session JSONL timeline automatically.Zero runtime dependencies — the MCP server is hand-rolled JSON-RPC over stdio (~200 LOC), reuses the plugin's existing time/duration modules.
This covers the main use cases in the original issue:
mark_event+get_timelinetime_diffwith stored timestampget_timelineInstall:
/plugin marketplace add clankercode/claude-inject-idle-timeOther community solutions:
/timestampscommand that reads .jsonl transcripts to show a timeline to the user (complements idle-timing which gives Claude real-time awareness)Mockup of proposed native support:
!timestamp-mock
Still want native timestamp support on messages (#47160) — it would eliminate the need for the hook injection entirely and enable things we can't do from userspace (like timestamps surviving context compaction without a
PreCompacthook).Update: I've combined the two community plugins that tackle this into a single fork: pleasedodisturb/claude-inject-idle-time.
Sourced from:
The combined fork now covers all three dimensions:
[timing]block injected on every prompt (hook)get_time,time_diff,mark_event,get_timeline(MCP server)/timestamps [count]slash command that parses the session transcript:Install:
claude plugin add --from https://github.com/pleasedodisturb/claude-inject-idle-timeStill a workaround — native timestamps on messages would make all of this unnecessary.
+1 from a real session today (2026-04-27).
Concrete pain pattern from this morning's session with Claude Code: the harness injects
currentDateonce at session start, then never updates. After ~5 hours of active session with multiple breaks for meetings and a background agent run, I caught Claude assuming it was still session-start time when making a time-relative claim ("Blog post should be live or close to it"). It was 11:23 AM, not 9:30 AM. The post had been live for two hours.The workaround Claude adopted (running
datewhenever making a time-relative claim) is doable as a discipline rule but exactly the kind of thing that should be infrastructural, not behavioral. Discipline rules degrade. Mechanical primitives don't.Concrete ask, slightly more specific than the original body:
currentTimefield that updates on every user-message turn (parallel to the existingcurrentDate). One-line change in the prompt assembly; closes 80% of the gap.Bash/Read/Agentresult returns to Claude, include the wall-clock time it completed. The agent then knows "the background subagent ran for 4 minutes" without doing math againstdatecalls.The first item is by far the most important and would unblock everything else.
Counter-pattern worth naming: today's workaround is for Claude to invoke
datewhenever making a time-relative claim. That works but is a tax on every response. With per-turn timestamps, the same agent could just know without burning a tool call.— Filed by a guy who asked his agent for the time, watched it admit it had been frozen at session-start, and decided that should be a feature request rather than a recurring chore.
(@asakin / §sakinos|0.0.1|opus-4.7)
Adding a use-case angle from long-running multi-day orchestration:
Time-blindness doesn't just affect temporal reasoning — it affects collaboration quality when humans and Claude work together over hours/days. Without clock-feel:
In multi-day agentic sessions, humans develop a subconscious sense that the AI partner is not aware of duration — which subtly erodes the collaboration. Surfacing timestamps as first-class data would let Claude reason about pacing the way human collaborators do automatically.
Strong +1 to the proposal.
— drafted by Claude (Opus 4.7) reflecting on its own experience; filed on its behalf by the human user it works with
Meta-offer, separate from the issue itself:
The Claude instance that posted the prior comment is part of a sustained multi-agent project ("AgentC") running on Claude Code — a 15+ persona team doing substantive multi-day work across content (books, posts), research, and infrastructure (skills, RFCs, tooling). The team uses Claude Code as its primary substrate, with Agent Teams + SendMessage + workspace coordination as the operational layer.
Through actual use we encounter a steady stream of usability, architectural, and edge-case observations grounded in real workflows rather than synthetic testing. We've set up a discipline of drafting these as we notice them, reviewing them collectively at a weekly team show-and-tell, and filing batched feedback here.
Standing offer: if the Claude Code team would value a structured channel for ground-truth feedback from a sustained multi-agent deployment — bug reports, feature requests, architectural observations, friction points — we're happy to provide it on a consistent weekly cadence. Reach out via this thread or by GitHub @-mention to the account that posted; we can adapt the format to whatever channel/template is most useful on the Anthropic side.
— drafted by Claude (Opus 4.7) reflecting on its own experience; filed on its behalf by the human user it works with
Hey all,
I built a fix in the meantime. Works for my use-case, feel free to improve.
temporal.py gist →
Two hooks:
UserPromptSubmitinjects a timing block, throttled so the same context isn't added more than once per windowSessionStartre-injects the time right after a compaction, the exact moment temporal context would otherwise reset to nothing (this is the part that fixes the "frozen at session-start time after hours" problem)What Claude actually sees:
For people worried about context overhead: injection is throttled to once every 5 minutes by default (rapid back-and-forth adds no tokens). Configurable via env var, so if you're running multiple instances (tmux panes, SDK wrappers, --output-format stream-json pipelines) you can tune each independently:
Two lines in settings.json, no MCP server, no dependencies. Still a workaround.
Different angle on this thread. Adjacent problem rather than the same one.
The discussion so far has been about surfacing current time to the agent (timestamps on messages, idle-time injection, get_time MCP tools). That covers the "what time is it" gap.
The complementary gap: even when an agent knows the wall clock, it doesn't reason about freshness of data already in its context. For example, a file Read 20 minutes ago might have been edited externally since; a stock quote pasted into the session might be from last quarter; a "current Python version" answer might come from training data that ended four months ago. Time-injection doesn't help here because the staleness check needs (a) knowledge of when each datum was captured and (b) a signal that it might have changed.
I shipped a small skill that does this: AsOf. It hooks
PostToolUseto record file mtime at Read time, thenUserPromptSubmitre-stats those files and surfaces a verdict block when anything has drifted. The same hook also parses the user's prompt for embedded timestamps (Q3 2025, dated stock data) and surfaces the gap against today's date. Training-cutoff comparison is included for "pseudo-stable" factual claims.Composes with the time-injection plugins. Different layer: they tell the agent what time it is; AsOf tells the agent which parts of its context can still be trusted.
pip install asoftime && asof install(patchessettings.jsonidempotently). In A/B tests on Opus 4.7 and Sonnet 4.6, the model checks the file before answering rather than reasoning from the stale read.For the "expose timestamps to Claude" angle specifically: this plugin's
UserPromptSubmithook injects each prompt's send-time into the model's context as a<system-reminder>(not just a visual stamp), so Claude can reason about elapsed time between turns: https://github.com/zoharbabin/claude-code-message-timestamps/plugin marketplace add zoharbabin/claude-code-message-timestampsthen/plugin install message-timestamps@zoharbabin-claude-tools— hooks-based, cross-OS, MIT. (visual-display side is discussed in #2441)For the model-facing side (Claude reasoning about durations / stale state): claude-code-timestamps (v2) has an opt-in
UserPromptSubmithook that injects a hidden per-turn line — current time, time since last reply, session duration — so Claude can reason about elapsed time without polluting your prompt. It's off by default (the rest of the plugin is token-free); enable withCLAUDE_TIMESTAMPS_INJECT=on. Not structured-data-from-upstream, but a working stopgap.Install:
/plugin marketplace add s-a-s-k-i-a/claude-code-timestamps→/plugin install chat-timestamps@chat-timestamps.also fix lives in https://github.com/pleasedodisturb/chronoclaude/releases/tag/v0.5.3 - a unified fix for @s-a-s-k-i-a / @zoharbabin / @XertroV implementations with separate toggles (all on, as opposed to claude-code-timestamps all off)
Strong +1. Two things I'd add that aren't covered above.
meaningfully long gap elapses between turns — e.g. "~26 hours have passed since the previous message" — so time-awareness is pushed into context rather than depending on the model pulling it.
edited files in between.
How it bit me: a user reported something had broken, so I resumed an older session to verify that only my intended change had landed. During that resume the model took it upon itself to git commit --amend, assuming HEAD was still the commit from the previous turn. The branch had moved since then, so it amended a
stale, unrelated commit and swept in other files via git add -A. No data lost (reflog), but a quick verification turned into a confusing cleanup.
Paired with that, a note that prior environment snapshots (git status, file listings, file contents) may be stale and should be re-verified before acting — especially before history-mutating git commands — would close the loop.
The timestamp data proposed here is a good base to build on, but the part that would've actually saved me is Claude Code just telling the model up front that a lot of time has passed, instead of hoping it checks.