fix: convert calendar event times to user timezone in briefing

The briefing was formatting event start_dt directly in UTC instead of
converting to the user's local timezone. Also, the day_start/day_end
query window was naive (UTC), so events at the edges of the user's day
could be missed or included incorrectly.

Now reads user_timezone setting, uses it for today's date boundary and
for converting each event's start_dt before formatting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 06:34:22 -04:00
parent aab478359b
commit 383a4430f1
@@ -8,6 +8,7 @@ import asyncio
import hashlib import hashlib
import logging import logging
from datetime import date, datetime, timezone from datetime import date, datetime, timezone
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
import httpx import httpx
@@ -129,7 +130,13 @@ async def _gather_internal(user_id: int) -> dict:
from fabledassistant.services.projects import list_projects from fabledassistant.services.projects import list_projects
from fabledassistant.services.caldav import is_caldav_configured, list_events from fabledassistant.services.caldav import is_caldav_configured, list_events
today = date.today().isoformat() tz_name = await get_setting(user_id, "user_timezone") or "UTC"
try:
user_tz = ZoneInfo(tz_name)
except ZoneInfoNotFoundError:
user_tz = ZoneInfo("UTC")
today = datetime.now(user_tz).date().isoformat()
# Tasks: overdue, due today, high priority in-progress # Tasks: overdue, due today, high priority in-progress
all_tasks: list[dict] = [] all_tasks: list[dict] = []
@@ -166,9 +173,9 @@ async def _gather_internal(user_id: int) -> dict:
calendar_events: list[str] = [] calendar_events: list[str] = []
try: try:
from fabledassistant.services.events import list_events as list_internal_events from fabledassistant.services.events import list_events as list_internal_events
today_date = date.today() today_date = datetime.now(user_tz).date()
day_start = datetime(today_date.year, today_date.month, today_date.day, 0, 0, 0) day_start = datetime(today_date.year, today_date.month, today_date.day, 0, 0, 0, tzinfo=user_tz)
day_end = datetime(today_date.year, today_date.month, today_date.day, 23, 59, 59) day_end = datetime(today_date.year, today_date.month, today_date.day, 23, 59, 59, tzinfo=user_tz)
internal_events = await list_internal_events( internal_events = await list_internal_events(
user_id=user_id, date_from=day_start, date_to=day_end user_id=user_id, date_from=day_start, date_to=day_end
) )
@@ -176,7 +183,8 @@ async def _gather_internal(user_id: int) -> dict:
if e.all_day: if e.all_day:
time_str = "all day" time_str = "all day"
elif e.start_dt: elif e.start_dt:
time_str = e.start_dt.strftime("%-I:%M %p") local_dt = e.start_dt.astimezone(user_tz) if e.start_dt.tzinfo else e.start_dt.replace(tzinfo=timezone.utc).astimezone(user_tz)
time_str = local_dt.strftime("%-I:%M %p")
else: else:
time_str = "unknown time" time_str = "unknown time"
calendar_events.append(f"{e.title} at {time_str}") calendar_events.append(f"{e.title} at {time_str}")