> > ...
> > +         (add-function :around (symbol-function 'current-time)
 #'org-at-current-time)
>
> While the function like you propose is long desired, reusing
> implementation from tests is not the greatest idea.
> We should not use advices in the main Org codebase.
>
> I suggest building upon existing patch that uses global variable
> instead. See https://list.orgmode.org/orgmode/[email protected]/

I reverted my changes and followed your advice. Unfortunately, I was not
able to create ert-tests for this approach. But it does work when I test it
manually.

Le sam. 4 juil. 2026 à 06:28, Ihor Radchenko <[email protected]> a écrit :

> Earl Chase <[email protected]> writes:
>
> > I reused `org-test-at-time'. Specifically, `org-test-at-time' is now an
> > alias of `org-at-time'. This "new" macro is what `org-todo-at' uses in
> > order to set the current time to the time that you provide it.
> > ...
> > +         (add-function :around (symbol-function 'current-time)
> #'org-at-current-time)
>
> While the function like you propose is long desired, reusing
> implementation from tests is not the greatest idea.
> We should not use advices in the main Org codebase.
>
> I suggest building upon existing patch that uses global variable
> instead. See https://list.orgmode.org/orgmode/[email protected]/
>
> --
> Ihor Radchenko // yantar92,
> Org mode maintainer,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>
>
From 3aa8e9bd1b9d9a0aeed4b80f0b070d4af35c8fa3 Mon Sep 17 00:00:00 2001
From: ApollonDeParnasse <[email protected]>
Date: Thu, 2 Jul 2026 16:43:08 -0500
Subject: [PATCH] lisp/org.el: New function for calling `org-todo' at a given
 time.

* lisp/org.el (org-overriding-effective-time): Place for
`org-todo-at' to store user-entered time.
(org-current-effective-time): Return the value of
`org-overriding-effective-time' when it is non-nil.
(org-todo-at): Call `org-todo' at a given
time.

* lisp/org-keys.el (org-mode-map): New keybinding
for `org-todo-at'.
---
 doc/org-manual.org | 22 ++++++++++++++++++++++
 etc/ORG-NEWS       |  5 +++++
 lisp/org-keys.el   |  2 ++
 lisp/org.el        | 40 +++++++++++++++++++++++++++++++++++++++-
 4 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index 9201a341a..70e274611 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -3977,6 +3977,14 @@ The most important commands to work with TODO entries are:
   buffer with the {{{kbd(t)}}} command key (see [[*Commands in the
   Agenda Buffer]]).
 
+- {{{kbd(C-c M-t)}}} (~org-todo-at~) ::
+
+  #+kindex: C-c M-t
+  #+cindex: cycling, of TODO states
+  Rotate the TODO state of the current item,
+  just like ~org-todo~.  Will prompt you for
+  a time for each state change.
+
 - {{{kbd(S-RIGHT)}}} {{{kbd(S-LEFT)}}} ::
 
   #+kindex: S-RIGHT
@@ -4369,6 +4377,13 @@ command ~org-todo~ with a prefix argument.
   The note is inserted as a list item below the headline, but can also
   be placed into a drawer, see [[*Tracking TODO state changes]].
 
+- {{{kbd(C-u C-c M-t)}}} (~org-todo-at~) ::
+
+  #+kindex: C-u C-M C-t
+  Prompt for a time for the TODO state change and a note.
+  The note is inserted as a list item below the headline,
+  but can also be placed into a drawer, just like ~org-todo~.
+
 If you want to be more systematic, Org mode can automatically record a
 timestamp and optionally a note when you mark a TODO item as DONE, or
 even each time you change the state of a TODO item.  This system is
@@ -7002,6 +7017,13 @@ about what to do with it.
   Changing the TODO state of an item to DONE automatically stops the
   clock if it is running in this same item.
 
+- {{{kbd(C-c M-t)}}} (~org-todo-at~) ::
+
+  #+kindex: C-c M-t
+  #+findex: org-todo-at
+  Changing the TODO state of an item to DONE automatically stops the
+  clock at the time that you provide if it is running in this same item.
+
 - {{{kbd(C-c C-x C-q)}}} (~org-clock-cancel~) ::
 
   #+kindex: C-c C-x C-q
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 261d5d3be..edc336988 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -227,6 +227,11 @@ needs. The recommended alternative value is ~itemize~.
 
 Given the completed and total number of tasks, format the percent
 cookie =[N%]=.
+*** New function ~org-todo-at~
+
+Like `org-todo', but you will be first be prompted for a time.
+This time will be used as the time of the TODO state change of
+the item that you are editing.
 
 ** Removed or renamed functions and variables
 
diff --git a/lisp/org-keys.el b/lisp/org-keys.el
index 84eb3bf13..6853e9f3d 100644
--- a/lisp/org-keys.el
+++ b/lisp/org-keys.el
@@ -215,6 +215,7 @@
 (declare-function org-timer-start "org-timer" (&optional offset))
 (declare-function org-timer-stop "org-timer" ())
 (declare-function org-todo "org" (&optional arg1))
+(declare-function org-todo-at "org" (time &optional arg))
 (declare-function org-toggle-archive-tag "org-archive" (&optional find-done))
 (declare-function org-toggle-checkbox "org-list" (&optional toggle-presence))
 (declare-function org-toggle-radio-button "org-list" (&optional arg))
@@ -576,6 +577,7 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
 (org-defkey org-mode-map (kbd "C-c C-x q") #'org-toggle-tags-groups)
 (org-defkey org-mode-map (kbd "C-c C-j") #'org-goto)
 (org-defkey org-mode-map (kbd "C-c C-t") #'org-todo)
+(org-defkey org-mode-map (kbd "C-c M-t") #'org-todo-at)
 (org-defkey org-mode-map (kbd "C-c C-q") #'org-set-tags-command)
 (org-defkey org-mode-map (kbd "C-c C-s") #'org-schedule)
 (org-defkey org-mode-map (kbd "C-c C-d") #'org-deadline)
diff --git a/lisp/org.el b/lisp/org.el
index 294a5eb52..3fb998ce1 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -9611,12 +9611,19 @@ nil or a string to be used for the todo mark." )
 
 (defvar org-agenda-headline-snapshot-before-repeat)
 
+(defvar org-overriding-effective-time nil
+  "Place for `org-todo-at' to store user-entered time.
+Used to override the value return by `org-current-effective-time'.")
+
 (defun org-current-effective-time ()
-  "Return current time adjusted for `org-extend-today-until' variable."
+  "Return current time adjusted for `org-extend-today-until' variable.
+If `org-overriding-effective-time' is non-nil, then its value will
+be returned instead."
   (let* ((ct (org-current-time))
 	 (dct (decode-time ct))
 	 (ct1
 	  (cond
+           (org-overriding-effective-time org-overriding-effective-time)
 	   (org-use-last-clock-out-time-as-effective-time
 	    (or (org-clock-get-last-clock-out-time) ct))
 	   ((and org-use-effective-time (< (decoded-time-hour dct) org-extend-today-until))
@@ -9918,6 +9925,37 @@ When called through Elisp, arg is also interpreted in the following way:
 		(run-hook-with-args 'org-trigger-hook change-plist)))
 	    (when commentp (org-toggle-comment))))))))
 
+(defun org-todo-at (&optional arg)
+  "Like `org-todo', but you will be prompted for a time.
+The value you provided will be used as the time value of any
+state change that is triggered by `org-todo'.
+
+With `\\[universal-argument]' prefix ARG, force logging the state change \
+and take a
+logging note.
+With a `\\[universal-argument] \\[universal-argument]' prefix, switch to the \
+next set of TODO \
+keywords (nextset).
+Another way to achieve this is `S-C-<right>'.
+With a `\\[universal-argument] \\[universal-argument] \\[universal-argument]' \
+prefix, circumvent any state blocking.
+With numeric prefix arg, switch to the Nth state.
+
+With a numeric prefix arg of 0, inhibit note taking for the change.
+With a numeric prefix arg of -1, cancel repeater to allow marking as DONE.
+
+When called through Elisp, arg is also interpreted in the following way:
+`none'        -> empty state
+\"\"            -> switch to empty state
+`done'        -> switch to DONE
+`nextset'     -> switch to the next set of keywords
+`previousset' -> switch to the previous set of keywords
+\"WAITING\"     -> switch to the specified keyword, but only if it
+                 really is a member of `org-todo-keywords'."
+  (interactive)
+  (let ((org-overriding-effective-time (org-read-date t t)))
+    (org-todo arg)))
+
 (defun org-block-todo-from-children-or-siblings-or-parent (change-plist)
   "Block turning an entry into a TODO, using the hierarchy.
 This checks whether the current task should be blocked from state
-- 
2.54.0

Reply via email to