"""Timezone-aware datetime helpers. All datetimes in BanGUI are stored and transmitted in UTC. Conversion to the user's display timezone happens only at the presentation layer (frontend). These utilities provide a consistent, safe foundation for working with time throughout the backend. """ import datetime def utc_now() -> datetime.datetime: """Return the current UTC time as a timezone-aware :class:`datetime.datetime`. Returns: Current UTC datetime with ``tzinfo=datetime.UTC``. """ return datetime.datetime.now(datetime.UTC) def utc_from_timestamp(ts: float) -> datetime.datetime: """Convert a POSIX timestamp to a timezone-aware UTC datetime. Args: ts: POSIX timestamp (seconds since Unix epoch). Returns: Timezone-aware UTC :class:`datetime.datetime`. """ return datetime.datetime.fromtimestamp(ts, tz=datetime.UTC) def add_minutes(dt: datetime.datetime, minutes: int) -> datetime.datetime: """Return a new datetime that is *minutes* ahead of *dt*. Args: dt: The source datetime (must be timezone-aware). minutes: Number of minutes to add. May be negative. Returns: A new timezone-aware :class:`datetime.datetime`. """ return dt + datetime.timedelta(minutes=minutes) def is_expired(expires_at: datetime.datetime) -> bool: """Return ``True`` if *expires_at* is in the past relative to UTC now. Args: expires_at: The expiry timestamp to check (must be timezone-aware). Returns: ``True`` when the timestamp is past, ``False`` otherwise. """ return utc_now() >= expires_at def hours_ago(hours: int) -> datetime.datetime: """Return a timezone-aware UTC datetime *hours* before now. Args: hours: Number of hours to subtract from the current time. Returns: Timezone-aware UTC :class:`datetime.datetime`. """ return utc_now() - datetime.timedelta(hours=hours)