0

While I understand the DEADLINE and SCHEDULED timestamp keywords are all that org mode support natively, I am wondering if it is possible to have something like this:

*** TODO [#1] Assignment
DEADLINE: <2021-11-07 Sun 10:00> (Time I want to finish the assignment)
Actual Deadline: <2021-11-08 Mon 10:00> (Time the assignment is actually due)

The idea is that I can have the DEADLINE keyword act normally, but have the second item show up in a special way (not just as a timestamp) on the Org Agenda. e.g. When the assignment shows up at <2021-11-08 Mon 10:00> on the agenda, a prefix denotes its purpose:

...
Monday   8 November 2021 W45
Math 4130:      10:00...... Actual Deadline: DONE [#1] Assignment
...

The Actual Deadline doesn't need any special DEADLINE behavior. For my purpose this functionality would be more helpful than the deadline "warning days" functionality Org Mode provides. I have functionality for other org-agenda-prefix information, but unsure how I might achieve this.

How might I do this?

EDIT: Just to be perfectly clear, Actual Deadline is just an example. Ideally any timestamp label could be displayed in the org-agenda-prefix.

1
  • Please don't change questions after they are answered. Although this is a small change, it does require additional code, so it would be best if you could ask a separate question, referring back to this one for context.
    – NickD
    Commented Nov 10, 2021 at 22:02

1 Answer 1

1

Here's a possible implementation. It is based on the idea of adding the actual deadline time stamp as a property to the heading (do C-h i g(org)properties and columns to read about properties in Org mode). So the Org mode file would look like this:

#+CATEGORY: Math 1234
* TODO Assignment
DEADLINE: <2021-11-14 Sun 10:00> (Time I want to finish the assignment)
:PROPERTIES:
:ActualDeadline: <2021-11-15 Mon 10:00>
:END:

Adding it as a property allows easy retrieval of it later using the Propery API (see C-h i g(org)Using the Property API). With point anywhere in a heading, all you have to do is:

(org-entry-get nil "ActualDeadline")

In the heading above, you get the string "<2021-11-15 Mon 10:00>" as the value. If there is no such property, you get nil.

The next step is to define a function that looks for an ActualDeadline property in the current heading and if it finds one, it compares the date from the timestamp with the agenda date: if the dates are equal, then the function returns the string "Actual Deadline ", otherwise it returns an empty string:

(defun actual-deadline ()
  (let ((ts (org-entry-get nil "ActualDeadline"))
        tsdate)
    (setq tsdate (if ts
                     (calendar-gregorian-from-absolute 
                        (time-to-days (org-read-date nil t ts nil)))
                   nil))
    (if (and tsdate (equal org-agenda-current-date tsdate))
        ;; there was an ActualDeadline property and the dates agree
        "Actual Deadline: "
      ""))

The hardest part here is to convert the date from the timestamp into the (month day year) format that the calendar uses. That is necessary because org-agenda-current-date, the variable that remembers the current date while processing each day's items in the agenda, uses that format. Note that if there is no ActualDeadline property or if the dates do not agree, the function returns the empty string.

Now all you have to do is redefine the agenda entry in org-agenda-prefix-format to use the above function. There is a %(expr) format that allows you to evaluate an arbitrary lisp expression: the format is replaced by the (string) value of the expression - see the doc string of the variable with C-h v org-agenda-prefix-format for the details. In particular, we can have it call the function above:

(setf (cdr (assq 'agenda org-agenda-prefix-format))
       " %i %-12:c%?-12t%(actual-deadline)% s")

That's the default value of the format for the agenda, except that I've interpolated the call to the function: %(actual-deadline).

You can add the function to your init file, but you have to be careful when you add the setting of org-agenda-prefix-format: the code above assumes that the variable is already defined, but the variable is only defined after org-agenda is loaded, so the safest thing to do is to use with-eval-after-load:

(with eval-after-load 'org-agenda
  (setf (cdr (assq 'agenda org-agenda-prefix-format))
        " %i %-12:c%?-12t%(actual-deadline)% s")

Here's how it looks:

Sunday     14 November 2021
  Weather:    6:30......  Sunrise 
  Math 1234:  10:00...... Deadline:   TODO Assignment
  Weather:    16:26...... Sunset 
Monday     15 November 2021 W46
  Weather:    6:32......  Sunrise 
  Math 1234:  10:00...... Actual Deadline: TODO Assignment
  Weather:    16:26...... Sunset 

EDIT: Adding a property to an Org mode heading is easy. There is a convenient keybinding for it: C-c C-x p bound to org-set-property. It asks you for the name of the property and it allows for completion, so that is easy to do. It then asks for the value of the property and that's a problem: you would like the value of the ActualDeadline problem to be an active timestamp, but the keybinding C-c . that is normally used for that is only available in an Org mode buffer, not in the minibuffer where org-set-property is asking you to enter it. You can enter it by hand of course, but it might be convenient to make org-time-stamp available through a keybinding in the minibuffer. Fortunately, the C-c . key is generally not defined in the minibuffer, so you can add it for yourself:

(define-key minibuffer-mode-map (kbd "C-c .") #'org-time-stamp)

That gives you the standard Org mode method of adding a time stamp in the minibuffer, so adding an ActualDeadline property with a time stamp value becomes pretty easy:

C-c C-x p Ac TAB RET C-c .  ... RET

i.e. call org-set-property, start giving it the name of the property, TAB for completion, RET to accept (assuming there is no other property starting with Ac) then C-c . to start the dialog for a time stamp, then RET when you are done. That's easier done than said.

2
  • This works for the example I gave, thank you. Question: is there an easy way to make this more general? Essentially the goal is for the prefix to be arbitrary. In this case the goal is for the property to be any non-reserved value, as long as it has a string association. ActualDeadline property --> "Actual deadline:" prefix; PossibleDeadline property --> "Possible deadline:" prefix and so on. Then the org agenda prefix could be more adaptable, like " %i %-12:c%?-12t%(timestamp-prefix)% s") instead of a "hardcoded" value.
    – Nathan
    Commented Nov 10, 2021 at 21:28
  • Yes, there is but this is already a long answer: please ask it in a separate question.
    – NickD
    Commented Nov 10, 2021 at 22:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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