Rename `icalendar-recur' type and related functions

More context in Bug#80786 and:
https://lists.gnu.org/archive/html/emacs-orgmode/2026-03/msg00286.html

`icalendar-recur' as a type name for RRULE values was confusing and made
the accessors for this type difficult to discover, because `icalendar-recur-'
is also used as a prefix in icalendar-recur.el.  This change renames the
`icalendar-recur' type to `icalendar-rrule-value' and renames the
accessor functions for these values appropriately.

* lisp/calendar/icalendar-parser.el:  Rename symbols as follows:
(icalendar-recur): `icalendar-rrule-value'
(icalendar-read-recur-rule-part): `icalendar-read-rrule-part'
(icalendar-print-recur-rule-part): `icalendar-print-rrule-part'
(icalendar-recur-rule-part): `icalendar-rrule-part'
(icalendar-read-recur): `icalendar-read-rrule-value'
(icalendar-print-recur): `icalendar-print-rrule-value'
(icalendar--recur-value-types): `icalendar--rrule-value-types'
(icalendar-recur-value-p): `icalendar-rrule-value-p'
(icalendar-recur-freq): `icalendar-rrule-freq'
(icalendar-recur-interval-size): `icalendar-rrule-interval-size'
(icalendar-recur-until): `icalendar-rrule-until'
(icalendar-recur-count): `icalendar-rrule-count'
(icalendar-recur-weekstart): `icalendar-rrule-weekstart'
(icalendar-recur-by*): `icalendar-rrule-by*'.
(icalendar-rrule):
(icalendar-index-insert):
(icalendar-index-get): Update references.
* lisp/calendar/icalendar-recur.el (icalendar-recur-find-interval):
(icalendar-recur-nth-interval):
(icalendar-recur-next-interval):
(icalendar-recur-previous-interval):
(icalendar-recur-refine-from-clauses):
(icalendar-recur-recurrences-in-interval):
(icalendar-recur-recurrences-in-window):
(icalendar-recur-recurrences-to-count):
(icalendar-recur-tz-observance-on): Update references.
* lisp/calendar/diary-icalendar.el: Update references.
* lisp/calendar/icalendar-shortdoc.el (icalendar): Update shortdoc examples.
* lisp/gnus/gnus-icalendar.el: Update references.
* test/lisp/calendar/diary-icalendar-tests.el:
* test/lisp/calendar/icalendar-parser-tests.el:
* test/lisp/calendar/icalendar-recur-tests.el: Update references in tests.
This commit is contained in:
Richard Lawrence 2026-05-17 12:54:24 +02:00 committed by Eli Zaretskii
parent 3d2bb233f2
commit 70b79b3ed8
8 changed files with 176 additions and 171 deletions

View file

