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 weekdaysAhead is positive, no adjustment
  2. Target day is today, time hasn’t passeddaysAhead is 0, schedule for today
  3. Target day is today, time already passeddaysAhead 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.