fix: timezone handling across fullstack — UTC parsing + display in configured TZ
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.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
# #region AgentChat.Api.Conversations [C:3] [TYPE Module] [SEMANTICS agent-chat,api,rest]
|
||||
# @defgroup AgentChat REST routes for conversation lifecycle.
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -170,10 +170,10 @@ async def save_conversation(
|
||||
id=body.conversation_id,
|
||||
user_id=user_id,
|
||||
title=body.title or "",
|
||||
created_at=datetime.utcnow(),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(conv)
|
||||
conv.updated_at = datetime.utcnow()
|
||||
conv.updated_at = datetime.now(timezone.utc)
|
||||
if body.title:
|
||||
conv.title = body.title
|
||||
|
||||
@@ -196,7 +196,7 @@ async def save_conversation(
|
||||
state=msg_data.get("state"),
|
||||
tool_calls=msg_data.get("tool_calls"),
|
||||
attachments=msg_data.get("attachments"),
|
||||
created_at=datetime.utcnow(),
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(msg)
|
||||
db.flush()
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, field_serializer
|
||||
|
||||
|
||||
def _serialize_datetime(v: datetime) -> str:
|
||||
"""Serialize datetime as ISO 8601 with Z suffix. Naive datetimes are treated as UTC."""
|
||||
if v.tzinfo is None:
|
||||
return v.strftime("%Y-%m-%dT%H:%M:%S") + "Z"
|
||||
return v.isoformat()
|
||||
|
||||
|
||||
# #region Schemas.Agent.ConversationItem [C:1] [TYPE Class] [SEMANTICS agent,schema,conversation]
|
||||
@@ -18,6 +25,8 @@ class ConversationItem(BaseModel):
|
||||
has_tool_calls: bool = False # conversation includes tool usage
|
||||
has_error: bool = False # conversation has error state
|
||||
risk_level: str | None = None # "safe" | "guarded" | "dangerous"
|
||||
|
||||
_serialize_updated_at = field_serializer("updated_at")(_serialize_datetime)
|
||||
# #endregion Schemas.Agent.ConversationItem
|
||||
|
||||
|
||||
@@ -63,6 +72,8 @@ class MessageItem(BaseModel):
|
||||
tool_calls: list[ToolCall] | None = None
|
||||
attachments: list[AttachmentMeta] | None = None
|
||||
created_at: datetime
|
||||
|
||||
_serialize_created_at = field_serializer("created_at")(_serialize_datetime)
|
||||
# #endregion Schemas.Agent.MessageItem
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user