@ -2690,12 +2690,12 @@ recurrence rule values in these nodes are adjusted NDAYS forward."
:duration (ical:period-dur-value value))) :duration (ical:period-dur-value value)))
(t (ical:date/time-add value :day ndays))))))) (t (ical:date/time-add value :day ndays)))))))
(ical:rrule (ical:rrule
(let ((mdays (ical:recur-by* 'BYMONTHDAY value)) (let ((mdays (ical:rrule-by* 'BYMONTHDAY value))
(ydays (ical:recur-by* 'BYYEARDAY value)) (ydays (ical:rrule-by* 'BYYEARDAY value))
(dows (ical:recur-by* 'BYDAY value)) (dows (ical:rrule-by* 'BYDAY value))
(bad-clause (bad-clause
(cond ((ical:recur-by* 'BYSETPOS value) 'BYSETPOS) (cond ((ical:rrule-by* 'BYSETPOS value) 'BYSETPOS)
((ical:recur-by* 'BYWEEKNO value) 'BYWEEKNO)))) ((ical:rrule-by* 'BYWEEKNO value) 'BYWEEKNO))))
;; We can't reliably subtract days in the following cases, so bail: ;; We can't reliably subtract days in the following cases, so bail:
(when (< 28 ndays) (when (< 28 ndays)
(di:signal-export-error (di:signal-export-error
@ -2970,12 +2970,12 @@ nil, if MONTHS, DAYS and YEARS are all integers)."
rdates (seq-remove (apply-partially #'equal dtstart) rdates)))) rdates (seq-remove (apply-partially #'equal dtstart) rdates))))
;; Return the pair of nodes (DTSTART RRULE) or (DTSTART RDATE): ;; Return the pair of nodes (DTSTART RRULE) or (DTSTART RDATE):
(let* ((recur-value (let* ((rrule-value
(delq nil (delq nil
`((FREQ ,freq) `((FREQ ,freq)
,(when bymonth (list 'BYMONTH bymonth)) ,(when bymonth (list 'BYMONTH bymonth))
,(when bymonthday (list 'BYMONTHDAY bymonthday))))) ,(when bymonthday (list 'BYMONTHDAY bymonthday)))))
(rrule-node (when freq (ical:make-property ical:rrule recur-value))) (rrule-node (when freq (ical:make-property ical:rrule rrule-value)))
(rdate-node (when rdates (rdate-node (when rdates
(ical:make-property ical:rdate rdates (ical:make-property ical:rdate rdates
(ical:valuetypeparam rdate-type)))) (ical:valuetypeparam rdate-type))))
@ -3548,7 +3548,7 @@ values (of the same type as START)."
(interval (icr:find-interval date start rule))) (interval (icr:find-interval date start rule)))
(cl-typecase start (cl-typecase start
(ical:date (ical:date
(if (ical:recur-count rule) (if (ical:rrule-count rule)
(when (member date (icr:recurrences-to-count vevent)) (when (member date (icr:recurrences-to-count vevent))
entry) entry)
(when (member date (icr:recurrences-in-interval interval vevent)) (when (member date (icr:recurrences-in-interval interval vevent))
@ -3581,7 +3581,7 @@ values (of the same type as START)."
(ical:date/time-add-duration start duration)) (ical:date/time-add-duration start duration))
(di:format-time-as-local start))) (di:format-time-as-local start)))
(date-entry (concat entry-time " " entry))) (date-entry (concat entry-time " " entry)))
(when (memq (ical:recur-freq date-rule) '(HOURLY MINUTELY SECONDLY)) (when (memq (ical:rrule-freq date-rule) '(HOURLY MINUTELY SECONDLY))
(setf (alist-get 'FREQ date-rule) 'DAILY) (setf (alist-get 'FREQ date-rule) 'DAILY)
(setf (alist-get 'INTERVAL date-rule) 1) (setf (alist-get 'INTERVAL date-rule) 1)
(setf (alist-get 'BYHOUR date-rule nil t) nil) (setf (alist-get 'BYHOUR date-rule nil t) nil)

View file

@ -1352,9 +1352,9 @@ See `icalendar-read-weekdaynum' for the format of VAL."
;; number alone just stands for a day: ;; number alone just stands for a day:
(car (rassq val ical:weekday-numbers)))) (car (rassq val ical:weekday-numbers))))
(defun ical:read-recur-rule-part (s) (defun ical:read-rrule-part (s)
"Read an `icalendar-recur-rule-part' from string S. "Read an `icalendar-rrule-part' from string S.
S should have been matched against `icalendar-recur-rule-part'. S should have been matched against `icalendar-rrule-part'.
The return value is a list (KEYWORD VALUE), where VALUE may The return value is a list (KEYWORD VALUE), where VALUE may
itself be a list, depending on the values allowed by KEYWORD." itself be a list, depending on the values allowed by KEYWORD."
;; TODO: this smells like a design flaw. Silence the byte compiler for now. ;; TODO: this smells like a design flaw. Silence the byte compiler for now.
@ -1376,7 +1376,7 @@ itself be a list, depending on the values allowed by KEYWORD."
(rx ical:weekdaynum) ",")) (rx ical:weekdaynum) ","))
(WKST (cdr (assoc values ical:weekday-numbers))))))) (WKST (cdr (assoc values ical:weekday-numbers)))))))
(defun ical:print-recur-rule-part (part) (defun ical:print-rrule-part (part)
"Serialize recur rule part PART to a string." "Serialize recur rule part PART to a string."
(let ((keyword (car part)) (let ((keyword (car part))
(values (cadr part)) (values (cadr part))
@ -1398,7 +1398,7 @@ itself be a list, depending on the values allowed by KEYWORD."
(concat (symbol-name keyword) "=" values-str))) (concat (symbol-name keyword) "=" values-str)))
(rx-define ical:recur-rule-part (rx-define ical:rrule-part
;; Group 11: keyword ;; Group 11: keyword
;; Group 12: value(s) ;; Group 12: value(s)
(or (seq (group-n 11 "FREQ") "=" (group-n 12 ical:freq)) (or (seq (group-n 11 "FREQ") "=" (group-n 12 ical:freq))
@ -1423,14 +1423,12 @@ itself be a list, depending on the values allowed by KEYWORD."
(ical:comma-list ical:yeardaynum))) (ical:comma-list ical:yeardaynum)))
(seq (group-n 11 "WKST") "=" (group-n 12 ical:weekday)))) (seq (group-n 11 "WKST") "=" (group-n 12 ical:weekday))))
(defun ical:read-recur (s) (defun ical:read-rrule-value (s)
"Read a recurrence rule value from string S. "Read a recurrence rule value from string S.
S should be a match against rx `icalendar-recur'." S should be a match against rx `icalendar-recur'."
;; TODO: let's switch to keywords and a plist, so we can more easily (ical:read-list-with #'ical:read-rrule-part s (rx ical:rrule-part) ";"))
;; write these clauses also in diary sexp entries without so many parens
(ical:read-list-with #'ical:read-recur-rule-part s (rx ical:recur-rule-part) ";"))
(defun ical:print-recur (val) (defun ical:print-rrule-value (val)
"Serialize a recurrence rule value VAL to a string." "Serialize a recurrence rule value VAL to a string."
;; RFC5545 sec. 3.3.10: "to ensure backward compatibility with ;; RFC5545 sec. 3.3.10: "to ensure backward compatibility with
;; applications that pre-date this revision of iCalendar the ;; applications that pre-date this revision of iCalendar the
@ -1438,15 +1436,15 @@ S should be a match against rx `icalendar-recur'."
;; RECUR value." ;; RECUR value."
(string-join (string-join
(cons (cons
(ical:print-recur-rule-part (assq 'FREQ val)) (ical:print-rrule-part (assq 'FREQ val))
(mapcar #'ical:print-recur-rule-part (mapcar #'ical:print-rrule-part
(seq-filter (lambda (part) (not (eq 'FREQ (car part)))) (seq-filter (lambda (part) (not (eq 'FREQ (car part))))
val))) val)))
";")) ";"))
(defconst ical:-recur-value-types (defconst ical:-rrule-value-types
;; `list-of' is not a cl-type specifier, just a symbol here; it is ;; `list-of' is not a cl-type specifier, just a symbol here; it is
;; handled specially when checking types in `ical:recur-value-p': ;; handled specially when checking types in `ical:rrule-value-p':
'(FREQ (member YEARLY MONTHLY WEEKLY DAILY HOURLY MINUTELY SECONDLY) '(FREQ (member YEARLY MONTHLY WEEKLY DAILY HOURLY MINUTELY SECONDLY)
UNTIL (or ical:date-time ical:date) UNTIL (or ical:date-time ical:date)
COUNT (integer 1 *) COUNT (integer 1 *)
@ -1470,7 +1468,7 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0."
(cl-typep (car val) '(integer 0 6)) (cl-typep (car val) '(integer 0 6))
(cl-typep (cdr val) '(or (integer -53 -1) (integer 1 53))))) (cl-typep (cdr val) '(or (integer -53 -1) (integer 1 53)))))
(defun ical:recur-value-p (vals) (defun ical:rrule-value-p (vals)
"Return non-nil if VALS is an iCalendar recurrence rule value." "Return non-nil if VALS is an iCalendar recurrence rule value."
(and (listp vals) (and (listp vals)
;; FREQ is always required: ;; FREQ is always required:
@ -1487,11 +1485,11 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0."
(assq 'BYHOUR vals) (assq 'BYHOUR vals)
(assq 'BYMINUTE vals) (assq 'BYMINUTE vals)
(assq 'BYSECOND vals)) (assq 'BYSECOND vals))
(let ((freq (ical:recur-freq vals)) (let ((freq (ical:rrule-freq vals))
(byday (ical:recur-by* 'BYDAY vals)) (byday (ical:rrule-by* 'BYDAY vals))
(byweekno (ical:recur-by* 'BYWEEKNO vals)) (byweekno (ical:rrule-by* 'BYWEEKNO vals))
(bymonthday (ical:recur-by* 'BYMONTHDAY vals)) (bymonthday (ical:rrule-by* 'BYMONTHDAY vals))
(byyearday (ical:recur-by* 'BYYEARDAY vals))) (byyearday (ical:rrule-by* 'BYYEARDAY vals)))
(and (and
;; "The BYDAY rule part MUST NOT be specified with a numeric ;; "The BYDAY rule part MUST NOT be specified with a numeric
;; value when the FREQ rule part is not set to MONTHLY or ;; value when the FREQ rule part is not set to MONTHLY or
@ -1518,7 +1516,7 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0."
(when (consp kv) (when (consp kv)
(let* ((keyword (car kv)) (let* ((keyword (car kv))
(val (cadr kv)) (val (cadr kv))
(type (plist-get ical:-recur-value-types keyword))) (type (plist-get ical:-rrule-value-types keyword)))
(and keyword val type (and keyword val type
(if (and (consp type) (if (and (consp type)
(eq (car type) 'list-of)) (eq (car type) 'list-of))
@ -1526,7 +1524,14 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0."
(cl-typep val type)))))) (cl-typep val type))))))
vals))) vals)))
(ical:define-type ical:recur "RECUR" (ical:define-type ical:rrule-value
;; Renamed from "ical:recur", which turns out to
;; produce confusing names downstream. Thus I've
;; deviated from the standard here, and call
;; `ical:rrule-value' what the standard calls a RECUR
;; value. (`ical:rrule' is not available because that
;; names the *property* containing such a value.)
"RECUR"
"Type for Recurrence Rule values. "Type for Recurrence Rule values.
When printed, a recurrence rule value looks like When printed, a recurrence rule value looks like
@ -1587,39 +1592,39 @@ Some examples:
Notice that singleton values are still wrapped in a list when the Notice that singleton values are still wrapped in a list when the
KEY accepts a list of values, but not when the KEY always has a KEY accepts a list of values, but not when the KEY always has a
single (e.g. integer) value." single (e.g. integer) value."
'(satisfies ical:recur-value-p) '(satisfies ical:rrule-value-p)
(ical:semicolon-list ical:recur-rule-part) (ical:semicolon-list ical:rrule-part)
:reader ical:read-recur :reader ical:read-rrule-value
:printer ical:print-recur :printer ical:print-rrule-value
:link "https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10") :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10")
(defun ical:recur-freq (recur-value) (defun ical:rrule-freq (rrule)
"Return the frequency in RECUR-VALUE." "Return the frequency in RRULE."
(car (alist-get 'FREQ recur-value))) (car (alist-get 'FREQ rrule)))
(defun ical:recur-interval-size (recur-value) (defun ical:rrule-interval-size (rrule)
"Return the interval size in RECUR-VALUE, or the default of 1." "Return the interval size in RRULE, or the default of 1."
(or (car (alist-get 'INTERVAL recur-value)) 1)) (or (car (alist-get 'INTERVAL rrule)) 1))
(defun ical:recur-until (recur-value) (defun ical:rrule-until (rrule)
"Return the UNTIL date(-time) in RECUR-VALUE." "Return the UNTIL date(-time) in RRULE."
(car (alist-get 'UNTIL recur-value))) (car (alist-get 'UNTIL rrule)))
(defun ical:recur-count (recur-value) (defun ical:rrule-count (rrule)
"Return the COUNT in RECUR-VALUE." "Return the COUNT in RRULE."
(car (alist-get 'COUNT recur-value))) (car (alist-get 'COUNT rrule)))
(defun ical:recur-weekstart (recur-value) (defun ical:rrule-weekstart (rrule)
"Return the weekday which starts the work week in RECUR-VALUE. "Return the weekday which starts the work week in RRULE.
If no starting weekday is specified in RECUR-VALUE, returns the default, If no starting weekday is specified in RRULE, returns the default,
1 (= Monday)." 1 (= Monday)."
(or (car (alist-get 'WKST recur-value)) 1)) (or (car (alist-get 'WKST rrule)) 1))
(defun ical:recur-by* (byunit recur-value) (defun ical:rrule-by* (byunit rrule)
"Return the values in the BYUNIT clause in RECUR-VALUE. "Return the values in the BYUNIT clause in RRULE.
BYUNIT should be a symbol: \\='BYMONTH, \\='BYDAY, etc. BYUNIT should be a symbol: \\='BYMONTH, \\='BYDAY, etc.
See `icalendar-recur' for all the possible BYUNIT values." See `icalendar-rrule-value' for all the possible BYUNIT values."
(car (alist-get byunit recur-value))) (car (alist-get byunit rrule)))
;;;; 3.3.11 Text ;;;; 3.3.11 Text
(rx-define ical:escaped-char (rx-define ical:escaped-char
@ -3272,7 +3277,7 @@ and times on which an `icalendar-vevent', `icalendar-todo',
`icalendar-daylight' component recurs. Together with the `icalendar-daylight' component recurs. Together with the
`icalendar-dtstart', `icalendar-rdate', and `icalendar-exdate' `icalendar-dtstart', `icalendar-rdate', and `icalendar-exdate'
properties, it defines the recurrence set of the component." properties, it defines the recurrence set of the component."
ical:recur ical:rrule-value
;; TODO: faces for subexpressions? ;; TODO: faces for subexpressions?
:child-spec (:zero-or-more (ical:otherparam)) :child-spec (:zero-or-more (ical:otherparam))
:link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.5.3") :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.5.3")
@ -4567,7 +4572,7 @@ which see."
(ical:dtend :first dtend-node :value dtend) (ical:dtend :first dtend-node :value dtend)
(ical:due :value due) (ical:due :value due)
(ical:duration :value duration) (ical:duration :value duration)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes) (ical:rdate :all rdate-nodes)
(ical:exdate :all exdate-nodes) (ical:exdate :all exdate-nodes)
(ical:uid :value uid)) (ical:uid :value uid))
@ -4592,7 +4597,7 @@ which see."
;; If the component has an RRULE that specifies a fixed number ;; If the component has an RRULE that specifies a fixed number
;; of recurrences, compute them now and index them for each date ;; of recurrences, compute them now and index them for each date
;; in each recurrence: ;; in each recurrence:
((and recur-value (ical:recur-count recur-value)) ((and rrule (ical:rrule-count rrule))
(let* ((tz (gethash (ical:with-param-of dtstart-node 'ical:tzidparam) (let* ((tz (gethash (ical:with-param-of dtstart-node 'ical:tzidparam)
tzid-index)) tzid-index))
(recs (cons dtstart (icr:recurrences-to-count component tz)))) (recs (cons dtstart (icr:recurrences-to-count component tz))))
@ -4605,7 +4610,7 @@ which see."
(list (ical:date/time-to-date (list (ical:date/time-to-date
(ical:date/time-to-local rec)))))))))) (ical:date/time-to-local rec))))))))))
;; Same with RDATEs when there's no RRULE: ;; Same with RDATEs when there's no RRULE:
((and rdates (not recur-value)) ((and rdates (not rrule))
(dolist (rec (cons dtstart rdates)) (dolist (rec (cons dtstart rdates))
(unless (or (cl-typep rec 'ical:period) (member rec exdates)) (unless (or (cl-typep rec 'ical:period) (member rec exdates))
(let ((end-time (let ((end-time
@ -4624,7 +4629,7 @@ which see."
(setq dates (append dates (ical:dates-until start end t))))))) (setq dates (append dates (ical:dates-until start end t)))))))
;; A non-recurring event also gets an index entry for each date ;; A non-recurring event also gets an index entry for each date
;; until its end time: ;; until its end time:
((not recur-value) ((not rrule)
(let ((end-time (let ((end-time
(or dtend due (or dtend due
(when duration (when duration
@ -4703,13 +4708,13 @@ Only one keyword argument can be queried at a time."
(dolist (component recurring) (dolist (component recurring)
(ical:with-component component (ical:with-component component
((ical:dtstart :first dtstart-node :value dtstart) ((ical:dtstart :first dtstart-node :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes) (ical:rdate :all rdate-nodes)
(ical:duration :value duration)) (ical:duration :value duration))
(unless (ical:date/time<= date dtstart) (unless (ical:date/time<= date dtstart)
(let* ((tz (ical:with-param-of dtstart-node 'ical:tzidparam nil (let* ((tz (ical:with-param-of dtstart-node 'ical:tzidparam nil
(gethash value (plist-get index :bytzid)))) (gethash value (plist-get index :bytzid))))
(int (icr:find-interval date dtstart recur-value tz)) (int (icr:find-interval date dtstart rrule tz))
(recs (icr:recurrences-in-interval int component tz))) (recs (icr:recurrences-in-interval int component tz)))
(catch 'found (catch 'found
(dolist (rec recs) (dolist (rec recs)

View file

@ -451,29 +451,29 @@ See `icalendar-recur-find-interval' for arguments' meanings."
;; Return the bounds: ;; Return the bounds:
(icr:make-interval low high next-low))) (icr:make-interval low high next-low)))
(defun icr:find-interval (target dtstart recur-value &optional vtimezone) (defun icr:find-interval (target dtstart rrule &optional vtimezone)
"Return the recurrence interval around TARGET. "Return the recurrence interval around TARGET.
TARGET and DTSTART should be `icalendar-date' or `icalendar-date-time' TARGET and DTSTART should be `icalendar-date' or `icalendar-date-time'
values. RECUR-VALUE should be an `icalendar-recur'. values. RRULE should be an `icalendar-recur'.
The returned value is an interval [LOW HIGH NEXT-LOW] which The returned value is an interval [LOW HIGH NEXT-LOW] which
represents the lower and upper bounds of a recurrence interval around represents the lower and upper bounds of a recurrence interval around
TARGET. For some N, LOW is equal to START + N*INTERVALSIZE units, HIGH TARGET. For some N, LOW is equal to START + N*INTERVALSIZE units, HIGH
is equal to START + (N+1)*INTERVALSIZE units, and LOW <= TARGET < HIGH. is equal to START + (N+1)*INTERVALSIZE units, and LOW <= TARGET < HIGH.
START here is a time derived from DTSTART depending on RECUR-VALUE's START here is a time derived from DTSTART depending on RRULE's
FREQ part: the first day of the year for a \\='YEARLY rule, first day FREQ part: the first day of the year for a \\='YEARLY rule, first day
of the month for a \\='MONTHLY rule, etc. of the month for a \\='MONTHLY rule, etc.
RECUR-VALUE's interval determines INTERVALSIZE, and its frequency RRULE's interval determines INTERVALSIZE, and its frequency
determines the units: a month for \\='MONTHLY, etc. determines the units: a month for \\='MONTHLY, etc.
If VTIMEZONE is provided, it is used to set time zone information in the If VTIMEZONE is provided, it is used to set time zone information in the
returned interval bounds. Otherwise, the bounds contain no time zone returned interval bounds. Otherwise, the bounds contain no time zone
information and represent floating local times." information and represent floating local times."
(let ((freq (ical:recur-freq recur-value)) (let ((freq (ical:rrule-freq rrule))
(intsize (ical:recur-interval-size recur-value)) (intsize (ical:rrule-interval-size rrule))
(weekstart (ical:recur-weekstart recur-value))) (weekstart (ical:rrule-weekstart rrule)))
(cl-case freq (cl-case freq
(SECONDLY (icr:find-secondly-interval target dtstart intsize vtimezone)) (SECONDLY (icr:find-secondly-interval target dtstart intsize vtimezone))
(MINUTELY (icr:find-minutely-interval target dtstart intsize vtimezone)) (MINUTELY (icr:find-minutely-interval target dtstart intsize vtimezone))
@ -484,22 +484,22 @@ information and represent floating local times."
(MONTHLY (icr:find-monthly-interval target dtstart intsize vtimezone)) (MONTHLY (icr:find-monthly-interval target dtstart intsize vtimezone))
(YEARLY (icr:find-yearly-interval target dtstart intsize vtimezone))))) (YEARLY (icr:find-yearly-interval target dtstart intsize vtimezone)))))
(defun icr:nth-interval (n dtstart recur-value &optional vtimezone) (defun icr:nth-interval (n dtstart rrule &optional vtimezone)
"Return the Nth recurrence interval after DTSTART. "Return the Nth recurrence interval after DTSTART.
The returned value is an interval [LOW HIGH NEXT-LOW] which is the Nth The returned value is an interval [LOW HIGH NEXT-LOW] which is the Nth
recurrence interval after DTSTART. LOW is equal to START + recurrence interval after DTSTART. LOW is equal to START +
N*INTERVALSIZE units, HIGH is equal to START + (N+1)*INTERVALSIZE units, N*INTERVALSIZE units, HIGH is equal to START + (N+1)*INTERVALSIZE units,
and LOW <= TARGET < HIGH. START here is a time derived from DTSTART and LOW <= TARGET < HIGH. START here is a time derived from DTSTART
depending on RECUR-VALUE's FREQ part: the first day of the year for a depending on RRULE's FREQ part: the first day of the year for a
\\='YEARLY rule, first day of the month for a \\='MONTHLY rule, etc. \\='YEARLY rule, first day of the month for a \\='MONTHLY rule, etc.
RECUR-VALUE's interval determines INTERVALSIZE, and its frequency RRULE's interval determines INTERVALSIZE, and its frequency
determines the units: a month for \\='MONTHLY, etc. determines the units: a month for \\='MONTHLY, etc.
N should be a non-negative integer. Interval 0 is the interval N should be a non-negative integer. Interval 0 is the interval
containing DTSTART. DTSTART should be an `icalendar-date' or containing DTSTART. DTSTART should be an `icalendar-date' or
`icalendar-date-time' value. RECUR-VALUE should be an `icalendar-date-time' value. RRULE should be an
`icalendar-recur'. `icalendar-recur'.
If VTIMEZONE is provided, it is used to set time zone information in the If VTIMEZONE is provided, it is used to set time zone information in the
@ -509,8 +509,8 @@ information and represent floating local times."
(let* ((start-dt (if (cl-typep dtstart 'ical:date) (let* ((start-dt (if (cl-typep dtstart 'ical:date)
(ical:date-to-date-time dtstart :tz vtimezone) (ical:date-to-date-time dtstart :tz vtimezone)
dtstart)) dtstart))
(freq (ical:recur-freq recur-value)) (freq (ical:rrule-freq rrule))
(intervalsize (ical:recur-interval-size recur-value)) (intervalsize (ical:rrule-interval-size rrule))
(unit (cl-case freq (unit (cl-case freq
(YEARLY :year) (YEARLY :year)
(MONTHLY :month) (MONTHLY :month)
@ -520,16 +520,16 @@ information and represent floating local times."
(MINUTELY :minute) (MINUTELY :minute)
(SECONDLY :second))) (SECONDLY :second)))
(target (ical:date/time-add start-dt unit (* n intervalsize) vtimezone))) (target (ical:date/time-add start-dt unit (* n intervalsize) vtimezone)))
(icr:find-interval target dtstart recur-value vtimezone))) (icr:find-interval target dtstart rrule vtimezone)))
(defun icr:next-interval (interval recur-value &optional vtimezone) (defun icr:next-interval (interval rrule &optional vtimezone)
"Return the next recurrence interval after INTERVAL. "Return the next recurrence interval after INTERVAL.
Given a recurrence interval [LOW HIGH NEXT], returns the next interval Given a recurrence interval [LOW HIGH NEXT], returns the next interval
[NEXT HIGHER HIGHER-NEXT], where HIGHER and HIGHER-NEXT are determined [NEXT HIGHER HIGHER-NEXT], where HIGHER and HIGHER-NEXT are determined
by the frequency and interval sizes of RECUR-VALUE." by the frequency and interval sizes of RRULE."
(let* ((new-low (icr:interval-next interval)) (let* ((new-low (icr:interval-next interval))
(freq (ical:recur-freq recur-value)) (freq (ical:rrule-freq rrule))
(unit (cl-case freq (unit (cl-case freq
(YEARLY :year) (YEARLY :year)
(MONTHLY :month) (MONTHLY :month)
@ -538,7 +538,7 @@ by the frequency and interval sizes of RECUR-VALUE."
(HOURLY :hour) (HOURLY :hour)
(MINUTELY :minute) (MINUTELY :minute)
(SECONDLY :second))) (SECONDLY :second)))
(intervalsize (ical:recur-interval-size recur-value)) (intervalsize (ical:rrule-interval-size rrule))
(new-high (ical:date/time-add new-low unit 1 vtimezone)) (new-high (ical:date/time-add new-low unit 1 vtimezone))
(new-next (new-next
(when (< 1 intervalsize) (when (< 1 intervalsize)
@ -552,17 +552,17 @@ by the frequency and interval sizes of RECUR-VALUE."
(icr:make-interval new-low new-high new-next))) (icr:make-interval new-low new-high new-next)))
(defun icr:previous-interval (interval recur-value dtstart &optional vtimezone) (defun icr:previous-interval (interval rrule dtstart &optional vtimezone)
"Given a recurrence INTERVAL, return the previous interval. "Given a recurrence INTERVAL, return the previous interval.
For an interval [LOW HIGH NEXT-LOW], the previous interval is For an interval [LOW HIGH NEXT-LOW], the previous interval is
[PREV-LOW PREV-HIGH LOW], where PREV-LOW and PREV-HIGH are determined by [PREV-LOW PREV-HIGH LOW], where PREV-LOW and PREV-HIGH are determined by
the frequency and interval sizes of RECUR-VALUE (see the frequency and interval sizes of RRULE (see
`icalendar-recur-find-interval'). If the resulting period of time `icalendar-recur-find-interval'). If the resulting period of time
between PREV-LOW and PREV-HIGH occurs entirely before DTSTART, then the between PREV-LOW and PREV-HIGH occurs entirely before DTSTART, then the
interval does not exist; in this case nil is returned." interval does not exist; in this case nil is returned."
(let* ((upper (icr:interval-low interval)) (let* ((upper (icr:interval-low interval))
(freq (ical:recur-freq recur-value)) (freq (ical:rrule-freq rrule))
(unit (cl-case freq (unit (cl-case freq
(YEARLY :year) (YEARLY :year)
(MONTHLY :month) (MONTHLY :month)
@ -571,7 +571,7 @@ interval does not exist; in this case nil is returned."
(HOURLY :hour) (HOURLY :hour)
(MINUTELY :minute) (MINUTELY :minute)
(SECONDLY :second))) (SECONDLY :second)))
(intervalsize (ical:recur-interval-size recur-value)) (intervalsize (ical:rrule-interval-size rrule))
(new-low (ical:date/time-add upper unit (* -1 intervalsize) vtimezone)) (new-low (ical:date/time-add upper unit (* -1 intervalsize) vtimezone))
(new-high (new-high
(if (< 1 intervalsize) (if (< 1 intervalsize)
@ -1032,12 +1032,12 @@ The returned value is RECURRENCES filtered by index."
(pop dts)) (pop dts))
(nreverse r))) (nreverse r)))
(defun icr:refine-from-clauses (interval recur-value dtstart (defun icr:refine-from-clauses (interval rrule dtstart
&optional vtimezone) &optional vtimezone)
"Resolve INTERVAL into subintervals based on the clauses in RECUR-VALUE. "Resolve INTERVAL into subintervals based on the clauses in RRULE.
The resulting list of subintervals represents all times in INTERVAL The resulting list of subintervals represents all times in INTERVAL
which match the BY* clauses of RECUR-VALUE except BYSETPOS, as well as which match the BY* clauses of RRULE except BYSETPOS, as well as
the constraints implicit in DTSTART. (For example, if there is no the constraints implicit in DTSTART. (For example, if there is no
BYMINUTE clause, subintervals will have the same minute value as BYMINUTE clause, subintervals will have the same minute value as
DTSTART.) DTSTART.)
@ -1047,14 +1047,14 @@ components and TZID should be the `icalendar-tzid' property value of one
of those timezones. In this case, TZID states the time zone of DTSTART, of those timezones. In this case, TZID states the time zone of DTSTART,
and the offsets effective in that time zone on the dates and times of and the offsets effective in that time zone on the dates and times of
recurrences will be local to that time zone." recurrences will be local to that time zone."
(let ((freq (ical:recur-freq recur-value)) (let ((freq (ical:rrule-freq rrule))
(weekstart (ical:recur-weekstart recur-value)) (weekstart (ical:rrule-weekstart rrule))
(subintervals (list interval))) (subintervals (list interval)))
(dolist (byunit (list 'BYMONTH 'BYWEEKNO (dolist (byunit (list 'BYMONTH 'BYWEEKNO
'BYYEARDAY 'BYMONTHDAY 'BYDAY 'BYYEARDAY 'BYMONTHDAY 'BYDAY
'BYHOUR 'BYMINUTE 'BYSECOND)) 'BYHOUR 'BYMINUTE 'BYSECOND))
(let ((values (ical:recur-by* byunit recur-value)) (let ((values (ical:rrule-by* byunit rrule))
(in-month nil)) (in-month nil))
;; When there is no explicit BY* clause, use the value implicit ;; When there is no explicit BY* clause, use the value implicit
;; in DTSTART. (These conditions are adapted from RFC8984: ;; in DTSTART. (These conditions are adapted from RFC8984:
@ -1086,26 +1086,26 @@ recurrences will be local to that time zone."
(setq values (list (ical:date/time-weekday dtstart)))) (setq values (list (ical:date/time-weekday dtstart))))
(when (and (eq byunit 'BYMONTHDAY) (when (and (eq byunit 'BYMONTHDAY)
(eq freq 'MONTHLY) (eq freq 'MONTHLY)
(not (ical:recur-by* 'BYDAY recur-value)) (not (ical:rrule-by* 'BYDAY rrule))
(not values)) (not values))
(setq values (list (ical:date/time-monthday dtstart)))) (setq values (list (ical:date/time-monthday dtstart))))
(when (and (eq freq 'YEARLY) (when (and (eq freq 'YEARLY)
(not (ical:recur-by* 'BYYEARDAY recur-value))) (not (ical:rrule-by* 'BYYEARDAY rrule)))
(when (and (eq byunit 'BYMONTH) (when (and (eq byunit 'BYMONTH)
(not values) (not values)
(not (ical:recur-by* 'BYWEEKNO recur-value)) (not (ical:rrule-by* 'BYWEEKNO rrule))
(or (ical:recur-by* 'BYMONTHDAY recur-value) (or (ical:rrule-by* 'BYMONTHDAY rrule)
(not (ical:recur-by* 'BYDAY recur-value)))) (not (ical:rrule-by* 'BYDAY rrule))))
(setq values (list (ical:date/time-month dtstart)))) (setq values (list (ical:date/time-month dtstart))))
(when (and (eq byunit 'BYMONTHDAY) (when (and (eq byunit 'BYMONTHDAY)
(not values) (not values)
(not (ical:recur-by* 'BYWEEKNO recur-value)) (not (ical:rrule-by* 'BYWEEKNO rrule))
(not (ical:recur-by* 'BYDAY recur-value))) (not (ical:rrule-by* 'BYDAY rrule)))
(setq values (list (ical:date/time-monthday dtstart)))) (setq values (list (ical:date/time-monthday dtstart))))
(when (and (eq byunit 'BYDAY) (when (and (eq byunit 'BYDAY)
(not values) (not values)
(ical:recur-by* 'BYWEEKNO recur-value) (ical:rrule-by* 'BYWEEKNO rrule)
(not (ical:recur-by* 'BYMONTHDAY recur-value))) (not (ical:rrule-by* 'BYMONTHDAY rrule)))
(setq values (list (ical:date/time-weekday dtstart))))) (setq values (list (ical:date/time-weekday dtstart)))))
;; Handle offsets in a BYDAY clause: ;; Handle offsets in a BYDAY clause:
@ -1120,7 +1120,7 @@ recurrences will be local to that time zone."
(when (and (eq byunit 'BYDAY) (when (and (eq byunit 'BYDAY)
(or (eq freq 'MONTHLY) (or (eq freq 'MONTHLY)
(and (eq freq 'YEARLY) (and (eq freq 'YEARLY)
(ical:recur-by* 'BYMONTH recur-value)))) (ical:rrule-by* 'BYMONTH rrule))))
(setq in-month t)) (setq in-month t))
;; On each iteration of the loop, we refine the subintervals ;; On each iteration of the loop, we refine the subintervals
@ -1246,10 +1246,10 @@ retrieved on subsequent calls with the same arguments."
(ical:with-component component (ical:with-component component
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:tzoffsetfrom :value offset-from) (ical:tzoffsetfrom :value offset-from)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes) ;; TODO: these can also be ical:period values (ical:rdate :all rdate-nodes) ;; TODO: these can also be ical:period values
(ical:exdate :all exdate-nodes)) (ical:exdate :all exdate-nodes))
(if (not (or recur-value rdate-nodes)) (if (not (or rrule rdate-nodes))
;; No recurrences to calculate, so just return early: ;; No recurrences to calculate, so just return early:
nil nil
;; Otherwise, calculate recurrences in the interval: ;; Otherwise, calculate recurrences in the interval:
@ -1267,19 +1267,19 @@ retrieved on subsequent calls with the same arguments."
(t (t
(let* (;; Start by generating all the recurrences matching the (let* (;; Start by generating all the recurrences matching the
;; BY* clauses except for BYSETPOS: ;; BY* clauses except for BYSETPOS:
(subs (icr:refine-from-clauses interval recur-value dtstart (subs (icr:refine-from-clauses interval rrule dtstart
vtimezone)) vtimezone))
(sub-recs (icr:subintervals-to-recurrences subs dtstart (sub-recs (icr:subintervals-to-recurrences subs dtstart
vtimezone)) vtimezone))
;; Apply any BYSETPOS clause to this set: ;; Apply any BYSETPOS clause to this set:
(keep-indices (ical:recur-by* 'BYSETPOS recur-value)) (keep-indices (ical:rrule-by* 'BYSETPOS rrule))
(pos-recs (pos-recs
(if keep-indices (if keep-indices
(icr:bysetpos-filter keep-indices sub-recs) (icr:bysetpos-filter keep-indices sub-recs)
sub-recs)) sub-recs))
;; Remove any recurrences before DTSTART or after UNTIL ;; Remove any recurrences before DTSTART or after UNTIL
;; (both of which are inclusive bounds): ;; (both of which are inclusive bounds):
(until (ical:recur-until recur-value)) (until (ical:rrule-until rrule))
(until-recs (until-recs
(seq-filter (seq-filter
(lambda (rec) (and (ical:date/time<= dtstart rec) (lambda (rec) (and (ical:date/time<= dtstart rec)
@ -1345,9 +1345,9 @@ UTC offsets local to that time zone."
(ical:with-component component (ical:with-component component
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:tzoffsetfrom :value offset-from) (ical:tzoffsetfrom :value offset-from)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes)) (ical:rdate :all rdate-nodes))
(if (not (or recur-value rdate-nodes)) (if (not (or rrule rdate-nodes))
;; No recurrences to calculate, so just return early: ;; No recurrences to calculate, so just return early:
nil nil
;; Otherwise, calculate the recurrences in the window: ;; Otherwise, calculate the recurrences in the window:
@ -1362,11 +1362,11 @@ UTC offsets local to that time zone."
(let* (;; don't look for nonexistent intervals: (let* (;; don't look for nonexistent intervals:
(low-start (if (ical:date/time< lower dtstart) dtstart lower)) (low-start (if (ical:date/time< lower dtstart) dtstart lower))
(until (ical:recur-until recur-value)) (until (ical:rrule-until rrule))
(high-end (if (and until (ical:date/time< until upper)) until upper)) (high-end (if (and until (ical:date/time< until upper)) until upper))
(curr-interval (icr:find-interval low-start dtstart recur-value (curr-interval (icr:find-interval low-start dtstart rrule
vtimezone)) vtimezone))
(high-interval (icr:find-interval high-end dtstart recur-value (high-interval (icr:find-interval high-end dtstart rrule
vtimezone)) vtimezone))
(high-intbound (icr:interval-high high-interval)) (high-intbound (icr:interval-high high-interval))
(recurrences nil)) (recurrences nil))
@ -1376,7 +1376,7 @@ UTC offsets local to that time zone."
(nconc (nconc
(icr:recurrences-in-interval curr-interval component vtimezone) (icr:recurrences-in-interval curr-interval component vtimezone)
recurrences)) recurrences))
(setq curr-interval (icr:next-interval curr-interval recur-value (setq curr-interval (icr:next-interval curr-interval rrule
vtimezone))) vtimezone)))
;; exclude any recurrences inside the first and last intervals but ;; exclude any recurrences inside the first and last intervals but
@ -1447,7 +1447,7 @@ UTC offsets local to that time zone."
(ical:with-component component (ical:with-component component
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:tzoffsetfrom :value offset-from) (ical:tzoffsetfrom :value offset-from)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes)) (ical:rdate :all rdate-nodes))
(when (memq (ical:ast-node-type component) '(ical:standard ical:daylight)) (when (memq (ical:ast-node-type component) '(ical:standard ical:daylight))
;; in time zone observances, set the zone field in dtstart ;; in time zone observances, set the zone field in dtstart
@ -1457,18 +1457,18 @@ UTC offsets local to that time zone."
:zone offset-from :zone offset-from
:dst (not (ical:daylight-component-p :dst (not (ical:daylight-component-p
component))))) component)))))
(unless (or recur-value rdate-nodes) (unless (or rrule rdate-nodes)
(error "No recurrence data in component: %s" component)) (error "No recurrence data in component: %s" component))
(unless (ical:recur-count recur-value) (unless (ical:rrule-count rrule)
(error "Recurrence rule has no COUNT clause")) (error "Recurrence rule has no COUNT clause"))
(let ((count (ical:recur-count recur-value)) (let ((count (ical:rrule-count rrule))
(int (icr:nth-interval 0 dtstart recur-value vtimezone)) (int (icr:nth-interval 0 dtstart rrule vtimezone))
recs) recs)
(while (length< recs count) (while (length< recs count)
(setq recs (setq recs
(nconc recs (icr:recurrences-in-interval int component vtimezone (nconc recs (icr:recurrences-in-interval int component vtimezone
(- count (length recs))))) (- count (length recs)))))
(setq int (icr:next-interval int recur-value vtimezone))) (setq int (icr:next-interval int rrule vtimezone)))
recs))) recs)))
@ -1771,7 +1771,7 @@ ignored."
(dolist (obs (append stds dls)) (dolist (obs (append stds dls))
(ical:with-component obs (ical:with-component obs
((ical:dtstart :value start) ((ical:dtstart :value start)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:rdate :all rdate-nodes) (ical:rdate :all rdate-nodes)
(ical:tzoffsetfrom :value offset-from)) (ical:tzoffsetfrom :value offset-from))
;; DTSTART of the observance must be given as local time, and is ;; DTSTART of the observance must be given as local time, and is
@ -1781,7 +1781,7 @@ ignored."
(effective-start (effective-start
(ical:date-time-variant start :zone offset-from (ical:date-time-variant start :zone offset-from
:dst (not is-daylight))) :dst (not is-daylight)))
(until (ical:recur-until recur-value)) (until (ical:rrule-until rrule))
(bound (bound
;; Optimization: compute a rough upper bound for when ;; Optimization: compute a rough upper bound for when
;; an observance might apply, thus allowing us to skip ;; an observance might apply, thus allowing us to skip
@ -1796,8 +1796,8 @@ ignored."
(when until (when until
(ical:date-time-variant until (ical:date-time-variant until
:year (+ (decoded-time-year until) :year (+ (decoded-time-year until)
(ical:recur-interval-size (ical:rrule-interval-size
recur-value))))) rrule)))))
(observance-might-apply (observance-might-apply
(if given-clock-time (if given-clock-time
(icr:-w/in-locally-p given-clock-time effective-start bound) (icr:-w/in-locally-p given-clock-time effective-start bound)
@ -1859,11 +1859,11 @@ ignored."
;; start of each observance onset), which ;; start of each observance onset), which
;; `icr:tz-set-zone' knows to handle specially without ;; `icr:tz-set-zone' knows to handle specially without
;; calling this function. ;; calling this function.
(when recur-value (when rrule
(let* ((target (or given-clock-time (let* ((target (or given-clock-time
(decode-time given-abs-time offset-from))) (decode-time given-abs-time offset-from)))
(int (icr:find-interval (int (icr:find-interval
target effective-start recur-value offset-from)) target effective-start rrule offset-from))
(<=given (<=given
(if given-clock-time (if given-clock-time
(lambda (rec) (lambda (rec)
@ -1883,7 +1883,7 @@ ignored."
;; actually be in the previous interval, e.g. ;; actually be in the previous interval, e.g.
;; if `dt' is in January after an annual change to ;; if `dt' is in January after an annual change to
;; Standard Time in November. So check that as well. ;; Standard Time in November. So check that as well.
(setq int (icr:previous-interval int recur-value (setq int (icr:previous-interval int rrule
effective-start effective-start
offset-from)) offset-from))
(setq int-recs (setq int-recs

View file

@ -252,22 +252,22 @@
"(icalendar-recur-recurrences-to-count '(1 1 2026) '(12 31 2026) "(icalendar-recur-recurrences-to-count '(1 1 2026) '(12 31 2026)
vevent)" vevent)"
:eg-result-string "((1 10 2026) (2 10 2026) (3 10 2026))") :eg-result-string "((1 10 2026) (2 10 2026) (3 10 2026))")
(icalendar-recur-freq (icalendar-rrule-freq
:eval :eval
(icalendar-recur-freq '((FREQ MONTHLY) (INTERVAL 3) (BYDAY ((5 . -1)))))) (icalendar-rrule-freq '((FREQ MONTHLY) (INTERVAL 3) (BYDAY ((5 . -1))))))
(icalendar-recur-interval-size (icalendar-rrule-interval-size
:eval (icalendar-recur-interval-size '((FREQ MONTHLY) (BYDAY ((5 . -1))))) :eval (icalendar-rrule-interval-size '((FREQ MONTHLY) (BYDAY ((5 . -1)))))
:eval (icalendar-recur-interval-size '((FREQ MONTHLY) (INTERVAL 3)))) :eval (icalendar-rrule-interval-size '((FREQ MONTHLY) (INTERVAL 3))))
(icalendar-recur-count (icalendar-rrule-count
:eval (icalendar-recur-count '((FREQ MONTHLY) (INTERVAL 2) (COUNT 6)))) :eval (icalendar-rrule-count '((FREQ MONTHLY) (INTERVAL 2) (COUNT 6))))
(icalendar-recur-until (icalendar-rrule-until
:eval (icalendar-recur-until '((FREQ WEEKLY) (UNTIL (12 31 2026))))) :eval (icalendar-rrule-until '((FREQ WEEKLY) (UNTIL (12 31 2026)))))
(icalendar-recur-by* (icalendar-rrule-by*
:eval (icalendar-recur-by* 'BYDAY '((FREQ MONTHLY) (BYDAY ((5 . -1)))))) :eval (icalendar-rrule-by* 'BYDAY '((FREQ MONTHLY) (BYDAY ((5 . -1))))))
(icalendar-recur-weekstart (icalendar-rrule-weekstart
:eval :eval
(icalendar-recur-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)) (WKST 0))) (icalendar-rrule-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)) (WKST 0)))
:eval :eval
(icalendar-recur-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)))))) (icalendar-rrule-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026))))))
(provide 'icalendar-shortdoc) (provide 'icalendar-shortdoc)

View file

@ -131,18 +131,18 @@
(cl-defmethod gnus-icalendar-event:recurring-freq ((event gnus-icalendar-event)) (cl-defmethod gnus-icalendar-event:recurring-freq ((event gnus-icalendar-event))
"Return recurring frequency of EVENT." "Return recurring frequency of EVENT."
(ical:recur-freq (gnus-icalendar-event:recur event))) (ical:rrule-freq (gnus-icalendar-event:recur event)))
(cl-defmethod gnus-icalendar-event:recurring-interval ((event gnus-icalendar-event)) (cl-defmethod gnus-icalendar-event:recurring-interval ((event gnus-icalendar-event))
"Return recurring interval of EVENT." "Return recurring interval of EVENT."
(ical:recur-interval-size (gnus-icalendar-event:recur event))) (ical:rrule-interval-size (gnus-icalendar-event:recur event)))
(cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event)) (cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event))
"Return, when available, the week day numbers on which the EVENT recurs." "Return, when available, the week day numbers on which the EVENT recurs."
(let ((rrule (gnus-icalendar-event:recur event))) (let ((rrule (gnus-icalendar-event:recur event)))
(when rrule (when rrule
(mapcar (lambda (el) (if (consp el) (car el) el)) (mapcar (lambda (el) (if (consp el) (car el) el))
(ical:recur-by* 'BYDAY rrule))))) (ical:rrule-by* 'BYDAY rrule)))))
(cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event)) (cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event))
(format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event))) (format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event)))

View file

@ -817,8 +817,8 @@ SOURCE, if given, should be a symbol; it is used to name the test."
(should (equal (ical:date-time-to-date dtstart) (should (equal (ical:date-time-to-date dtstart)
(calendar-nth-named-day 1 4 1 di:recurring-start-year))) (calendar-nth-named-day 1 4 1 di:recurring-start-year)))
(should (= 16 (decoded-time-hour dtstart))) (should (= 16 (decoded-time-hour dtstart)))
(should (eq (ical:recur-freq rrule) 'WEEKLY)) (should (eq (ical:rrule-freq rrule) 'WEEKLY))
(should (equal (ical:recur-by* 'BYDAY rrule) (list 4))))) (should (equal (ical:rrule-by* 'BYDAY rrule) (list 4)))))
(dit:parse-test (dit:parse-test
;; Multiline entry, parsed as one event: ;; Multiline entry, parsed as one event:
@ -961,10 +961,10 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart '(5 28 1995))) (should (equal dtstart '(5 28 1995)))
(should (eq (ical:recur-freq recur-value) 'YEARLY)) (should (eq (ical:rrule-freq rrule) 'YEARLY))
(should (equal summary "H's birthday")))) (should (equal summary "H's birthday"))))
(dit:parse-test (dit:parse-test
@ -977,11 +977,11 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart '(6 24 2012))) (should (equal dtstart '(6 24 2012)))
(should (equal (ical:recur-freq recur-value) 'DAILY)) (should (equal (ical:rrule-freq rrule) 'DAILY))
(should (equal (ical:recur-until recur-value) '(7 10 2012))) (should (equal (ical:rrule-until rrule) '(7 10 2012)))
(should (equal summary "Vacation")))) (should (equal summary "Vacation"))))
(dit:parse-test (dit:parse-test
@ -994,11 +994,11 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart '(3 1 2012))) (should (equal dtstart '(3 1 2012)))
(should (eq (ical:recur-freq recur-value) 'DAILY)) (should (eq (ical:rrule-freq rrule) 'DAILY))
(should (eq (ical:recur-interval-size recur-value) 50)) (should (eq (ical:rrule-interval-size rrule) 50))
(should (equal summary "Renew medication")))) (should (equal summary "Renew medication"))))
(dit:parse-test (dit:parse-test
@ -1011,13 +1011,13 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart (should (equal dtstart
(calendar-nth-named-day 4 4 11 di:recurring-start-year))) (calendar-nth-named-day 4 4 11 di:recurring-start-year)))
(should (eq (ical:recur-freq recur-value) 'MONTHLY)) (should (eq (ical:rrule-freq rrule) 'MONTHLY))
(should (equal (ical:recur-by* 'BYMONTH recur-value) (list 11))) (should (equal (ical:rrule-by* 'BYMONTH rrule) (list 11)))
(should (equal (ical:recur-by* 'BYDAY recur-value) (list '(4 . 4)))) (should (equal (ical:rrule-by* 'BYDAY rrule) (list '(4 . 4))))
(should (equal summary "American Thanksgiving")))) (should (equal summary "American Thanksgiving"))))
(dit:parse-test (dit:parse-test
@ -1030,13 +1030,13 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart (should (equal dtstart
(calendar-nth-named-day 4 5 1 di:recurring-start-year))) (calendar-nth-named-day 4 5 1 di:recurring-start-year)))
(should (eq (ical:recur-freq recur-value) 'MONTHLY)) (should (eq (ical:rrule-freq rrule) 'MONTHLY))
;; day 3 is Wednesday, so offset of 2 means Friday (=5): ;; day 3 is Wednesday, so offset of 2 means Friday (=5):
(should (equal (ical:recur-by* 'BYDAY recur-value) (list '(5 . 4)))) (should (equal (ical:rrule-by* 'BYDAY rrule) (list '(5 . 4))))
(should (equal summary "Monthly committee meeting")))) (should (equal summary "Monthly committee meeting"))))
(dit:parse-test (dit:parse-test
@ -1052,11 +1052,11 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:exdate :values exdates) (ical:exdate :values exdates)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart '(11 11 2024))) (should (equal dtstart '(11 11 2024)))
(should (eq (ical:recur-freq recur-value) 'WEEKLY)) (should (eq (ical:rrule-freq rrule) 'WEEKLY))
(should (equal exdates '((12 23 2024) (12 30 2024)))) (should (equal exdates '((12 23 2024) (12 30 2024))))
(should (equal summary "Reading group")))) (should (equal summary "Reading group"))))
@ -1070,12 +1070,12 @@ SOURCE, if given, should be a symbol; it is used to name the test."
:tests :tests
(ical:with-component (car parsed) (ical:with-component (car parsed)
((ical:dtstart :value dtstart) ((ical:dtstart :value dtstart)
(ical:rrule :value recur-value) (ical:rrule :value rrule)
(ical:summary :value summary)) (ical:summary :value summary))
(should (equal dtstart (list 10 22 di:recurring-start-year))) (should (equal dtstart (list 10 22 di:recurring-start-year)))
(should (eq (ical:recur-freq recur-value) 'YEARLY)) (should (eq (ical:rrule-freq rrule) 'YEARLY))
(should (equal (ical:recur-by* 'BYMONTH recur-value) (list 10 11 12))) (should (equal (ical:rrule-by* 'BYMONTH rrule) (list 10 11 12)))
(should (equal (ical:recur-by* 'BYMONTHDAY recur-value) (list 22))) (should (equal (ical:rrule-by* 'BYMONTHDAY rrule) (list 22)))
(should (equal summary "Rake leaves")))) (should (equal summary "Rake leaves"))))
(dit:parse-test (dit:parse-test

View file

@ -388,21 +388,21 @@ test."
(ipt:parse/print-test (ipt:parse/print-test
"FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1"
:type icalendar-recur :type icalendar-rrule-value
:parser icalendar-parse-value-node :parser icalendar-parse-value-node
:printer icalendar-print-value-node :printer icalendar-print-value-node
:source rfc5545-sec3.3.10/1) :source rfc5545-sec3.3.10/1)
(ipt:parse/print-test (ipt:parse/print-test
"FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30" "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30"
:type icalendar-recur :type icalendar-rrule-value
:parser icalendar-parse-value-node :parser icalendar-parse-value-node
:printer icalendar-print-value-node :printer icalendar-print-value-node
:source rfc5545-sec3.3.10/2) :source rfc5545-sec3.3.10/2)
(ipt:parse/print-test (ipt:parse/print-test
"FREQ=DAILY;COUNT=10;INTERVAL=2" "FREQ=DAILY;COUNT=10;INTERVAL=2"
:type icalendar-recur :type icalendar-rrule-value
:parser icalendar-parse-value-node :parser icalendar-parse-value-node
:printer icalendar-print-value-node :printer icalendar-print-value-node
:source rfc5545-sec3.3.10/3) :source rfc5545-sec3.3.10/3)

View file

@ -1383,7 +1383,7 @@ END:VTIMEZONE
(ts-obs/onset (icr:tz-observance-on ts ict:tz-eastern))) (ts-obs/onset (icr:tz-observance-on ts ict:tz-eastern)))
(should (eq 'ical:daylight (ical:ast-node-type obs))) (should (eq 'ical:daylight (ical:ast-node-type obs)))
(should (equal dt onset)) (should (equal dt onset))
(should (equal end (ical:recur-until (should (equal end (ical:rrule-until
(ical:with-property-of obs 'ical:rrule nil value)))) (ical:with-property-of obs 'ical:rrule nil value))))
(should (equal obs/onset ts-obs/onset))) (should (equal obs/onset ts-obs/onset)))
@ -1534,10 +1534,10 @@ SOURCE should be a symbol; it is used to name the test."
,(format "Parse and evaluate recur-value example from `%s':\n%s" ,(format "Parse and evaluate recur-value example from `%s':\n%s"
source doc) source doc)
:tags ,tags :tags ,tags
(let* ((parsed (ical:parse-from-string 'ical:recur ,recur-string)) (let* ((parsed (ical:parse-from-string 'ical:rrule-value ,recur-string))
(recvalue (ical:ast-node-value parsed)) (recvalue (ical:ast-node-value parsed))
(until (ical:recur-until recvalue)) (until (ical:rrule-until recvalue))
(count (ical:recur-count recvalue)) (count (ical:rrule-count recvalue))
(dtstart ,dtstart) (dtstart ,dtstart)
(tzid (tzid
(when (cl-typep dtstart 'ical:date-time) (when (cl-typep dtstart 'ical:date-time)