Function: icalendar-recur-refine-from-clauses
icalendar-recur-refine-from-clauses is a byte-compiled function
defined in icalendar-recur.el.gz.
Signature
(icalendar-recur-refine-from-clauses INTERVAL RRULE DTSTART &optional VTIMEZONE)
Documentation
Resolve INTERVAL into subintervals based on the clauses in RRULE.
The resulting list of subintervals represents all times in INTERVAL which match the BY* clauses of RRULE except BYSETPOS, as well as the constraints implicit in DTSTART. (For example, if there is no BYMINUTE clause, subintervals will have the same minute value as DTSTART.)
If specified, VTIMEZONES should be a list of icalendar-vtimezone
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,
and the offsets effective in that time zone on the dates and times of
recurrences will be local to that time zone.
Source Code
;; Defined in /usr/src/emacs/lisp/calendar/icalendar-recur.el.gz
(defun icr:refine-from-clauses (interval rrule dtstart
&optional vtimezone)
"Resolve INTERVAL into subintervals based on the clauses in RRULE.
The resulting list of subintervals represents all times in INTERVAL
which match the BY* clauses of RRULE except BYSETPOS, as well as
the constraints implicit in DTSTART. (For example, if there is no
BYMINUTE clause, subintervals will have the same minute value as
DTSTART.)
If specified, VTIMEZONES should be a list of `icalendar-vtimezone'
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,
and the offsets effective in that time zone on the dates and times of
recurrences will be local to that time zone."
(let ((freq (ical:rrule-freq rrule))
(weekstart (ical:rrule-weekstart rrule))
(subintervals (list interval)))
(dolist (byunit (list 'BYMONTH 'BYWEEKNO
'BYYEARDAY 'BYMONTHDAY 'BYDAY
'BYHOUR 'BYMINUTE 'BYSECOND))
(let ((values (ical:rrule-by* byunit rrule))
(in-month nil))
;; When there is no explicit BY* clause, use the value implicit
;; in DTSTART. (These conditions are adapted from RFC8984:
;; https://www.rfc-editor.org/rfc/rfc8984.html#section-4.3.3.1-4.3.1
;; Basically, the conditions are somewhat complicated because
;; the meanings of various BY* clauses are not independent and
;; so we have to be careful about the information we take to be
;; implicit in DTSTART, especially with MONTHLY and YEARLY
;; rules. For example, we *do* want to take the weekday of
;; DTSTART as an implicit constraint if a BYWEEKNO clause is
;; present, but not if an explicit BYDAY or BYMONTHDAY clause is
;; also present, since they might contain conflicting
;; constraints.)
(when (and (eq byunit 'BYSECOND)
(not (eq freq 'SECONDLY))
(not values))
(setq values (list (ical:date/time-second dtstart))))
(when (and (eq byunit 'BYMINUTE)
(not (memq freq '(SECONDLY MINUTELY)))
(not values))
(setq values (list (ical:date/time-minute dtstart))))
(when (and (eq byunit 'BYHOUR)
(not (memq freq '(SECONDLY MINUTELY HOURLY)))
(not values))
(setq values (list (ical:date/time-hour dtstart))))
(when (and (eq byunit 'BYDAY)
(eq freq 'WEEKLY)
(not values))
(setq values (list (ical:date/time-weekday dtstart))))
(when (and (eq byunit 'BYMONTHDAY)
(eq freq 'MONTHLY)
(not (ical:rrule-by* 'BYDAY rrule))
(not values))
(setq values (list (ical:date/time-monthday dtstart))))
(when (and (eq freq 'YEARLY)
(not (ical:rrule-by* 'BYYEARDAY rrule)))
(when (and (eq byunit 'BYMONTH)
(not values)
(not (ical:rrule-by* 'BYWEEKNO rrule))
(or (ical:rrule-by* 'BYMONTHDAY rrule)
(not (ical:rrule-by* 'BYDAY rrule))))
(setq values (list (ical:date/time-month dtstart))))
(when (and (eq byunit 'BYMONTHDAY)
(not values)
(not (ical:rrule-by* 'BYWEEKNO rrule))
(not (ical:rrule-by* 'BYDAY rrule)))
(setq values (list (ical:date/time-monthday dtstart))))
(when (and (eq byunit 'BYDAY)
(not values)
(ical:rrule-by* 'BYWEEKNO rrule)
(not (ical:rrule-by* 'BYMONTHDAY rrule)))
(setq values (list (ical:date/time-weekday dtstart)))))
;; Handle offsets in a BYDAY clause:
;; "If present, this [offset] indicates the nth occurrence of a
;; specific day within the MONTHLY or YEARLY "RRULE". For
;; example, within a MONTHLY rule, +1MO (or simply 1MO)
;; represents the first Monday within the month, whereas -1MO
;; represents the last Monday of the month. The numeric value
;; in a BYDAY rule part with the FREQ rule part set to YEARLY
;; corresponds to an offset within the month when the BYMONTH
;; rule part is present"
(when (and (eq byunit 'BYDAY)
(or (eq freq 'MONTHLY)
(and (eq freq 'YEARLY)
(ical:rrule-by* 'BYMONTH rrule))))
(setq in-month t))
;; On each iteration of the loop, we refine the subintervals
;; with these explicit or implicit values:
(when values
(setq subintervals
(delq nil
(mapcan (lambda (in)
(icr:refine-by byunit in values in-month
weekstart vtimezone))
subintervals))))))
;; Finally return the refined subintervals after we've looked at all
;; clauses:
subintervals))