0

I have repeating items that I would like to do every day but sometimes miss, e.g.:

** TODO That nasty habit
SCHEDULED: <2019-09-28 Sat 06:00 ++1d>
:PROPERTIES:...
:LOGBOOK:...

I can move point to the timestamp after SCHEDULED and do S-RIGHT, which shifts to the next day.

I could not find a relevant function M-x org-*delay, nor M-x org-*schedule.

How can I delay the item, while keeping the time and the repetition cookie, ideally with a key binding?

1

Delaying by one or multiple days

Here is a function inspired by org-shiftright, the function bound to the key S-RIGHT (find it with C-h k S-RIGHT), which on a timestamp calls org-timestamp-up-day, which itself calls org-timestamp-change. It binds the function to C-c d. Place this in your init file, such as ~/.emacs:

(defun org-delay-scheduled (&optional n)
  "Delay the headline at point by the number of days, keeping the time and repetition cookie."
  (interactive)
  ;; Raise error if entry lacks SCHEDULED, or whatever the value of org-scheduled-string
  ;; except for the colon
  (let ((scheduled-string (car (split-string org-scheduled-string ":"))))
    (unless (org-entry-get nil scheduled-string)
      (user-error (concat "This entry lacks a '" scheduled-string "' property")))
    (save-excursion
      (search-forward (concat org-scheduled-string " ")) ; move point to the scheduled entry
      (search-forward " ") ; move point inside the timestamp
      (org-timestamp-change (if n n 1) 'day nil))))

(define-key org-mode-map (kbd "C-c d") 'org-delay-scheduled)

The result is:

** TODO That nasty habit
SCHEDULED: <2019-09-29 Sun 06:00 ++1d>
:PROPERTIES:...
:LOGBOOK:...

You can then call this function from another, e.g. if you want another key binding to delay by two weeks:

(defun org-delay-scheduled-sprint ()
  "Delay the headline at point by the default of a sprint, 2 weeks."
  (interactive)
  (org-delay-scheduled 14))

(define-key org-mode-map (kbd "C-c w") 'org-delay-scheduled-sprint)

Delaying to present day first

The function above will delay by a fixed number of days. If you have an item far in the past the result may still be in the past, unlike the behavior of S-RIGHT on an entry with a repeating cookie, which first moves it to present. Here is a revision that first delays the entry to today, and then increments the fixed number of days. It uses a function that gets a slice of a list (from this thread), and does some juggling to get the number of days from the timestamp to today.

(defun get-n-items (lst num)
  "Get the first num items from the list."
  (if (> num 0)
      (cons (car lst) (get-n-items (cdr lst) (- num 1)))
    '()))

(defun slice (lst start count)
  "Gets count elements from list starting at start."
  (if (> start 1)
      (slice (cdr lst) (- start 1) count)
    (get-n-items lst count)))

(defun org-delay-scheduled (&optional n)
  "Delay the headline at point by the number of days, keeping the time and repetition cookie."
  (interactive)
  ;; Raise error if entry lacks SCHEDULED, or whatever the value of org-scheduled-string
  ;; except for the colon
  (let ((scheduled-string (car (split-string org-scheduled-string ":"))))
    (unless (org-entry-get nil scheduled-string)
      (user-error (concat "This entry lacks a '" scheduled-string "' property")))
    (save-excursion
      (search-forward (concat org-scheduled-string " ")) ; move point to the scheduled entry
      (let* ((timestamp-string (buffer-substring (point) (line-end-position)))
         (timestamp-list (org-parse-time-string timestamp-string))
         (timestamp-start-of-day (append '(0 0 0) (slice timestamp-list 4 3)))
         (timestamp-internal (apply #'encode-time timestamp-start-of-day))
         (day-in-seconds (* 24 (* 60 60)))
         (days-to-present (truncate (/ (float-time (time-subtract (current-time) timestamp-internal)) day-in-seconds)))
         (days-to-delay (+ days-to-present (if n n 1))))
    (search-forward " ") ; move point inside the timestamp
    (org-timestamp-change days-to-delay 'day nil)))))

(defun org-delay-scheduled-sprint ()
  "Delay the headline at point by the default of a sprint, 2 weeks."
  (interactive)
  (org-delay-scheduled 14))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.