161 lines
3.8 KiB
Python
Executable File
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)
|