DEV Community

DavidAI311
DavidAI311

Posted on

Claude Wrote the Wrong Weekday on All 5 Dates. In an Interview Email.

I got invited to interview at a major AI company.

I asked Claude Code to draft my reply with available time slots. Claude produced five neatly formatted dates with weekday labels and time ranges.

Looked perfect. I hit send.

Hours later, the recruiter replied politely:

"June 2nd is a Tuesday, not a Monday. Could you please double-check your availability?"

All five weekday labels were wrong.


Why LLMs Cannot Calculate Day-of-Week

This is not a Claude-specific bug. It happens with GPT, Gemini, and every other LLM.

The reason is fundamental. An LLM is a statistical next-token predictor, not a calendar. Training data contains sentences like "Today is Monday," "Today is Tuesday," etc. with roughly equal frequency. For the model, each weekday has approximately 1/7 probability — it is essentially rolling a die.

Concept Analogy
LLM weekday calculation Rolling a 7-sided die
new Date().getDay() Looking at a calendar
A Hook Checking the calendar before Claude rolls the die

This is well-documented on GitHub:

  • #17338 — "Claude always uses wrong weekdays"
  • #24466 — "consistently off by one day"
  • #2618 — "A date tool should be included by default"

Multiple people I know have independently confirmed: Claude always gets the days wrong.


The Fix: A Date-Weekday Verification Hook

Claude Code's Hooks system lets you intercept and validate content before Claude writes it to disk.

This hook:

  1. Intercepts Write and Edit tool calls
  2. Scans content for date + weekday patterns using regex
  3. Computes the actual weekday using new Date().getDay()
  4. If claimed ≠ actual → blocks the tool call (exit code 2)

Supported Patterns

Format Example Language
Full month June 2 (Tue) English
Abbreviated Jun 2 (Tue) English
Slash format 6/2 (Tue) English
Japanese 6月2日(火) Japanese

Installation

Step 1: Save the Hook

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 DAY_LABELS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

function verify(content) {
  const errors = [];
  const year = new Date().getFullYear();
  let match;

  // "Month Day (Weekday)" e.g. "June 2 (Mon)"
  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)" e.g. "6/2 (Mon)"
  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()] });
  }
  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'].includes(h.tool_name)) { process.exit(0); return; }
    const ti = h.tool_input || {};
    const content = ti.content || ti.new_string || '';
    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); }
});
Enter fullscreen mode Exit fullscreen mode

Step 2: Register in settings.json

Add to ~/.claude/settings.json under hooks.PreToolUse:

{
  "matcher": "Write|Edit",
  "hooks": [
    {
      "type": "command",
      "command": "node \"~/.claude/hooks/date-weekday-verifier.js\"",
      "timeout": 5
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Test

# 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
Enter fullscreen mode Exit fullscreen mode

What It Looks Like in Action

When Claude tries to write a wrong weekday:

DATE VERIFIER: 1 wrong weekday(s)!

  WRONG: "June 9 (Mon)" -- claimed Mon, actually Tue

Fix before proceeding.
Enter fullscreen mode Exit fullscreen mode

The tool call is blocked. Claude automatically corrects the weekday and retries.

A fun side effect: while writing this article, the hook detected the intentional wrong-date examples in the code blocks and blocked the file write. I had to bypass my own trap.


Before / After

Before (no Hook) After (with Hook)
Weekday accuracy Dice roll (1/7) 100% correct
Discovery Pointed out by the other party Blocked before writing
Fix cost Embarrassing correction email Automatic
Languages English + Japanese

Beyond Weekdays

This pattern extends far beyond date verification. By swapping the regex, you can validate:

  • Phone number formats
  • Email address validity
  • Currency amounts and decimal places
  • Address formatting

Anything Claude should verify before writing can be enforced with a Hook.

GitHub Issue with full code: anthropics/claude-code #63098

Top comments (0)