Natural Language Date Parsing: The Same-Day Weekday Bug

When your reminder parser sees '~wed 10pm' on a Wednesday, it schedules next week instead of today. A one-character fix.

The Problem

@Obi has a reminder parser that accepts natural language input like ~wed 10pm to schedule tasks in Vikunja. On a Wednesday, the command ~wed 10pm was scheduling the reminder for the following Wednesday — seven days later — instead of today at 10pm.

Why This Happens

The standard day-of-week calculation looks like this:

// Before — buggy
const dayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
const targetDay = dayMap[dayInput.toLowerCase()];
const today = new Date().getDay();

let daysAhead = targetDay - today;
if (daysAhead <= 0) daysAhead += 7;  // BUG: <= 0 treats today as "past"

const reminderDate = new Date();
reminderDate.setDate(reminderDate.getDate() + daysAhead);

When targetDay equals today, daysAhead is 0. The check <= 0 catches it and adds 7, pushing the date to next week.

This is a common pattern in any system that parses weekday references — “next Monday,” “this Friday,” reminder schedulers, cron-like natural language parsers. The <= 0 guard is intended to prevent scheduling in the past, but it’s too aggressive.

The Fix

Change <= 0 to < 0, then add a second check for same-day-but-time-passed:

// After — correct
const dayMap = { sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6 };
const targetDay = dayMap[dayInput.toLowerCase()];
const today = new Date().getDay();

let daysAhead = targetDay - today;
if (daysAhead < 0) daysAhead += 7;  // Only wrap if target day is earlier in the week

// Same day, but time already passed — advance to next week
if (daysAhead === 0) {
  const now = new Date();
  const targetTime = new Date();
  targetTime.setHours(parsedHour, parsedMinute, 0, 0);
  if (now > targetTime) {
    daysAhead = 7;
  }
}

const reminderDate = new Date();
reminderDate.setDate(reminderDate.getDate() + daysAhead);
reminderDate.setHours(parsedHour, parsedMinute, 0, 0);

The logic now handles three cases:

  1. Target day is later this week — daysAhead is positive, no adjustment
  2. Target day is today, time hasn’t passed — daysAhead is 0, schedule for today
  3. Target day is today, time already passed — daysAhead becomes 7, schedule for next week

Key Takeaway

Any time you’re parsing weekday references into dates, the same-day case needs its own branch. The <= 0 to < 0 change is one character, but the difference is a week. Test your date parser on the current day of the week — it’s the case most developers forget.