The Problem
Claude Code is stateless about time. The system prompt injects today's date exactly once when the session starts—like a clock that stops running the moment you open the app.
So it guesses. You ask Claude to "schedule something for this evening" and it might already be past midnight. You get "good morning" greetings at 4pm. It suggests "tomorrow morning" for something you need right now. The delta between what Claude thinks the time is and what it actually is grows wider the longer your session runs.
I've been building CAST—a multi-agent Claude Code framework with 30+ agents and an elaborate hook pipeline—and this time problem kept bubbling up in subtle ways. Scheduling decisions were off. Generated emails were time-stamped wrong. Suggestions assumed dayparts that had already passed.
The fix should be simple, right? Just tell Claude what time it is.
And it was.
The Fix
# At session start, Claude now sees:
## Session Time Context
Date: Tuesday, 2026-05-05
Time: 13:49 EDT
Timezone: EDT (UTC-4)
Day type: weekday
Time of day: afternoon
Session started: 2026-05-05T17:49:00Z (epoch: 1746467340)
No commands. No slash-command integration. No behavior change. Claude just knows.
How It Works
Claude Code has a hooks system—shell commands that fire on lifecycle events (SessionStart, PreToolUse, PostToolUse, etc.) and can inject additional context via JSON.
The hook I built is a SessionStart hook. When you open a new Claude Code session, it runs automatically and emits structured time data. Here's the interesting part of the script:
# Gather time data (all via date, no external deps)
LOCAL_DATE="$(date '+%Y-%m-%d')"
LOCAL_TIME="$(date '+%H:%M')"
TZ_ABBREV="$(date '+%Z')"
HOUR=$((10#$(date '+%H')))
# Semantic bucket: which part of the day is it?
if (( HOUR >= 7 && HOUR <= 11 )); then BUCKET="morning"
elif (( HOUR == 12 )); then BUCKET="midday"
elif (( HOUR >= 13 && HOUR <= 16 )); then BUCKET="afternoon"
elif (( HOUR >= 17 && HOUR <= 20 )); then BUCKET="evening"
else BUCKET="night"
fi
Then emit via JSON:
python3 -c '
import json, os
lines = [
"## Session Time Context",
"",
"Date: " + os.environ["CAST_TC_FULL_DATE"],
"Time: " + os.environ["CAST_TC_LOCAL_TIME"] + " " + os.environ["CAST_TC_TZ_ABBREV"],
"Timezone: " + os.environ["CAST_TC_TZ_ABBREV"] + " (" + os.environ["CAST_TC_UTC_LABEL"] + ")",
"Day type: " + os.environ["CAST_TC_DAY_TYPE"],
"Time of day: " + os.environ["CAST_TC_BUCKET"],
"Session started: " + os.environ["CAST_TC_ISO_UTC"] + " (epoch: " + os.environ["CAST_TC_EPOCH"] + ")",
]
context_text = "\n".join(lines)
output = {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": context_text
}
}
print(json.dumps(output))
'
No network. No external dependencies. No telemetry. It just runs date and Python's stdlib json module. Exit 0 always—the hook must never block the session.
The hook ID is registered in ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [{
"id": "cast-time-context",
"hooks": [{
"type": "command",
"command": "bash ~/.claude/scripts/cast-time-context-hook.sh",
"timeout": 3
}]
}]
}
}
Install merges this into your existing config automatically, backing up the original so it's safe to run on an existing setup.
Install
With Homebrew:
brew tap ek33450505/cast-time
brew install cast-time
bash $(brew --prefix cast-time)/install.sh
Without Homebrew:
git clone https://github.com/ek33450505/cast-time.git
cd cast-time
bash install.sh
Runs once per session. No configuration.
The Meta-Observation
I've been building CAST for a while now. It's the most complex thing I've shipped—30+ agents, an 81-hook lifecycle pipeline, a 26-table SQLite observability schema. I'm genuinely proud of it. It's also the thing that takes the longest to explain.
cast-time is the opposite. The README fits in a terminal window. The problem is legible in one sentence: "Claude doesn't know what time it is." The fix is invisible to anyone who installs it. Install takes 30 seconds.
And cast-time is spreading faster than CAST.
I think the reason is that big capable things require buy-in before they demonstrate value. You have to understand the architecture to see why it's useful. Small things prove themselves immediately. cast-time doesn't ask you to understand anything—it just fixes an annoyance you've experienced a dozen times.
The pattern might generalize for anyone building ecosystem tooling on top of Claude Code or similar platforms: lead with the smallest demonstration of your idea. Something that does one thing instantly and visibly. Something that just works. Once someone has that small win and feels it, they'll follow you deeper into bigger ideas.
I'm building a few more things in this vein—standalone hooks that each fix one obvious problem. cast-time is the first, and I think it's why people are actually using it.
Links
- GitHub: ek33450505/cast-time
-
Homebrew tap:
ek33450505/cast-time - CAST (the multi-agent framework): ek33450505/claude-agent-team
If you're building anything on top of Claude Code's hook system, I'd love to hear about it. Drop a comment or find me on GitHub.
Top comments (0)