Files
bakery-ia/shared/dt_utils/timezone.py

161 lines
3.8 KiB
Python
Executable File

"""
Timezone Operations
Timezone metadata and conversion utilities.
"""
from datetime import datetime, date, time
from zoneinfo import ZoneInfo
from typing import Optional
import structlog
from .constants import DEFAULT_TIMEZONE, SUPPORTED_TIMEZONES
logger = structlog.get_logger()
def get_timezone_info(tz: str) -> ZoneInfo:
"""
Get ZoneInfo object for timezone.
Args:
tz: IANA timezone string
Returns:
ZoneInfo object
Raises:
ZoneInfoNotFoundError: If timezone is invalid
"""
return ZoneInfo(tz)
def get_utc_offset_hours(tz: str) -> float:
"""
Get current UTC offset for timezone in hours.
Args:
tz: IANA timezone string
Returns:
UTC offset in hours (e.g., +2.0 for CEST)
"""
try:
zone = ZoneInfo(tz)
now = datetime.now(zone)
offset_seconds = now.utcoffset().total_seconds()
return offset_seconds / 3600
except Exception as e:
logger.warning(f"Could not get offset for {tz}", error=str(e))
return 0.0
def list_supported_timezones() -> list[str]:
"""
Get list of supported timezones.
Returns:
List of IANA timezone strings
"""
return sorted(SUPPORTED_TIMEZONES)
def get_current_date_in_tz(tz: str = DEFAULT_TIMEZONE) -> date:
"""
Get current date in specified timezone.
Args:
tz: IANA timezone string
Returns:
Current date in the specified timezone
"""
try:
zone = ZoneInfo(tz)
return datetime.now(zone).date()
except Exception as e:
logger.warning(f"Invalid timezone {tz}, using default", error=str(e))
return datetime.now(ZoneInfo(DEFAULT_TIMEZONE)).date()
def get_current_datetime_in_tz(tz: str = DEFAULT_TIMEZONE) -> datetime:
"""
Get current datetime in specified timezone.
Args:
tz: IANA timezone string
Returns:
Current datetime in the specified timezone
"""
try:
zone = ZoneInfo(tz)
return datetime.now(zone)
except Exception as e:
logger.warning(f"Invalid timezone {tz}, using default", error=str(e))
return datetime.now(ZoneInfo(DEFAULT_TIMEZONE))
def combine_date_time_in_tz(
target_date: date,
target_time: time,
tz: str = DEFAULT_TIMEZONE
) -> datetime:
"""
Combine date and time in specified timezone.
Args:
target_date: Date component
target_time: Time component
tz: IANA timezone string
Returns:
Datetime combining date and time in specified timezone
"""
try:
zone = ZoneInfo(tz)
return datetime.combine(target_date, target_time, tzinfo=zone)
except Exception as e:
logger.warning(f"Invalid timezone {tz}, using default", error=str(e))
zone = ZoneInfo(DEFAULT_TIMEZONE)
return datetime.combine(target_date, target_time, tzinfo=zone)
def convert_to_utc(dt: datetime) -> datetime:
"""
Convert datetime to UTC.
Args:
dt: Datetime to convert (must be timezone-aware)
Returns:
Datetime in UTC timezone
"""
if dt.tzinfo is None:
logger.warning("Converting naive datetime to UTC, assuming UTC")
return dt.replace(tzinfo=ZoneInfo("UTC"))
return dt.astimezone(ZoneInfo("UTC"))
def convert_from_utc(dt: datetime, target_timezone: str) -> datetime:
"""
Convert UTC datetime to target timezone.
Args:
dt: UTC datetime
target_timezone: Target IANA timezone string
Returns:
Datetime in target timezone
"""
if dt.tzinfo is None:
dt = dt.replace(tzinfo=ZoneInfo("UTC"))
try:
zone = ZoneInfo(target_timezone)
return dt.astimezone(zone)
except Exception as e:
logger.warning(f"Invalid timezone {target_timezone}, using default", error=str(e))
zone = ZoneInfo(DEFAULT_TIMEZONE)
return dt.astimezone(zone)