Files

50 lines
1.5 KiB
Python

"""Shared timezone-aware UTC helpers for runtime persistence and comparisons."""
from datetime import datetime, timezone
from typing import Optional
UTC = timezone.utc
UTC_MIN = datetime.min.replace(tzinfo=UTC)
def ensure_utc(value: datetime) -> datetime:
"""Normalize a datetime to timezone-aware UTC.
IMPORTANT: legacy persisted timestamps in Doctor were historically naive UTC.
Treating naive values as UTC preserves backward compatibility across upgrades.
"""
if value.tzinfo is None:
return value.replace(tzinfo=UTC)
return value.astimezone(UTC)
def utc_now() -> datetime:
"""Return the current timezone-aware UTC datetime."""
return datetime.now(UTC)
def utc_isoformat(value: Optional[datetime] = None) -> str:
"""Serialize a datetime as ISO-8601 UTC with a trailing `Z`."""
normalized = ensure_utc(value or utc_now())
return normalized.isoformat().replace("+00:00", "Z")
def utc_filename_timestamp(value: Optional[datetime] = None) -> str:
"""Stable UTC timestamp for backup/migration filenames."""
return ensure_utc(value or utc_now()).strftime("%Y%m%d-%H%M%S")
def parse_utc_timestamp(value: Optional[str]) -> Optional[datetime]:
"""Parse legacy/new persisted timestamps into aware UTC datetimes."""
if not value:
return None
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except (ValueError, TypeError, AttributeError):
return None
return ensure_utc(parsed)