summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-27 08:18:43 -0600
committerCraig Jennings <c@cjennings.net>2026-01-27 08:18:43 -0600
commitd7bef9bbf6ad41502981466638811e3c8e736111 (patch)
tree2d7b88e063cd9fab42b7bd21065c04c26d1b3661
parent31355b5e0472e86842df9ce278000677bdaf4e71 (diff)
fix(calendar-sync): resolve freeze on DST transitions and large ICS files
Two bugs caused Emacs to freeze during calendar sync: 1. split-events used catastrophic regex (\(.\|\n\)*?) on multi-MB ICS data. Replaced with buffer-based search-forward (0.011s for 4.5MB). 2. add-days used midnight for date arithmetic. On DST fall-back days, adding 86400s to midnight CDT yields 11pm CST (same date), creating an infinite loop. Fixed by using noon so ±1h DST shift stays correct.
-rw-r--r--modules/calendar-sync.el20
1 files changed, 13 insertions, 7 deletions
diff --git a/modules/calendar-sync.el b/modules/calendar-sync.el
index 15953ba1..06b5531c 100644
--- a/modules/calendar-sync.el
+++ b/modules/calendar-sync.el
@@ -278,11 +278,14 @@ Monday = 1, Sunday = 7."
(defun calendar-sync--add-days (date days)
"Add DAYS to DATE (year month day).
-Returns new (year month day)."
+Returns new (year month day).
+Uses noon internally to avoid DST boundary issues where adding
+86400 seconds to midnight can land on the same calendar date
+during fall-back transitions."
(let* ((year (nth 0 date))
(month (nth 1 date))
(day (nth 2 date))
- (time (encode-time 0 0 0 day month year))
+ (time (encode-time 0 0 12 day month year))
(new-time (time-add time (days-to-time days)))
(decoded (decode-time new-time)))
(list (nth 5 decoded) (nth 4 decoded) (nth 3 decoded))))
@@ -292,11 +295,14 @@ Returns new (year month day)."
(defun calendar-sync--split-events (ics-content)
"Split ICS-CONTENT into individual VEVENT blocks.
Returns list of strings, each containing one VEVENT block."
- (let ((events '())
- (start 0))
- (while (string-match "BEGIN:VEVENT\\(.\\|\n\\)*?END:VEVENT" ics-content start)
- (push (match-string 0 ics-content) events)
- (setq start (match-end 0)))
+ (let ((events '()))
+ (with-temp-buffer
+ (insert ics-content)
+ (goto-char (point-min))
+ (while (search-forward "BEGIN:VEVENT" nil t)
+ (let ((start (match-beginning 0)))
+ (when (search-forward "END:VEVENT" nil t)
+ (push (buffer-substring-no-properties start (point)) events)))))
(nreverse events)))
(defun calendar-sync--get-property (event property)