Function: diary-icalendar-dates-to-recurrence

diary-icalendar-dates-to-recurrence is a byte-compiled function defined in diary-icalendar.el.gz.

Signature

(diary-icalendar-dates-to-recurrence MONTHS DAYS YEARS)

Documentation

Convert values representing one or more dates to iCalendar recurrences.

MONTHS, DAYS, and YEARS should either be integers, lists of integers, or the symbol t.

Returns a pair of nodes (START R), where START is an icalendar-dtstart node and R is an icalendar-rrule node or icalendar-rdate node (or nil, if MONTHS, DAYS and YEARS are all integers).

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/diary-icalendar.el.gz
(defun di:dates-to-recurrence (months days years)
  "Convert values representing one or more dates to iCalendar recurrences.

MONTHS, DAYS, and YEARS should either be integers, lists of integers, or
the symbol t.

Returns a pair of nodes (START R), where START is an `icalendar-dtstart'
node and R is an `icalendar-rrule' node or `icalendar-rdate' node (or
nil, if MONTHS, DAYS and YEARS are all integers)."
  (if (and (integerp months) (integerp days) (integerp years))
      ;; just a regular date, without recurrence data:
      (list
       (ical:make-property ical:dtstart (list months days years))
       nil)

    (when (integerp months) (setq months (list months)))
    (when (integerp days) (setq days (list days)))
    (when (integerp years) (setq years (list years)))
    (let (dtstart freq bymonth bymonthday rdates rdate-type)
      (cond ((and (eq days t) (eq months t) (eq years t))
             (setq freq 'DAILY
                   dtstart (list 1 1 di:recurring-start-year)))
            ((and (eq months t) (eq years t))
             (setq freq 'MONTHLY
                   bymonthday days
                   dtstart (list 1 (car days) di:recurring-start-year)))
            ((and (eq years t) (eq days t))
             (setq freq 'DAILY
                   bymonth months
                   dtstart (list (apply #'min months)
                                 1
                                 di:recurring-start-year)))
            ((eq years t)
             (setq freq 'YEARLY
                   bymonth months
                   bymonthday days
                   dtstart
                   (list (apply #'min months)
                         (apply #'min days)
                         di:recurring-start-year)))
            ;; The remaining cases are not representable as RRULEs,
            ;; because there is no BYYEAR clause.  So we generate an RDATE
            ;; covering each specified date.
            ((and (eq months t) (eq days t))
             ;; In this case we represent each of the specified years as a period:
             (setq rdate-type 'ical:period
                   rdates
                   (mapcar
                    (lambda (y)
                      (ical:make-period
                       (ical:make-date-time :year y :month 1 :day 1
                                            :hour 0 :minute 0 :second 0)
                       :end
                       (ical:make-date-time :year (1+ y) :month 1 :day 1
                                            :hour 0 :minute 0 :second 0)))
                    years)
                   dtstart (ical:date-time-to-date
                            (ical:period-start (car rdates)))))
            (t
             ;; Otherwise, represent each date individually:
             (setq rdate-type 'ical:date
                   rdates
                   (mapcan
                    (lambda (y)
                      (mapcan
                       (lambda (m)
                         (mapcar
                          (lambda (d) (list m d y))
                          (if (listp days) days
                            ;; days = t:
                            (number-sequence 1 (calendar-last-day-of-month m y)))))
                       (if (listp months) months
                         ;; months = t:
                         (number-sequence 1 12))))
                    years)
                   ;; ensure dtstart is the earliest recurrence:
                   dtstart (apply #'ical:date/time-min rdates)
                   rdates (seq-remove (apply-partially #'equal dtstart) rdates))))

      ;; Return the pair of nodes (DTSTART RRULE) or (DTSTART RDATE):
      (let* ((rrule-value
              (delq nil
                    `((FREQ ,freq)
                      ,(when bymonth (list 'BYMONTH bymonth))
                      ,(when bymonthday (list 'BYMONTHDAY bymonthday)))))
           (rrule-node (when freq (ical:make-property ical:rrule rrule-value)))
           (rdate-node (when rdates
                         (ical:make-property ical:rdate rdates
                           (ical:valuetypeparam rdate-type))))
           (dtstart-node (ical:make-property ical:dtstart dtstart)))
        (list dtstart-node (or rrule-node rdate-node))))))