41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import DateTime as SQLADateTime
|
|
from sqlalchemy import Dialect, TypeDecorator
|
|
from sqlalchemy.orm import DeclarativeBase
|
|
|
|
|
|
def utcnow() -> datetime:
|
|
"""
|
|
Return current UTC time as timezone-aware datetime.
|
|
|
|
This is the canonical way to get current UTC time for database fields.
|
|
"""
|
|
return datetime.now(tz=UTC)
|
|
|
|
|
|
class UTCDateTime(TypeDecorator):
|
|
impl = SQLADateTime
|
|
cache_ok = True
|
|
|
|
def process_bind_param(self, value: datetime | None, dialect: Dialect) -> datetime | None:
|
|
"""Convert datetime to UTC before storing."""
|
|
_ = dialect
|
|
if value is not None:
|
|
if value.tzinfo is None:
|
|
return value.replace(tzinfo=UTC)
|
|
return value.astimezone(UTC).replace(tzinfo=None)
|
|
return value
|
|
|
|
def process_result_value(self, value: datetime | None, dialect: Dialect) -> datetime | None:
|
|
"""Ensure datetime is timezone-aware (UTC) when loading."""
|
|
_ = dialect
|
|
if value is not None and value.tzinfo is None:
|
|
return value.replace(tzinfo=UTC)
|
|
return value
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
pass
|