Function: icalendar-recur-refine-byday

icalendar-recur-refine-byday is a byte-compiled function defined in icalendar-recur.el.gz.

Signature

(icalendar-recur-refine-byday INTERVAL WEEKDAYS &optional IN-MONTH VTIMEZONE)

Documentation

Refine INTERVAL to days matching the given WEEKDAYS.

WEEKDAYS should be a list of values from a recurrence rule's BYDAY=... clause; see icalendar-recur for the possible values.

If WEEKDAYS contains pairs (DOW . OFFSET), then IN-MONTH indicates whether OFFSET is relative to the month of the start of the interval. If it is nil, OFFSET will be relative to the year, rather than the month.

Source Code

;; Defined in /usr/src/emacs/lisp/calendar/icalendar-recur.el.gz
(defun icr:refine-byday (interval weekdays &optional in-month vtimezone)
  "Refine INTERVAL to days matching the given WEEKDAYS.

WEEKDAYS should be a list of values from a recurrence rule's
BYDAY=... clause; see `icalendar-recur' for the possible values.

If WEEKDAYS contains pairs (DOW . OFFSET), then IN-MONTH indicates
whether OFFSET is relative to the month of the start of the interval.  If
it is nil, OFFSET will be relative to the year, rather than the month."
  (let* ((sorted-weekdays (sort (seq-filter #'natnump weekdays)))
         (with-offsets (sort (seq-filter #'consp weekdays)
                             :key #'car))
         (interval-start (icr:interval-low interval))
         (curr-abs (calendar-absolute-from-gregorian
                    (ical:date-time-to-date interval-start)))
         (interval-end (icr:interval-high interval))
         (end-abs (calendar-absolute-from-gregorian
                   (ical:date-time-to-date interval-end)))
         (subintervals nil))

    ;; For days where an offset was given, the subinterval is a single
    ;; weekday relative to the month or year of interval-start:
    (dolist (wo with-offsets)
      (let* ((dow (car wo))
             (offset (cdr wo))
             (low-date
              (ical:nth-weekday-in offset dow
                                   (ical:date/time-year interval-start)
                                   (when in-month
                                     (ical:date/time-month interval-start))))
             (low (ical:date-to-date-time low-date :tz vtimezone))
             (high (ical:date/time-add low :day 1 vtimezone)))
        (when (ical:date/time< low interval-start)
          (setq low interval-start))
        (when (ical:date/time< interval-end high)
          (setq high interval-end))
        (when vtimezone
          (icr:tz-set-zone low vtimezone)
          (icr:tz-set-zone high vtimezone))
        (when (and (ical:date/time<= interval-start low)
                   (ical:date/time<= high interval-end)
                   (ical:date/time< low high))
          (push (icr:make-interval low high) subintervals))))

    ;; When no offset was given, for each day in the interval...
    (while (and curr-abs sorted-weekdays)
      ;; ...the subinterval is one day long on matching weekdays.
      (when (memq (mod curr-abs 7) ; = weekday of absolute date;
                  sorted-weekdays) ;   see `calendar-day-of-week'
          (let* ((gdate (calendar-gregorian-from-absolute curr-abs))
                 (low (ical:date-to-date-time gdate))
                 (high (ical:date/time-add low :day 1 vtimezone)))
            (when (ical:date/time< low interval-start)
              (setq low interval-start))
            (when (ical:date/time< interval-end high)
              (setq high interval-end))
            (when vtimezone
              (icr:tz-set-zone low vtimezone)
              (icr:tz-set-zone high vtimezone))
            (when (and (ical:date/time<= interval-start low)
                       (ical:date/time<= high interval-end)
                       (ical:date/time< low high))
              (push (icr:make-interval low high) subintervals))))
      (setq curr-abs (1+ curr-abs))
      (when (<= end-abs curr-abs)
        ;; we're done:
        (setq curr-abs nil)))

    ;; Finally, sort and return all subintervals:
    (sort subintervals
          :key #'icr:interval-low
          :lessp #'ical:date-time<
          :in-place t)))