events: RRULE EXDATE/RDATE exceptions + 8 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
ev-event-full carries :exdate/:rdate. ev-expand-base = raw expansion; ev-expand applies exceptions: RDATE adds in-window occurrences, EXDATE removes matching starts, de-duped, EXDATE wins over RDATE and the rrule (RFC 5545). RDATE-only events supported; plain ev-event unaffected. 248/248 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -421,6 +421,115 @@
|
||||
(list (quote a) (list 2026 6 2))
|
||||
(list (quote a) (list 2026 6 3))))))))
|
||||
|
||||
;; ---- EXDATE / RDATE exceptions ----
|
||||
(define
|
||||
ev-cal-ex-run-all!
|
||||
(fn
|
||||
()
|
||||
(do
|
||||
;; EXDATE removes a matching occurrence from the recurrence
|
||||
(let
|
||||
((ex
|
||||
(ev-event-full
|
||||
(quote standup)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
{:freq :daily :count 5}
|
||||
1
|
||||
(list (ev-dt 2026 6 3 9 0))
|
||||
(list))))
|
||||
(ev-cal-check!
|
||||
"EXDATE excludes the matching occurrence"
|
||||
(ev-cal-starts (ev-expand ex (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
(list (list 2026 6 1) (list 2026 6 2) (list 2026 6 4) (list 2026 6 5))))
|
||||
;; EXDATE that matches nothing is a no-op
|
||||
(let
|
||||
((ex2
|
||||
(ev-event-full
|
||||
(quote s)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
{:freq :daily :count 3}
|
||||
1
|
||||
(list (ev-dt 2026 6 9 9 0))
|
||||
(list))))
|
||||
(ev-cal-check!
|
||||
"EXDATE not matching any occurrence is a no-op"
|
||||
(len (ev-expand ex2 (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
3))
|
||||
;; RDATE adds an explicit occurrence (within the window)
|
||||
(let
|
||||
((rd
|
||||
(ev-event-full
|
||||
(quote s)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
{:freq :daily :count 3}
|
||||
1
|
||||
(list)
|
||||
(list (ev-dt 2026 6 10 9 0)))))
|
||||
(do
|
||||
(ev-cal-check!
|
||||
"RDATE adds an explicit occurrence, sorted in"
|
||||
(ev-cal-starts (ev-expand rd (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
(list (list 2026 6 1) (list 2026 6 2) (list 2026 6 3) (list 2026 6 10)))
|
||||
(ev-cal-check!
|
||||
"RDATE outside the window is dropped"
|
||||
(len (ev-expand rd (ev-date 2026 6 1) (ev-date 2026 6 5)))
|
||||
3)))
|
||||
;; RDATE coinciding with an rrule occurrence is de-duplicated
|
||||
(let
|
||||
((rdup
|
||||
(ev-event-full
|
||||
(quote s)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
{:freq :daily :count 3}
|
||||
1
|
||||
(list)
|
||||
(list (ev-dt 2026 6 2 9 0)))))
|
||||
(ev-cal-check!
|
||||
"RDATE duplicating an occurrence does not double it"
|
||||
(len (ev-expand rdup (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
3))
|
||||
;; EXDATE wins over RDATE for the same datetime
|
||||
(let
|
||||
((both
|
||||
(ev-event-full
|
||||
(quote s)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
{:freq :daily :count 3}
|
||||
1
|
||||
(list (ev-dt 2026 6 2 9 0))
|
||||
(list (ev-dt 2026 6 2 9 0)))))
|
||||
(ev-cal-check!
|
||||
"EXDATE wins over RDATE and the rrule for the same date"
|
||||
(ev-cal-starts (ev-expand both (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
(list (list 2026 6 1) (list 2026 6 3))))
|
||||
;; RDATE-only event (no rrule)
|
||||
(let
|
||||
((ronly
|
||||
(ev-event-full
|
||||
(quote s)
|
||||
(ev-dt 2026 6 1 9 0)
|
||||
30
|
||||
nil
|
||||
1
|
||||
(list)
|
||||
(list (ev-dt 2026 6 5 9 0) (ev-dt 2026 6 3 9 0)))))
|
||||
(ev-cal-check!
|
||||
"RDATE-only event yields dtstart plus the extra dates, sorted"
|
||||
(ev-cal-starts (ev-expand ronly (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
(list (list 2026 6 1) (list 2026 6 3) (list 2026 6 5))))
|
||||
;; plain ev-event (no exception keys) is unaffected
|
||||
(let
|
||||
((plain (ev-event (quote p) (ev-dt 2026 6 1 9 0) 30 {:freq :daily :count 3} 1)))
|
||||
(ev-cal-check!
|
||||
"plain event without exceptions expands unchanged"
|
||||
(len (ev-expand plain (ev-date 2026 6 1) (ev-date 2026 7 1)))
|
||||
3)))))
|
||||
|
||||
(define
|
||||
ev-calendar-tests-run!
|
||||
(fn
|
||||
@@ -430,4 +539,5 @@
|
||||
(set! ev-cal-fail 0)
|
||||
(set! ev-cal-failures (list))
|
||||
(ev-cal-run-all!)
|
||||
(ev-cal-ex-run-all!)
|
||||
{:failures ev-cal-failures :total (+ ev-cal-pass ev-cal-fail) :passed ev-cal-pass :failed ev-cal-fail})))
|
||||
|
||||
Reference in New Issue
Block a user