fix: re-importing an ICS file duplicates every tz-aware timed event (#1683)

This commit is contained in:
Afonso Coutinho
2026-06-03 06:22:49 +01:00
committed by GitHub
parent 1161040efe
commit fbb52a73a0
2 changed files with 66 additions and 1 deletions
+23 -1
View File
@@ -16,6 +16,23 @@ from src.auth_helpers import get_current_user, require_user
logger = logging.getLogger(__name__)
def _ics_naive_dtstart(dt):
"""Naive value matching how import_ics STORES CalendarEvent.dtstart.
Timed tz-aware events are stored as UTC with tzinfo stripped, all-day
dates as midnight datetimes, naive datetimes unchanged. The ICS dedup
must compute the same value or a re-import never matches the stored row.
"""
if isinstance(dt, datetime):
if dt.tzinfo is not None:
from datetime import timezone as _tz
return dt.astimezone(_tz.utc).replace(tzinfo=None)
return dt
if isinstance(dt, date):
return datetime(dt.year, dt.month, dt.day)
return dt
# Single-user fallback identity. Used only when:
# 1. The app is configured for single-user (no auth middleware), AND
# 2. The request didn't resolve to an authenticated user.
@@ -1023,7 +1040,12 @@ def setup_calendar_routes() -> APIRouter:
source_uid = str(comp.get("uid", "")) or None
if source_uid:
src_dtstart = dtstart.dt
naive_src = src_dtstart.replace(tzinfo=None) if hasattr(src_dtstart, 'tzinfo') and src_dtstart.tzinfo else src_dtstart
# Normalize to the SAME naive form import_ics stores, so a
# re-import of a tz-aware event matches the existing row.
# The old code stripped tzinfo WITHOUT converting to UTC
# (wall clock), while storage converts to UTC first, so
# every re-import of a TZID event created a duplicate.
naive_src = _ics_naive_dtstart(src_dtstart)
existing = (
db.query(CalendarEvent)
.filter(