79286064

Date: 2024-12-16 21:07:33
Score: 0.5
Natty:
Report link

I've written a package to handle this: repeat-todo. It supports weekdays, weekends, and custom combinations of days:

* TODO Water the flowers
SCHEDULED: <... ++1d>
:PROPERTIES:
:REPEAT: weekdays
:END
* TODO Go for a walk
SCHEDULED: <... ++1d>
:PROPERTIES:
:REPEAT: weekend
:END:
* TODO Get the mail
SCHEDULED: <... ++1d>
:PROPERTIES:
:REPEAT: M W F
:END:

The key methods are:

(defun repeat-todo--parse-property (prop-value)
  "Return repeating days for PROP-VALUE as list of day numbers (sun=0, mon=1, etc)."
  (let ((case-fold-search nil))
    (cond
     ((string-match "weekday\\(s\\)?" prop-value)
      '(1 2 3 4 5))
     ((string-match "weekend\\(s\\)?" prop-value)
      '(6 0))
     (t
      (let ((repeat-days '())
            (prop-values (string-split " " prop-value 'omit-nulls)))
        (dolist (value prop-values)
          ;; su, sun, sunday
          ((when (string-match "^su\\(n\\(day\\)?\\)?")
             (push 0 repeat-days)))
          ;; m, mon, monday
          ((string-match "^m\\(on\\(day\\)?\\)?") (push 1 repeat-days))
          ;; t, tue, tues, tuesday
          ((when (string-match "^t\\(ue\\(s\\(day\\)?\\)?")
             (push 2 repeat-days)))
          ;; w, wed, wednesday
          ((when (string-match "^w\\(ed\\(nesday\\)?\\)?")
             (push 3 repeat-days)))
          ;; r, thu, thur, thurs, thursday
          ((when (or (string= "r")
                     (string= "R")
                     (string-match "thu\\(r\\(s\\(day\\)?\\)?\\)?"))
             (push 4 repeat-days)))
          ;; f, fri, friday
          ((when (string-match "^f\\(ri\\(day\\)?\\)?")
             (push 5 repeat-days)))
          ;; sa, sat, saturday
          ((when (string-match "^sa\\(t\\(urday\\)?\\)?")
             (push 6 repeat-days))))
        (reverse repeat-days))))))

(defun repeat-todo--next-scheduled-time (current-scheduled-time weekdays)
  "Return the next valid, by WEEKDAYS, time after CURRENT-SCHEDULED-TIME.

WEEKDAYS: See `repeat-todo--weekdays'."
  (when weekdays
    (let ((new-scheduled-time
           (time-add current-scheduled-time (days-to-time 1))))
      (while (not
              (member
               (string-to-number
                (format-time-string "%u" new-scheduled-time))
               weekdays))
        (setq new-scheduled-time
              (time-add new-scheduled-time (days-to-time 1))))
      new-scheduled-time)))

(defun repeat-todo--reschedule (point-or-marker)
  "Reschedule heading at POINT-OR-MARKER to the next appropriate weekday."
  (when (and repeat-todo-mode
             (org-entry-is-done-p)
             (repeat-todo--p point-or-marker))
    (org-schedule
     nil
     (string-replace
      " 00:00" ""
      (format-time-string
       "%F %H:%M"
       ;; Schedule to the day before the next schedule time because
       ;; it'll get moved forward one day past when we schedule it
       (time-subtract
        (repeat-todo--next-scheduled-time
         (org-get-scheduled-time point-or-marker)
         (repeat-todo--parse-property
          (or (org-entry-get point-or-marker repeat-todo--property) "")))
        (days-to-time 1)))))))
Reasons:
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: cashpw