feat(backend): resolve spoken deadlines to ISO dates
Jalali conversion is arithmetic here, not inference. A model asked to turn "۲۵ مهر" into ISO answers confidently and is often wrong, and @IsDateString() accepts the wrong answer — so the model emits a date intent and this decides what it means. Deviation from the spec, deliberately: the resolver takes todayIso rather than an IANA zone. Working in civil dates means nothing here reasons about instants. The zone is used one level up, where civilDateInZone() derives "today" from the actor's zone server-side — better than the spec's client-supplied date, which the client could set arbitrarily. Conventions pinned by tests: - "this <weekday>" is the soonest occurrence strictly after today, so "by Thursday" said on a Thursday means the next one; a deadline of today is almost never what was meant. "next" adds a further week. - month offsets clamp to the end of shorter months (31 Jan + 1 = 28/29 Feb) - a resolved date in the past, or more than five years out, is treated as unresolved however it was arrived at — an absolute date the model invented can land anywhere - no due date at all is not an error; an unparseable one is, and echoes what was heard so the review sheet can show it Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,3 +53,23 @@ export function civilDateJsWeekday(isoDate: string): number {
|
||||
const utcNoon = new Date(Date.UTC(y, m - 1, d, 12, 0, 0, 0));
|
||||
return utcNoon.getUTCDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Today's civil date (`YYYY-MM-DD`) in an IANA zone.
|
||||
*
|
||||
* Lets the server derive "today" from a client-supplied time zone instead of trusting a
|
||||
* client-supplied date, which matters for relative deadlines like "by Thursday".
|
||||
*/
|
||||
export function civilDateInZone(date: Date, timeZone: string): string {
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
}).formatToParts(date);
|
||||
|
||||
const year = parts.find((p) => p.type === 'year')?.value ?? '1970';
|
||||
const month = parts.find((p) => p.type === 'month')?.value ?? '01';
|
||||
const day = parts.find((p) => p.type === 'day')?.value ?? '01';
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user