Serve events in ics with UTZ timestamps.
This commit is contained in:
parent
77ea6a8afa
commit
5eab5361b2
|
|
@ -4,7 +4,7 @@ import decimal
|
|||
import hashlib
|
||||
import os
|
||||
import typing
|
||||
from datetime import date, datetime, timedelta, timezone, tzinfo as datetime_tzinfo
|
||||
from datetime import date, datetime, timedelta, timezone
|
||||
|
||||
import flask
|
||||
import pycountry
|
||||
|
|
@ -548,43 +548,16 @@ def _append_ical_property(lines: list[str], name: str, value: str) -> None:
|
|||
lines.append(line)
|
||||
|
||||
|
||||
def _append_datetime_property(lines: list[str], name: str, dt_value: datetime) -> None:
|
||||
"""Append a datetime property preserving timezone."""
|
||||
formatted, tzid = _format_ical_datetime(dt_value)
|
||||
prop_name = f"{name};TZID={tzid}" if tzid else name
|
||||
_append_ical_property(lines, prop_name, formatted)
|
||||
|
||||
|
||||
def _ensure_timezone(dt_value: datetime) -> datetime:
|
||||
"""Ensure datetimes are timezone-aware."""
|
||||
if dt_value.tzinfo is None or dt_value.tzinfo.utcoffset(dt_value) is None:
|
||||
def _ensure_utc(dt_value: datetime) -> datetime:
|
||||
"""Ensure datetimes are timezone-aware in UTC."""
|
||||
if dt_value.tzinfo is None:
|
||||
return dt_value.replace(tzinfo=timezone.utc)
|
||||
return dt_value
|
||||
return dt_value.astimezone(timezone.utc)
|
||||
|
||||
|
||||
def _tzid_from_tzinfo(tzinfo_obj: datetime_tzinfo, dt_value: datetime) -> str | None:
|
||||
"""Return TZID string for timezone-aware datetime."""
|
||||
for attr in ("zone", "key"):
|
||||
tzid = getattr(tzinfo_obj, attr, None)
|
||||
if isinstance(tzid, str):
|
||||
return tzid
|
||||
tzname = tzinfo_obj.tzname(dt_value)
|
||||
if isinstance(tzname, str) and tzname.upper() not in {"UTC", "GMT"}:
|
||||
return tzname
|
||||
return None
|
||||
|
||||
|
||||
def _format_ical_datetime(dt_value: datetime) -> tuple[str, str | None]:
|
||||
"""Format datetime objects, preserving timezone when available."""
|
||||
dt_value = _ensure_timezone(dt_value)
|
||||
tzinfo = dt_value.tzinfo
|
||||
assert tzinfo
|
||||
tzid = _tzid_from_tzinfo(tzinfo, dt_value)
|
||||
if tzid:
|
||||
local_dt = dt_value.astimezone(tzinfo).replace(tzinfo=None)
|
||||
return local_dt.strftime("%Y%m%dT%H%M%S"), tzid
|
||||
utc_dt = dt_value.astimezone(timezone.utc)
|
||||
return utc_dt.strftime("%Y%m%dT%H%M%SZ"), None
|
||||
def _format_ical_datetime(dt_value: datetime) -> str:
|
||||
"""Format datetime objects using UTC and RFC5545 format."""
|
||||
return _ensure_utc(dt_value).strftime("%Y%m%dT%H%M%SZ")
|
||||
|
||||
|
||||
def _format_ical_date(date_value: date) -> str:
|
||||
|
|
@ -598,7 +571,7 @@ def _event_datetimes(element: TripElement) -> tuple[datetime, datetime]:
|
|||
end_dt = as_datetime(element.end_time) if element.end_time else None
|
||||
if end_dt is None or end_dt <= start_dt:
|
||||
end_dt = start_dt + DEFAULT_EVENT_DURATION
|
||||
return _ensure_timezone(start_dt), _ensure_timezone(end_dt)
|
||||
return _ensure_utc(start_dt), _ensure_utc(end_dt)
|
||||
|
||||
|
||||
def _event_all_day_dates(element: TripElement) -> tuple[date, date]:
|
||||
|
|
@ -784,7 +757,7 @@ def build_trip_ical(trips: list[Trip]) -> bytes:
|
|||
for index, element in enumerate(trip.elements()):
|
||||
lines.append("BEGIN:VEVENT")
|
||||
_append_ical_property(lines, "UID", _trip_element_uid(trip, element, index))
|
||||
_append_datetime_property(lines, "DTSTAMP", generated)
|
||||
_append_ical_property(lines, "DTSTAMP", _format_ical_datetime(generated))
|
||||
if element.all_day:
|
||||
start_date, end_date = _event_all_day_dates(element)
|
||||
_append_ical_property(
|
||||
|
|
@ -795,8 +768,8 @@ def build_trip_ical(trips: list[Trip]) -> bytes:
|
|||
)
|
||||
else:
|
||||
start_dt, end_dt = _event_datetimes(element)
|
||||
_append_datetime_property(lines, "DTSTART", start_dt)
|
||||
_append_datetime_property(lines, "DTEND", end_dt)
|
||||
_append_ical_property(lines, "DTSTART", _format_ical_datetime(start_dt))
|
||||
_append_ical_property(lines, "DTEND", _format_ical_datetime(end_dt))
|
||||
summary = _escape_ical_text(_trip_element_summary(trip, element))
|
||||
_append_ical_property(lines, "SUMMARY", summary)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue