#31
8 days ago
🦄 Dates, Times, and LLMs
How do you make an LLM amazing at dates? Relative dates, absolute dates, timezones, all that madness. Let's talk dates, times, and all that goodness.
Project Details
Open in GitHubPractical recipe for turning squishy scheduling language into data you can ship: label the intent, carry the user's clock, let deterministic code do the math.
Episode Summary
- Broke scheduling language into three structures (
AbsoluteDate,RelativeDate,RecurringDate) so we know when to ask follow-up questions, when to compute offsets, and when to hand things to the cron parser. - Added an explicit
sourcedate to every prompt; the model no longer guesses what “next Friday” means. - Kept the model on labeling duty only; cron math, timezone lookups, and validation run in pure Python.
- Brian (Applied AI Lab) walked through their production guardrails: normalize timestamps before memory writes, reuse the user’s timezone everywhere, and only re-bucket recent memories when users move timezones.
What We Shipped
- BAML schema + regression tests covering absolute dates, relative durations, and recurring schedules.
- Prompt template that always includes a reference clock and captures any timezone hints from the user.
next_dayhelper that resolves cron expressions with a fallback timezone and fails fast on invalid input.- UX notes for agents: when a time component is missing, show a UI control or ask a follow-up instead of guessing.
Patterns Worth Reusing
- Always carry the clock. If you don’t pass “today” (and the user’s zone), relative strings drift.
- Schema drives behavior. Intent-specific types keep the LLM output explainable and let deterministic code branch cleanly.
- Timezones are user-facing. Default to the client’s zone unless the user typed one; store what they meant, not what the server runs on.
- Normalize once, reuse everywhere. Whether it’s memories or cron jobs, there’s no reason for each subsystem to redo timezone math.
Prompt + Tests in BAML
- The
ExtractDatesfunction captures every mention without performing arithmetic, keeping the LLM’s job limited to tagging intent and metadata.
class AbsoluteDate {
year int
month int
day int
time string?
}
class RelativeDate {
type "relative"
relative_date string @description(#"
use duration strings like P1D, etc
"#)
}
class RecurringDate {
type "recurring"
recurrence string @description(#"
use cron strings like "0 10 * * *" for every day at 10am
"#)
timezone string? @description(#"
only if explicitly provided
"#)
}
type Date = AbsoluteDate | RelativeDate | RecurringDate





























