29
votes

I read the org-mode manual but couldn't find an easy way to add a CREATED field to newly created TODOs. In combination with org-log-done one could then compute the time it took to close a particular TODO. This is especially useful when using archive files.

Example:

* TODO Do something
  CREATED:  [2012-09-02 Sun 23:02]
* DONE Do something else
  CREATED: [2012-09-02 Sun 20:02]
  CLOSED: [2012-09-02 Sun 22:02]

I would expect the CREATED field to be added to new tasks (tasks which don't have that field) whenever the file is saved.

Any suggestions on how to achieve this? Using something like Git is not a solution for me to track the creations of TODOS.

8
Are you intending to add the tasks using a Capture template? Or manually inserting them? If you're using a Capture template you can include it through the template. Adding it on save would be slightly trickier. - Jonathan Leech-Pepin
I usually manually insert tasks and I don't actually intend to change that, because I want to keep the feeling of just editing a text file. - Renke Grunwald

8 Answers

17
votes

I use org-expiry to implement that functionality, which is in the contrib directory of org.

The base configuration I use is:

;; Allow automatically handing of created/expired meta data.
(require 'org-expiry)
;; Configure it a bit to my liking
(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
)

(defun mrb/insert-created-timestamp()
  "Insert a CREATED property using org-expiry.el for TODO entries"
  (org-expiry-insert-created)
  (org-back-to-heading)
  (org-end-of-line)
  (insert " ")
)

;; Whenever a TODO entry is created, I want a timestamp
;; Advice org-insert-todo-heading to insert a created timestamp using org-expiry
(defadvice org-insert-todo-heading (after mrb/created-timestamp-advice activate)
  "Insert a CREATED property using org-expiry.el for TODO entries"
  (mrb/insert-created-timestamp)
)
;; Make it active
(ad-activate 'org-insert-todo-heading)

If you are using capture it does not automatically work and needs a little glue. I have posted the complete config here: https://gist.github.com/4037694

9
votes

You don't need to modify functions with 'defadvice' to run expiry code on capture. You should use hook:

(add-hook 'org-capture-before-finalize-hook 
          (lambda()
               (save-excursion
                    (org-back-to-heading)
                    (org-expiry-insert-created))))

Same for 'org-insert-todo-heading'. There is a hook:

(add-hook 'org-insert-todo-heading-hook 
          (lambda()
               (save-excursion
                    (org-back-to-heading)
                    (org-expiry-insert-created))))
7
votes

A more lightweight solution would be to add ! flag to the TODO state:

(setq org-todo-keywords '((sequence "TODO(!)" "DONE")))

Then:

* TODO get of your ass
  - State "TODO"    from    [2016-06-03 to. 10:35]

It isn't very pretty though.

Ref: http://orgmode.org/org.html#Tracking-TODO-state-changes

6
votes

Org provides a hook org-after-todo-state-change-hook which you can use here:

org-after-todo-state-change-hook is a variable defined in ‘org.el’.

Documentation:

Hook which is run after the state of a TODO item was changed. The new state (a string with a TODO keyword, or nil) is available in the Lisp variable ‘org-state’.

Use it as follows:

(require 'org-expiry)

(add-hook 'org-after-todo-state-change-hook
          (lambda ()
            (when (string= org-state "TODO")
              (save-excursion
                (org-back-to-heading)
                (org-expiry-insert-created)))))

org-expiry is part of org-contrib, which is included in the org-plus-contrib package on the org ELPA.

5
votes

Here's a buried treasure:

(setq org-treat-insert-todo-heading-as-state-change t)

I found it here, in response to someone saying they wanted an org-insert-todo-heading-hook.

Just tried it out and, true to form, when you org-insert-todo-heading, it counts as a state change, so ex: #+TODO: TODO(t!) | ... will add a log.

4
votes

If you create all your TODOs with org-capture the following capture template does the trick:

(setq org-capture-templates
'(
    ("t" "TODO Task" entry (file+headline "~/inbox.org" "Tasks")
         "* TODO %?\nCREATED: %u\nSRC: %a\n%i\n")
    ))

The result will look something like this:

* Tasks
** TODO Dummy task
CREATED: [2015-05-08 Fri]
SRC: [[file:~/path/to/file/where/you/created/the/task.org::*heading"][heading]]
2
votes

Here is a lightweight solution that does not require an external package. I got it from the answer by @MarcinAntczak, the comments by @Clément, and this similar thread. 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 it would need to be in a properties drawer and I prefer to not have those extra lines. If you prefer to have it in properties, use the function defined in see this thread.

1
votes

You can add a time stamp at creation time with zero config, but it won't be labeled CREATED. Rather than manually typing TODO, use C-c C-t. It will then be logged as "state changed to TODO from """ and time stamped.