Root cause: backend returned naive ISO datetimes (no Z/offset) → JS parsed them as
browser local time → 3h drift for MSK users → '3ч' instead of 'только что'.
Backend:
- schemas/agent.py: add field_serializer('Z' suffix) for ConversationItem.updated_at
and MessageItem.created_at — naive datetimes serialized as UTC
- routes/agent_conversations.py: datetime.utcnow() → datetime.now(timezone.utc) (3x)
Frontend:
- New: stores/timezone.svelte.ts — global reactive appTimezone store
- dateFormat.ts: add parseDateUTC() (appends 'Z' to naive ISO), all format*()
functions now use parseDateUTC + { timeZone: appTimezone.current }
- ~25 files: replace new Date(apiString) → parseDateUTC(apiString),
add timeZone: appTimezone.current to toLocaleString()/toLocaleDateString()
- SystemSettings.svelte + HealthCenterModel sync appTimezone to global store
- ConversationList.svelte: fix relativeTime() and date grouping (the '3ч' bug)
Verified: backend schema test, frontend 2501 tests pass, build succeeds,
browser validation on /agent and /settings.