""" Timezone utilities for Terra-View. Provides consistent timezone handling throughout the application. All database times are stored in UTC; this module converts for display. """ from datetime import datetime from zoneinfo import ZoneInfo from typing import Optional from backend.database import SessionLocal from backend.models import UserPreferences # Default timezone if none set DEFAULT_TIMEZONE = "America/New_York" def get_user_timezone() -> str: """ Get the user's configured timezone from preferences. Returns: Timezone string (e.g., "America/New_York") """ db = SessionLocal() try: prefs = db.query(UserPreferences).filter_by(id=1).first() if prefs and prefs.timezone: return prefs.timezone return DEFAULT_TIMEZONE finally: db.close() def get_timezone_info(tz_name: str = None) -> ZoneInfo: """ Get ZoneInfo object for the specified or user's timezone. Args: tz_name: Timezone name, or None to use user preference Returns: ZoneInfo object """ if tz_name is None: tz_name = get_user_timezone() try: return ZoneInfo(tz_name) except Exception: return ZoneInfo(DEFAULT_TIMEZONE) def utc_to_local(dt: datetime, tz_name: str = None) -> datetime: """ Convert a UTC datetime to local timezone. Args: dt: Datetime in UTC (naive or aware) tz_name: Target timezone, or None to use user preference Returns: Datetime in local timezone """ if dt is None: return None tz = get_timezone_info(tz_name) # Assume naive datetime is UTC if dt.tzinfo is None: dt = dt.replace(tzinfo=ZoneInfo("UTC")) return dt.astimezone(tz) def local_to_utc(dt: datetime, tz_name: str = None) -> datetime: """ Convert a local datetime to UTC. Args: dt: Datetime in local timezone (naive or aware) tz_name: Source timezone, or None to use user preference Returns: Datetime in UTC (naive, for database storage) """ if dt is None: return None tz = get_timezone_info(tz_name) # Assume naive datetime is in local timezone if dt.tzinfo is None: dt = dt.replace(tzinfo=tz) # Convert to UTC and strip tzinfo for database storage return dt.astimezone(ZoneInfo("UTC")).replace(tzinfo=None) def format_local_datetime(dt: datetime, fmt: str = "%Y-%m-%d %H:%M", tz_name: str = None) -> str: """ Format a UTC datetime as local time string. Args: dt: Datetime in UTC fmt: strftime format string tz_name: Target timezone, or None to use user preference Returns: Formatted datetime string in local time """ if dt is None: return "N/A" local_dt = utc_to_local(dt, tz_name) return local_dt.strftime(fmt) def format_local_time(dt: datetime, tz_name: str = None) -> str: """ Format a UTC datetime as local time (HH:MM format). Args: dt: Datetime in UTC tz_name: Target timezone Returns: Time string in HH:MM format """ return format_local_datetime(dt, "%H:%M", tz_name) def format_local_date(dt: datetime, tz_name: str = None) -> str: """ Format a UTC datetime as local date (YYYY-MM-DD format). Args: dt: Datetime in UTC tz_name: Target timezone Returns: Date string """ return format_local_datetime(dt, "%Y-%m-%d", tz_name) def get_timezone_abbreviation(tz_name: str = None) -> str: """ Get the abbreviation for a timezone (e.g., EST, EDT, PST). Args: tz_name: Timezone name, or None to use user preference Returns: Timezone abbreviation """ tz = get_timezone_info(tz_name) now = datetime.now(tz) return now.strftime("%Z") # Common US timezone choices for settings dropdown TIMEZONE_CHOICES = [ ("America/New_York", "Eastern Time (ET)"), ("America/Chicago", "Central Time (CT)"), ("America/Denver", "Mountain Time (MT)"), ("America/Los_Angeles", "Pacific Time (PT)"), ("America/Anchorage", "Alaska Time (AKT)"), ("Pacific/Honolulu", "Hawaii Time (HT)"), ("UTC", "UTC"), ]