Skip to content

Instantly share code, notes, and snippets.

@vherrmann
Created January 3, 2026 22:23
Show Gist options
  • Select an option

  • Save vherrmann/4637284194b214f2a83964b6306883ba to your computer and use it in GitHub Desktop.

Select an option

Save vherrmann/4637284194b214f2a83964b6306883ba to your computer and use it in GitHub Desktop.
Rescheduling repeating todos in org mode without breaking regular schedule
(defun iko/org-is-static-repeater ()
;; Is this a repeater that uses the current date for rescheduling (.+) or
;; a „static“ repeater that uses the last scheduled date (++)?
(when-let ((repeater (org-get-repeat)))
(not (string-prefix-p ".+" repeater))))
(defun iko/org-entry-put-nil (EPOM PROPERTY VALUE)
(and VALUE (org-entry-put EPOM PROPERTY VALUE)))
(defun iko/org-ts-inactive→active (text)
(when (and text (string-match "\\[\\(.+\\)\\]" text))
(concat "<" (match-string 1 text) ">")))
(defun iko/org-ts-active→inactive (text)
(when (and text (string-match "<\\(.+\\)>" text))
(concat "[" (match-string 1 text) "]")))
(advice-add
'org-todo
:around
(defun iko/org-todo-schedule-fix (oldfun &rest r)
(if (iko/org-is-static-repeater)
(let ((prev-scheduled-stamp (org-entry-get nil "SCHEDULED"))
(prev-deadline-stamp (org-entry-get nil "DEADLINE"))
;; The original timestamps are stored as inactive timestamps
(orig-scheduled-stamp (iko/org-ts-inactive→active (org-entry-get nil "ORIG_SCHEDULED")))
(orig-deadline-stamp (iko/org-ts-inactive→active (org-entry-get nil "ORIG_DEADLINE"))))
(iko/org-entry-put-nil nil "SCHEDULED" (or orig-scheduled-stamp prev-scheduled-stamp))
(iko/org-entry-put-nil nil "DEADLINE" (or orig-deadline-stamp prev-deadline-stamp))
(unwind-protect
(apply oldfun r)
(let ((next-scheduled-stamp (org-entry-get nil "SCHEDULED"))
(next-deadline-stamp (org-entry-get nil "DEADLINE")))
(if (equal orig-scheduled-stamp next-scheduled-stamp)
(iko/org-entry-put-nil nil "SCHEDULED" prev-scheduled-stamp)
(iko/org-entry-put-nil nil "ORIG_SCHEDULED"
(iko/org-ts-active→inactive next-scheduled-stamp)))
(if (equal orig-deadline-stamp next-deadline-stamp)
(iko/org-entry-put-nil nil "DEADLINE" prev-deadline-stamp)
(iko/org-entry-put-nil nil "ORIG_DEADLINE"
(iko/org-ts-active→inactive next-deadline-stamp))))))
(apply oldfun r))))
(advice-add
'org-schedule
:before
(defun iko/org-schedule-schedule-fix (&rest r)
(when (and (iko/org-is-static-repeater)
(not (org-entry-get nil "ORIG_SCHEDULED")))
(iko/org-entry-put-nil nil "ORIG_SCHEDULED" (org-entry-get nil "SCHEDULED")))))
(advice-add
'org-deadline
:before
(defun iko/org-deadline-schedule-fix (&rest r)
(when (and (iko/org-is-static-repeater)
(not (org-entry-get nil "ORIG_DEADLINE")))
(iko/org-entry-put-nil nil "ORIG_DEADLINE" (org-entry-get nil "DEADLINE")))))
@vherrmann
Copy link
Author

Let's say it is Saturday and you have a repeating task for your exercise sheet like

** TODO [#A] Übungsblatt: Modern Discrete Probability
DEADLINE: <2026-01-08 Thu +1w> SCHEDULED: <2026-01-03 Sat +1w>

that is usually scheduled for Saturday. Let us further assume you do not want do today for whatever reason.

In that case it would be very convenient to be able to reschedule the task without changing the general behavior of it being scheduled for Saturdays.

The following config achieves this by adding properties ORIG_SCHEDULED and ORIG_DEADLINE when changing the todo state such that the original timestamps can be recovered after changing them with org-schedule or org-schedule.

@vherrmann
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment