"""ISO-8601 UTC helpers. All schedule instants are tz-aware UTC, formatted with a trailing 'Z' so golden fixtures compare exactly.""" from datetime import date, datetime, timezone from zoneinfo import ZoneInfo def parse_start(value: str, tz_name: str = "UTC") -> datetime: """Accepts 'YYYY-MM-DD' or full ISO (optionally trailing 'Z'). A bare date (or a datetime without an explicit offset) is interpreted in ``tz_name`` (the project's calendar timezone), so '2026-07-01' for a Dubai project means local midnight, not UTC midnight. An explicit offset/`Z` wins. """ v = value.strip() if len(v) == 10: return datetime.fromisoformat(v).replace(tzinfo=ZoneInfo(tz_name)).astimezone(timezone.utc) dt = datetime.fromisoformat(v.replace("Z", "+00:00")) if dt.tzinfo is None: dt = dt.replace(tzinfo=ZoneInfo(tz_name)) return dt.astimezone(timezone.utc) def to_iso_z(dt: datetime) -> str: return dt.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") def today_iso() -> str: return date.today().isoformat()