[Workaround] Date-Weekday Verification Hook — Prevents Claude from writing wrong weekdays
Problem
Claude consistently gets the day of the week wrong when writing dates. For example, writing "June 2 (Mon)" when June 2, 2026 is actually a Tuesday. This is a well-documented issue (see #17338, #24466, #11505, #24362) that has bitten many users.
This is not a one-off -- it happens reliably across conversations, devices, and contexts. LLMs fundamentally cannot calculate day-of-week because training data contains roughly equal probability for each weekday, so the model is essentially guessing.
Real-World Impact
In my case, I was applying for a position at a major AI company. Claude drafted my interview availability email with all 5 dates having the wrong weekday. The recruiter politely pointed out the mismatch. Embarrassing, but preventable.
My wife (who uses Claude for work in a different domain) independently confirmed: "Claude ALWAYS gets the days wrong."
Solution: A Claude Code Hook
I built a PreToolUse hook that intercepts Write, Edit, and evaluate_script calls, scans for date+weekday patterns, and blocks the tool call if any weekday is incorrect.
What it catches
| Pattern | Example | Language |
|---------|---------|----------|
| Full month name | June 2 (Tue) | English |
| Abbreviated month | Jun 2 (Tue) | English |
| Slash format | 6/2 (Tue) | English |
| Japanese | 6月2日(火) | Japanese |
How it works
- Regex extracts all date+weekday pairs from the content being written
- Node.js
new Date(year, month, day).getDay()computes the actual weekday - If claimed != actual -> exit code 2 (blocks the tool call) with a clear error message
- If all correct -> exit 0 (passes silently)
The hook code
Save as ~/.claude/hooks/date-weekday-verifier.js:
#!/usr/bin/env node
const MONTH_NAMES = {
january: 0, february: 1, march: 2, april: 3, may: 4, june: 5,
july: 6, august: 7, september: 8, october: 9, november: 10, december: 11,
jan: 0, feb: 1, mar: 2, apr: 3, jun: 5,
jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,
};
const EN_DAY_NAMES = {
sun: 0, sunday: 0, mon: 1, monday: 1, tue: 2, tuesday: 2, tues: 2,
wed: 3, wednesday: 3, thu: 4, thursday: 4, thur: 4, thurs: 4,
fri: 5, friday: 5, sat: 6, saturday: 6,
};
const JP_DAY_NAMES = {
'\u65e5': 0, '\u6708': 1, '\u706b': 2, '\u6c34': 3, '\u6728': 4, '\u91d1': 5, '\u571f': 6,
};
const DAY_LABELS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
function verify(content) {
const errors = [];
const year = new Date().getFullYear();
let match;
// "Month Day (Weekday)"
const p1 = /\b(january|february|march|april|may|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec)\s+(\d{1,2})\s*\((\w+)\)/gi;
while ((match = p1.exec(content)) !== null) {
const month = MONTH_NAMES[match[1].toLowerCase()];
const day = parseInt(match[2], 10);
const claimed = EN_DAY_NAMES[match[3].toLowerCase()];
if (month === undefined || claimed === undefined) continue;
const d = new Date(year, month, day);
if (d.getMonth() !== month || d.getDate() !== day) continue;
if (d.getDay() !== claimed)
errors.push({ text: match[0], claimed: DAY_LABELS[claimed], actual: DAY_LABELS[d.getDay()] });
}
// "M/D (Weekday)"
const p2 = /\b(\d{1,2})\/(\d{1,2})\s*\((\w+)\)/g;
while ((match = p2.exec(content)) !== null) {
const m = parseInt(match[1], 10) - 1, day = parseInt(match[2], 10);
const claimed = EN_DAY_NAMES[match[3].toLowerCase()];
if (claimed === undefined || m < 0 || m > 11) continue;
const d = new Date(year, m, day);
if (d.getMonth() !== m || d.getDate() !== day) continue;
if (d.getDay() !== claimed)
errors.push({ text: match[0], claimed: DAY_LABELS[claimed], actual: DAY_LABELS[d.getDay()] });
}
// Japanese "M\u6708D\u65e5(\u66dc\u65e5)"
const p3 = /(\d{1,2})\u6708(\d{1,2})\u65e5\s*[\uff08(]([\u65e5\u6708\u706b\u6c34\u6728\u91d1\u571f](?:\u66dc\u65e5?)?)[\uff09)]/g;
while ((match = p3.exec(content)) !== null) {
const m = parseInt(match[1], 10) - 1, day = parseInt(match[2], 10);
const claimed = JP_DAY_NAMES[match[3].charAt(0)];
if (claimed === undefined || m < 0 || m > 11) continue;
const d = new Date(year, m, day);
if (d.getMonth() !== m || d.getDate() !== day) continue;
if (d.getDay() !== claimed) {
const jp = ['\u65e5', '\u6708', '\u706b', '\u6c34', '\u6728', '\u91d1', '\u571f'];
errors.push({ text: match[0], claimed: jp[claimed], actual: jp[d.getDay()] });
}
}
return errors;
}
let input = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', (c) => { input += c; });
process.stdin.on('end', () => {
try {
const h = JSON.parse(input);
if (!['Write', 'Edit', 'mcp__chrome-devtools__evaluate_script'].includes(h.tool_name)) {
process.exit(0); return;
}
const ti = h.tool_input || {};
const content = ti.content || ti.new_string || ti.function || '';
if (!content || content.length < 5) { process.exit(0); return; }
const errors = verify(content);
if (!errors.length) { process.exit(0); return; }
const list = errors.map(e => ' WRONG: "' + e.text + '" -- claimed ' + e.claimed + ', actually ' + e.actual).join('\n');
console.error('DATE VERIFIER: ' + errors.length + ' wrong weekday(s)!\n\n' + list + '\n\nFix before proceeding.');
process.exit(2);
} catch { process.exit(0); }
});
Installation
Add to ~/.claude/settings.json under hooks.PreToolUse:
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "node \"~/.claude/hooks/date-weekday-verifier.js\"",
"timeout": 5
}
]
}
Test it
# Should BLOCK (wrong weekday):
echo '{"tool_name":"Write","tool_input":{"file_path":"t.md","content":"Meeting June 9 (Mon)"}}' \
| node ~/.claude/hooks/date-weekday-verifier.js
# Should PASS (correct weekday):
echo '{"tool_name":"Write","tool_input":{"file_path":"t.md","content":"Meeting June 9 (Tue)"}}' \
| node ~/.claude/hooks/date-weekday-verifier.js
Why this matters
Previous issues (#17338, #24466) were closed as duplicates without a user-facing solution. The model-level fix may take time, but users can protect themselves today with this hook. It is zero-config after installation and catches errors before they leave your machine.
This is especially critical for:
- Job applications and interview scheduling
- Business emails with meeting proposals
- Calendar integrations
- Any client-facing communication with dates
Environment
- Platform: Windows 11 (also works on Linux/macOS)
- Claude Code version: latest
- Node.js: v24+
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