The CR-hardcoded today broke two scenarios: the owner traveling, and
any future non-CR user. Now the provider sends the browser's IANA
timezone (X-Client-Timezone) with every CopilotKit request — the
runtime forwards x-* headers to the agent on its own — and the backend
binds it to a per-request ContextVar next to the DB session.
get_current_date and the future-cuota bounds use it; the tool also
reports the timezone so the model can echo it. Unknown or absent zones
(n8n posts, tests) fall back to Costa Rica; comma-joined duplicate
header values are tolerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two bugs from prod (server clock is UTC, user is UTC-6):
- get_recent_transactions returned future-dated Tasa Cero cuotas; it now
excludes anything dated after the user's today, same rule as
/transactions/recent.
- "¿Cuánto he gastado hoy?" answered for the wrong day: the prompt baked
date.today() in at boot — frozen from startup AND in server-UTC (8 pm
in Costa Rica is already tomorrow in UTC). The prompt no longer carries
a date; a new get_current_date tool returns today in CR time plus the
active billing cycle, and the prompt directs the model to call it for
any relative date reference. today_cr() lives in timeutil (CR has no
DST, fixed UTC-6).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 29 call sites now use app.timeutil.utcnow(), which returns naive
UTC via the non-deprecated API. Semantics are unchanged on purpose:
every datetime column is TIMESTAMP WITHOUT TIME ZONE, so going aware
here would poison naive/aware comparisons. The TIMESTAMPTZ migration
(Phase 2) now has a single place to change. (BE-01)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>