Spaces:
Configuration error
Configuration error
| """Shared helpers for working with the user's preferred timezone.""" | |
| from __future__ import annotations | |
| from datetime import datetime, timezone | |
| from typing import Optional | |
| from zoneinfo import ZoneInfo, ZoneInfoNotFoundError | |
| from ..logging_config import logger | |
| from ..services.timezone_store import get_timezone_store | |
| UTC = timezone.utc | |
| def get_user_timezone_name(default: str = "UTC") -> str: | |
| """Return the stored timezone preference or a default.""" | |
| store = get_timezone_store() | |
| return store.get_timezone(default) | |
| def resolve_user_timezone(default: str = "UTC") -> ZoneInfo: | |
| """Resolve the stored timezone to a ZoneInfo, falling back to default on error.""" | |
| tz_name = get_user_timezone_name(default) | |
| try: | |
| return ZoneInfo(tz_name) | |
| except ZoneInfoNotFoundError: | |
| logger.warning( | |
| "unknown timezone; defaulting to %s", | |
| default, | |
| extra={"timezone": tz_name}, | |
| ) | |
| except Exception as exc: # pragma: no cover - defensive | |
| logger.warning( | |
| "timezone resolution failed; defaulting to %s", | |
| default, | |
| extra={"error": str(exc)}, | |
| ) | |
| return ZoneInfo(default) | |
| def now_in_user_timezone(fmt: Optional[str] = None, *, default: str = "UTC") -> datetime | str: | |
| """Return the current time in the user's timezone. | |
| When *fmt* is provided, the result is formatted using ``datetime.strftime``; | |
| otherwise the aware ``datetime`` object is returned. | |
| """ | |
| current = datetime.now(resolve_user_timezone(default)) | |
| if fmt is None: | |
| return current | |
| return current.strftime(fmt) | |
| def convert_to_user_timezone(dt: datetime, *, default: str = "UTC") -> datetime: | |
| """Convert *dt* into the user's timezone with UTC fallback.""" | |
| if dt.tzinfo is None: | |
| dt = dt.replace(tzinfo=UTC) | |
| tz = resolve_user_timezone(default) | |
| try: | |
| return dt.astimezone(tz) | |
| except Exception as exc: # pragma: no cover - defensive | |
| logger.warning( | |
| "timezone conversion failed; defaulting to %s", | |
| default, | |
| extra={"error": str(exc)}, | |
| ) | |
| return dt.astimezone(ZoneInfo(default)) | |
| __all__ = [ | |
| "UTC", | |
| "convert_to_user_timezone", | |
| "get_user_timezone_name", | |
| "now_in_user_timezone", | |
| "resolve_user_timezone", | |
| ] | |