Function: nndiary-last-occurrence

nndiary-last-occurrence is a byte-compiled function defined in nndiary.el.gz.

Signature

(nndiary-last-occurrence SCHED)

Aliases

nndiary-last-occurence (obsolete since 26.1)

Source Code

;; Defined in /usr/src/emacs/lisp/gnus/nndiary.el.gz
(defun nndiary-last-occurrence (sched)
  ;; Returns the last occurrence of schedule SCHED as an Emacs time struct, or
  ;; nil for permanent schedule or errors.
  (let ((minute (nndiary-max (nth 0 sched)))
	(hour (nndiary-max (nth 1 sched)))
	(year (nndiary-max (nth 4 sched)))
	(time-zone (or (car (nth 6 sched))
		       (current-time-zone))))
    (when year
      (or minute (setq minute 59))
      (or hour (setq hour 23))
      ;; I'll just compute all possible values and test them by decreasing
      ;; order until one succeeds. This is probably quite rude, but I got
      ;; bored in finding a good algorithm for doing that ;-)
      ;; ### FIXME: remove identical entries.
      (let ((dom-list (nth 2 sched))
	    (month-list (sort (nndiary-flatten (nth 3 sched) 1 12) #'>))
	    (year-list (sort (nndiary-flatten (nth 4 sched) 1971) #'>))
	    (dow-list (nth 5 sched)))
	;; Special case: an asterisk in one of the days specifications means
	;; that only the other should be taken into account. If both are
	;; unspecified, you would get all possible days in both.
	(cond ((null dow-list)
	       ;; this gets all days if dom-list is nil
	       (setq dom-list (nndiary-flatten dom-list 1 31)))
	      ((null dom-list)
	       ;; this also gets all days if dow-list is nil
	       (setq dow-list (nndiary-flatten dow-list 0 6)))
	      (t
	       (setq dom-list (nndiary-flatten dom-list 1 31))
	       (setq dow-list (nndiary-flatten dow-list 0 6))))
	(or
	 (catch 'found
	   (while (setq year (pop year-list))
	     (let ((months month-list)
		   month)
	       (while (setq month (pop months))
		 ;; Now we must merge the Dows with the Doms. To do that, we
		 ;; have to know which day is the 1st one for this month.
		 ;; Maybe there's simpler, but decode-time(encode-time) will
		 ;; give us the answer.
		 (let ((first (decoded-time-weekday
			       (decode-time
				(encode-time 0 0 0 1 month year
					     time-zone))))
		       (max (cond ((= month 2)
				   (if (date-leap-year-p year) 29 28))
				  ((<= month 7)
				   (if (zerop (% month 2)) 30 31))
				  (t
				   (if (zerop (% month 2)) 31 30))))
		       (doms dom-list)
		       (dows dow-list)
		       day days)
		   ;; first, review the doms to see if they are valid.
		   (while (setq day (pop doms))
		     (and (<= day max)
			  (push day days)))
		   ;; second add all possible dows
		   (while (setq day (pop dows))
		     ;; days start at 1.
		     (setq day (1+ (- day first)))
		     (and (< day 0) (setq day (+ 7 day)))
		     (while (<= day max)
		       (push day days)
		       (setq day (+ 7 day))))
		   ;; Finally, if we have some days, they are valid
		   (when days
		     (throw 'found
			    (encode-time 0 minute hour
					 (apply #'max days)
                                         month year time-zone)))
		   )))))
	 ;; There's an upper limit, but we didn't find any last occurrence.
	 ;; This means that the schedule is undecidable. This can happen if
	 ;; you happen to say something like "each Feb 31 until 2038".
	 (progn
	   (nnheader-report 'nndiary "Undecidable schedule")
	   nil))
	))))