Function: icalendar-rrule-validator
icalendar-rrule-validator is a byte-compiled function defined in
icalendar-parser.el.gz.
Signature
(icalendar-rrule-validator NODE)
Documentation
Validate that NODE has the properties required by a recurrence rule.
NODE should represent an iCalendar component. When NODE has an
icalendar-rrule property, this function validates that its
icalendar-dtstart, icalendar-rdate, and icalendar-exdate
properties satisfy the requirements imposed by this rule.
This function is called by the additional validator functions for
component nodes (e.g. icalendar-vevent-validator); it is not normally
necessary to call it directly.
Source Code
;; Defined in /usr/src/emacs/lisp/calendar/icalendar-parser.el.gz
(defun ical:rrule-validator (node)
"Validate that NODE has the properties required by a recurrence rule.
NODE should represent an iCalendar component. When NODE has an
`icalendar-rrule' property, this function validates that its
`icalendar-dtstart', `icalendar-rdate', and `icalendar-exdate'
properties satisfy the requirements imposed by this rule.
This function is called by the additional validator functions for
component nodes (e.g. `icalendar-vevent-validator'); it is not normally
necessary to call it directly."
(let* ((rrule (ical:ast-node-first-child-of 'ical:rrule node))
(recval (when rrule (ical:ast-node-value rrule)))
(dtstart (ical:ast-node-first-child-of 'ical:dtstart node))
(start (when dtstart (ical:ast-node-value dtstart)))
(rdates (ical:ast-node-children-of 'ical:rdate node))
(included (when rdates
(mapcar #'ical:ast-node-value
(apply #'append
(mapcar #'ical:ast-node-value rdates))))))
(when rrule
(unless dtstart
(ical:signal-validation-error
"An `icalendar-rrule' requires an `icalendar-dtstart' property"
:node node))
(when included
;; ""RDATE" in this usage [i.e., in STANDARD and DAYLIGHT
;; subcomponents] MUST be specified as a date with local time
;; value, relative to the UTC offset specified in the
;; "TZOFFSETFROM" property."
(when (and (memq (ical:ast-node-type node) '(ical:standard ical:daylight)))
(unless (ical:list-of-p included 'ical:date-time)
(ical:signal-validation-error
(format
(concat "`icalendar-rdate' values must be `icalendar-date-time' "
"values in %s components")
(ical:ast-node-type node))
:node node))
(when (seq-some #'decoded-time-zone included)
(ical:signal-validation-error
(format
(concat "`icalendar-rdate' values must be in local (\"floating\")"
"time in %s components")
(ical:ast-node-type node))
:node node))))
(let* ((freq (car (alist-get 'FREQ recval)))
(until (car (alist-get 'UNTIL recval))))
(when (eq 'ical:date (ical:ast-node-type start))
(when (or (memq freq '(HOURLY MINUTELY SECONDLY))
(assq 'BYSECOND recval)
(assq 'BYMINUTE recval)
(assq 'BYHOUR recval))
(ical:signal-validation-error
(concat "`icalendar-rrule' must not contain time-based "
"rules when `icalendar-dtstart' is a plain date")
:node node)))
(when until
(unless (eq (ical:ast-node-type start)
(ical:ast-node-type until))
(ical:signal-validation-error
(concat "`icalendar-rrule' UNTIL clause must agree with "
"type of `icalendar-dtstart' property")
:node node))
(when (eq 'ical:date-time (ical:ast-node-type until))
(let ((until-zone
(decoded-time-zone (ical:ast-node-value until)))
(start-zone
(decoded-time-zone (ical:ast-node-value start))))
;; "If the "DTSTART" property is specified as a date
;; with local time, then the UNTIL rule part MUST also
;; be specified as a date with local time":
(when (and (null start-zone) (not (null until-zone)))
(ical:signal-validation-error
(concat "`icalendar-rrule' UNTIL clause must be in "
"local time if `icalendar-dtstart' is")
:node node))
;; "If the "DTSTART" property is specified as a date
;; with UTC time or a date with local time and time zone
;; reference, then the UNTIL rule part MUST be specified
;; as a date with UTC time":
(when (and (integerp start-zone)
(not (ical:date-time-is-utc-p until)))
(ical:signal-validation-error
(concat "`icalendar-rrule' UNTIL clause must be in UTC time "
"if `icalendar-dtstart' has a defined time zone")
:node node))))
(when (memq (ical:ast-node-type node) '(ical:standard ical:daylight))
;; "In the case of the "STANDARD" and "DAYLIGHT"
;; sub-components the UNTIL rule part MUST always be
;; specified as a date with UTC time":
(unless (ical:date-time-is-utc-p until)
(ical:signal-validation-error
(concat "`icalendar-rrule' UNTIL clause must be in UTC time in "
"`icalendar-standard' and `icalendar-daylight' components")
:node node))))
;; "DTSTART in this usage [i.e., in STANDARD and DAYLIGHT
;; subcomponents] MUST be specified as a date with a local
;; time value."
(when (memq (ical:ast-node-type node) '(ical:standard ical:daylight))
(unless (eq 'ical:date-time (ical:ast-node-type start))
(ical:signal-validation-error
(concat "`icalendar-dtstart' must be an `icalendar-date-time' in "
"`icalendar-standard' and `icalendar-daylight' components")
:node node))
(when (decoded-time-zone (ical:ast-node-value start))
(ical:signal-validation-error
(concat "`icalendar-dtstart' must be in local (\"floating\") time in "
"`icalendar-standard' and `icalendar-daylight' components")
:node node)))))
;; Success:
node))