Function: diary-icalendar-float-sexp-to-recurrence

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

Signature

(diary-icalendar-float-sexp-to-recurrence SEXP)

Documentation

Convert diary-float SEXP to icalendar-dtstart and icalendar-rrule.

Returns a pair of nodes (START RRULE).

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/diary-icalendar.el.gz
(defun di:float-sexp-to-recurrence (sexp)
  "Convert `diary-float' SEXP to `icalendar-dtstart' and `icalendar-rrule'.
Returns a pair of nodes (START RRULE)."
  (let* ((month-exp (nth 1 sexp))
         (months (cond ((eq month-exp t) nil) ; don't add a BYMONTH clause
                       ((integerp month-exp) (list month-exp))
                       ((and (listp month-exp) (eq 'quote (car month-exp)))
                        (eval month-exp nil)) ; unquote a literal list of ints
                       (t month-exp)))
         (_ (unless (seq-every-p #'integerp months)
              (di:signal-export-error
               (format "Malformed month(s) in `diary-float' S-expression:\n%s"
                       sexp))))
         (dow (nth 2 sexp))
         (n (nth 3 sexp))
         (day (or (nth 4 sexp)
                  (if (< 0 n) 1
                    'last))) ; = "last day of the month" for any month
         ;; Calculate the offset within the month from day, n:
         (offset
          (cond ((eq day 'last) n)
                ((and (< 0 day) (< 0 n))
                 ;; In this case, to get the offset relative to
                 ;; the start of the month, we need to add to n
                 ;; the number of weeks in the month before day:
                 ;; e.g. if day = 8, n = 2, then we are looking
                 ;; for the second DOW after the 8th of the
                 ;; month, which is the 3rd DOW after the 1st of
                 ;; the month
                 (+ n (/ (1- day) 7)))
                ((and (< 0 day) (< n 0) (< day (* 7 (abs n))))
                 ;; In this case, we need to cross into the
                 ;; previous month and adjust the offset
                 ;; accordingly to reflect the correct number of
                 ;; weeks before the end of the month.
                 ;; e.g. if day = 15, n = -3, we're looking for the
                 ;; 3rd DOW before the 15th of the month,
                 ;; which is the 1st DOW "before" the end of the
                 ;; previous month (where "before" is inclusive,
                 ;; e.g offset = -1 will work when DOW is the last
                 ;; day of the month)
                 (when months
                   (setq months
                         (sort
                          :in-place t
                          (mapcar
                           (lambda (m) (if (eql m 1) 12 (1- m)))
                           months))))
                 (+ n (/ (1- day) 7)))))
         (rrule (delq nil
                      `((FREQ MONTHLY)
                        ,(when months
                           (list 'BYMONTH months))
                        (BYDAY ((,dow . ,offset))))))
         (dtstart
          (calendar-nth-named-day n dow
                                  (if months (apply #'min months) 1)
                                  di:recurring-start-year
                                  (unless (eq day 'last) day))))

    ;; if at this point we have an offset which could put us outside the
    ;; month boundaries, warn the user that this may not be supported:
    (when (< 4 (abs offset))
      (ical:warn
       (format
        "`diary-float' with large N=%d may not be supported on other systems" n)))

    (list (ical:make-property ical:dtstart dtstart
            (ical:valuetypeparam 'ical:date))
          (ical:make-property ical:rrule rrule))))