Hi Org Mode community, I am writing to share a solution to a common workflow issue I encounter and to propose making this a built-in feature.
*The Problem:* When using Org Mode, I often face this scenario: I completed a recurring TODO task (e.g., with a `+1w` repeater) a few days ago, but I forgot to mark it as DONE at that time. If I mark it as DONE today using `C-c C-t`, Org Mode naturally uses the **current time** for the completion timestamp in the Logbook. Consequently, the next occurrence’s DEADLINE/SCHEDULED date is calculated based on today, pushing the schedule forward incorrectly, rather than based on the actual completion date. Previously, I had to manually edit the timestamp in the Logbook and calculate the next deadline by hand, which is quite tedious. *My Current Solution:* I wrote a custom function that uses `advice-add` around `org-todo`. The core idea is to use `cl-letf` to temporarily shadow internal time-fetching functions (like `org-current-effective-time`, `org-today`, etc.) so that Org believes “now” is actually the time I specify. The workflow is: 1. Run `M-x my/set-next-org-todo-time`. 2. Select the actual completion time using `org-read-date` (e.g., typing `-3` for three days ago). 3. Immediately press `C-c C-t` to switch the TODO state. The Logbook entry and the repeater calculation are then correctly based on the retroactive date. The advice is designed to be one-off and removes itself after execution. Here is the proof-of-concept code: ┌──── │ (defun my/set-next-org-todo-time (time-string) │ "Set the timestamp for the next `org-todo' invocation. │ Normally, `org-todo' uses the current time. This function uses an │ advice to temporarily shadow time-related functions like │ `org-current-effective-time' so that the next `org-todo' call │ operates relative to the provided timestamp." │ (interactive │ (list (org-read-date nil nil nil "Time for next org-todo: "))) │ (let* ((time (cond │ ((stringp time-string) │ (org-time-string-to-time time-string)) │ ((and (consp time-string) │ (numberp (car time-string))) │ time-string) │ (t │ (current-time)))) │ (advice │ (lambda (orig-fn &rest args) │ (cl-letf (((symbol-function 'org-current-effective-time) │ (lambda (&optional _ignored) time)) │ ((symbol-function 'org-today) │ (lambda () │ (time-to-days time))) │ ((symbol-function 'org-timestamp-to-now) │ (lambda (timestamp-string &optional seconds) │ (let ((fdiff (if seconds #'float-time #'time-to-days))) │ (- (funcall fdiff (org-time-string-to-time timestamp-string)) │ (funcall fdiff time)))))) │ (unwind-protect │ (apply orig-fn args) │ (advice-remove 'org-todo 'override-todo-timestamp-once)))))) │ (advice-add 'org-todo :around advice │ '((name . override-todo-timestamp-once))))) └──── *Discussion:* I believe this “retroactive completion” is a fairly common use case. While my advice-based approach works, it feels like a hack. I am interested to know: 1. Do others find this feature useful? 2. Could we consider adding an official mechanism to the core `org-todo` logic to support this? Perhaps a prefix argument or a configuration option that prompts for a time before marking a task as done? I would welcome any feedback or suggestions on how to implement this more cleanly within Org’s codebase. Best regards, Milan Glacier
