6

Is there a way to have any TODO (whether entered by org-capture or manually) to get a CREATED timestamp added like SCHEDULED and DEADLINE ?

i.e. today I have entries like:

* TODO call mom  
  SCHEDULED: <2017-09-26 Tue>
* TODO walk the dog
* TODO buy milk

and over time the list grows and I often find myself wanting to clear out old entries or review if still relevant - but without any info on when entries was added that is hard. I would love if I could tell org-mode to add CREATED: so the above would be:

* TODO call mom  
  SCHEDULED: <2017-09-26 Tue> CREATED: [2015-09-26]
* TODO walk the dog
  CREATED: [2017-09-21]
* TODO buy milk
  CREATED: [2017-01-26]

Any ideas/suggestions?

I'm thinking an on-save action or similar would be best but not sure how to about it.

3 Answers 3

4

I typically insert TODO headings using M-S-return (bound to org-insert-todo-heading). You might find this suitable for your purposes:

(defun my/log-todo-creation-date (&rest ignore)
  "Log TODO creation time in the property drawer under the key 'CREATED'."
  (when (and (org-get-todo-state)
             (not (org-entry-get nil "CREATED")))
    (org-entry-put nil "CREATED" (format-time-string (cdr org-time-stamp-formats)))))

(advice-add 'org-insert-todo-heading :after #'my/log-todo-creation-date)
(advice-add 'org-insert-todo-heading-respect-content :after #'my/log-todo-creation-date)
(advice-add 'org-insert-todo-subheading :after #'my/log-todo-creation-date)

If you want to add creation dates to TODO's that you typed manually you could catch those proactively by (temporarily) advising org-get-todo-state itself (so that org internals effectively create the property entries for you). Alternately, the org-after-todo-state-change-hook hook may be sufficient. This would log creation dates for existing TODO's the next time you cycle/set the todo state (such as with org-shiftright):

(add-hook 'org-after-todo-state-change-hook #'my/log-todo-creation-date)

These create a property entry like the following:

** TODO Test
:PROPERTIES:
:CREATED:  <2017-09-26 Tue 23:33>
:END:
3
  • yeah I got that somewhat working - I really would just prefer the CREATED part would be listed same way as CLOSED, SCHEDULED, DEADLINE is so it doesn't add 3 more lines. Sep 27, 2017 at 9:53
  • I use (org-entry-put nil "CREATED" (format-time-string "[%Y-%m-%d %a]")) to insert an inactive time stamp in the same format as Org, otherwise these items show up in agenda view.
    – emonigma
    Oct 29, 2018 at 11:20
  • @miguelmorin This gives me an error in doom emacs: Error in private config: config.el, (void-function org-entry-put).
    – azureai
    Mar 16, 2021 at 22:21
2

I use the org-expiry package to do this. Here's what I've got in my init file:

  (req-package org-expiry
    :loader :el-get-local
    :require org-capture
    :init (progn
            (org-expiry-insinuate)
            (setq
             org-expiry-created-property-name "CREATED" ; Name of property when an item is created
             org-expiry-inactive-timestamps   t         ; Don't have everything in the agenda view
             )))

Notice that I'm using req-package. You could create a similar effect with just this:

(require 'org-expiry)
(org-expiry-insinuate)
(setq org-expiry-created-property-name "CREATED")
5
  • I added those but I see nothing being added when I type in "* TODO test" how is it triggered ? Sep 26, 2017 at 7:54
  • it seem to only add something when I change the type (i.e. from TODO to DONE) and then it add it as a properties section meaning it gets very verbose - but I guess I can live with that for now. Just need to grok how to get it actually added to all things I create. Sep 26, 2017 at 9:38
  • Out of curiosity, why do you have :require org-capture in there?
    – kshenoy
    Jun 28, 2018 at 16:57
  • @kshenoy, you're right, it's unnecessary. Currently, I'm using this code: (req-package org-expiry :el-get t :config (org-expiry-insinuate) (customize-set-variable 'org-expiry-inactive-timestamps t)) ; don't have everything in the agenda view Jun 29, 2018 at 20:36
  • @jeffkowalski Thanks for the explanation. I wasn't sure if I was unaware of some dependency between the two.
    – kshenoy
    Jul 3, 2018 at 21:13
0

Cross-posted from a similar question on StackOverflow.

Here is a lightweight solution that does not require an external package, adapted from the answer by @ebpa but without the extra lines for the properties drawer. It works with org-capture and with M-S-RET. Put this in your Emacs initialization file (e.g. ~/.emacs):

(defun insert-created-date(&rest ignore)
  (insert (format-time-string
       (concat "\nCREATED: "
           (cdr org-time-stamp-formats))
       ))
  (org-back-to-heading) ; in org-capture, this folds the entry; when inserting a heading, this moves point back to the heading line
  (move-end-of-line()) ; when inserting a heading, this moves point to the end of the line
  )


                    ; add to the org-capture hook
(add-hook 'org-capture-before-finalize-hook 
         #'insert-created-date
)

                    ; hook it to adding headings with M-S-RET
                    ; do not add this to org-insert-heading-hook, otherwise this also works in non-TODO items
                    ; and Org-mode has no org-insert-todo-heading-hook
(advice-add 'org-insert-todo-heading :after #'insert-created-date)

I did not add this function to state changes (e.g., from plain heading to TODO) because then @ebpa's answer is cleaner.

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.