Files
FabledScribe/src/fabledassistant/services/tz.py
T
bvandeusenandClaude Opus 4.7 d352e9264b feat(models): Moment + MomentEmbedding + junctions; rename Conversation.briefing_date → day_date
Also rename services/tz.user_briefing_date → user_day_date with a backwards
compat alias (briefing modules using the old name will be deleted in the
upcoming briefing tear-down stage). Update services/chat.py to_dict to use
day_date.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 20:40:02 -04:00

53 lines
2.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""User-timezone helpers.
All datetimes in the DB are stored as UTC. The helpers here bridge between
that UTC storage and the user's configured local timezone (IANA string in
the ``user_timezone`` setting). Use these anywhere the model or UI talks
in terms of "today", "tomorrow", or a bare calendar date — never
``date.today()`` or ``datetime.now(timezone.utc)`` inside a per-user flow.
"""
from __future__ import annotations
from datetime import date, datetime, timedelta
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from fabledassistant.services.settings import get_setting
# Day-rollover boundary for journal/day-anchored views. The "day" flips at
# this local hour (not midnight) so the 00:0004:00 local window still
# shows yesterday's content until the user "starts" the new day.
DAY_ROLLOVER_HOUR = 4
# Backwards-compat alias for code paths still referencing the old name.
BRIEFING_DAY_START_HOUR = DAY_ROLLOVER_HOUR
async def get_user_tz(user_id: int) -> ZoneInfo:
"""Return the user's IANA ``ZoneInfo``, falling back to UTC."""
tz_str = await get_setting(user_id, "user_timezone") or "UTC"
try:
return ZoneInfo(tz_str)
except (ZoneInfoNotFoundError, KeyError):
return ZoneInfo("UTC")
async def user_today(user_id: int) -> date:
"""Return today's calendar date in the user's local timezone."""
tz = await get_user_tz(user_id)
return datetime.now(tz).date()
async def user_day_date(user_id: int) -> date:
"""Return the current "day" in the user's local timezone.
The day flips at ``DAY_ROLLOVER_HOUR`` (4am local) rather than midnight,
so the 00:0004:00 local window still returns *yesterday* — the user's
journal stays anchored to yesterday until they cross the rollover.
"""
tz = await get_user_tz(user_id)
return (datetime.now(tz) - timedelta(hours=DAY_ROLLOVER_HOUR)).date()
# Backwards-compat alias used by briefing-only modules being torn down in Stage B.
user_briefing_date = user_day_date