Clocking Time with Emacs Org
| emacs, org, wickedcoolemacs2Many professionals bill clients for their time. Even if you don’t, keeping track of the time you actually spend on tasks can help you improve your time estimates and check if you’re spending enough time on the things that are important to you. For example, keeping track of the time you spend on tasks might show you that you spend two and a half hours each day just responding to e-mail. If you can identify problem areas like that, then you can look for more effective ways to perform the tasks that take up a lot of your time.
I love Org’s timeclocking support, and I think you will too. Because it’s integrated with your task list, you don’t have to switch to separate application or reenter data. You can get more detailed time reports, too. All you have to do is remember to clock in before you start a task and clock out when you finish it.
Starting and stopping the clock
You can clock in by moving your cursor to the task headline in either your organizer.org file or the org agenda view, and then pressing C-c C-x C-i (org-agenda-clock-in or org-clock-in, depending on context). This adds the time stamp to the task. If you are already clocked into another task in that organizer file, you’ll be clocked out of it to prevent you from accidentally double-billing.
To clock out of a task, type C-c C-x C-o from the task headline. Marking a task as done will also automatically stop the clock, if that was the task with the active clock.
Here’s some code to make this even easier. The following code clocks in whenever you market task is started, and clocks out when you market a task as WAITING. It also automatically market task is started if you clock in. This takes advantage of the Org configuration previously suggested in the Setup section. Add this to your ~/.emacs and evaluate it:
(eval-after-load 'org '(progn (defun wicked/org-clock-in-if-starting () "Clock in when the task is marked STARTED." (when (and (string= state "STARTED") (not (string= last-state state))) (org-clock-in))) (add-hook 'org-after-todo-state-change-hook 'wicked/org-clock-in-if-starting) (defadvice org-clock-in (after wicked activate) "Set this task's status to 'STARTED'." (org-todo "STARTED")) (defun wicked/org-clock-out-if-waiting () "Clock out when the task is marked WAITING." (when (and (string= state "WAITING") (equal (marker-buffer org-clock-marker) (current-buffer)) (< (point) org-clock-marker) (> (save-excursion (outline-next-heading) (point)) org-clock-marker) (not (string= last-state state))) (org-clock-out))) (add-hook 'org-after-todo-state-change-hook 'wicked/org-clock-out-if-waiting)))
What if you forgot to clock into a task when you started? No problem. Simply clock in and out of it, then edit the starting timestamp for the task in your ~/organizer.org file. To find a starting timestamp, move your cursor to the task headline. If the task has been collapsed to a single line, press TAB to expand it. Look for a line that starts with CLOCK:, or a collapsed segment that starts with :CLOCK:. If you see a collapsed segment, he expanded by moving a cursor to it and pressing tab. Find the clock entry you want to change, and if the timestamp, and press C-c C-y (org-evaluate-time-range) to update the time total.
Reporting time
By project
To see how much time you’ve spent on a project or task, open your ~/organizer.org file and press C-c C-x C-d (org-clock-display). Total times will be added to each headline, summarizing the times for each subtree.
You can also use one of Org’s dynamic blocks. Open your ~/organizer.org file, move your cursor to where you want the report inserted, and type C-c C-x C-r (org-clock-report). By default, the reports will include all the second-level headings for all the days.
What if you want to limit the report to just the time you clocked last week?
Reporting time for a period
To summarize it for a span of days, change the starting line from:
#+BEGIN: clocktable :maxlevel 2 :emphasize nil
to something like:
#+BEGIN: clocktable :maxlevel 2 :emphasize nil :tstart "<2007-12-25 Sun>" :tend "<2007-12-31 Mon>"
where tstart is the starting time/date and tend is the ending time/date. You can add the timestamps either manually or with C-c C-. (org-time-stamp). After you change the block definition, update the clock table by typing C-c C-x C-u (org-dblock-update).
You can also use a definition like:
#+BEGIN: clocktable :maxlevel 2 :emphasize nil :block today
to see today’s entries. Other block keywords are ‘yesterday’, ‘thisweek’, ‘lastweek’, ‘thismonth’, ‘lastmonth’, ‘thisyear’, or ‘lastyear’.
If you need more levels of headings, change the value of maxlevel. For example, to see a detailed clock table with up to 10 levels of headings, use
#+BEGIN: clocktable :maxlevel 10 :emphasize nil :block today
clocktable summarizes the reported time. What if you want the time broken down by day?
Reporting time by days
The following code creates a custom dynamic block that breaks the reported time by date. Add the following code to your ~/.emacs:
(defun org-dblock-write:rangereport (params) "Display day-by-day time reports." (let* ((ts (plist-get params :tstart)) (te (plist-get params :tend)) (start (time-to-seconds (apply 'encode-time (org-parse-time-string ts)))) (end (time-to-seconds (apply 'encode-time (org-parse-time-string te)))) day-numbers) (setq params (plist-put params :tstart nil)) (setq params (plist-put params :end nil)) (while (<= start end) (save-excursion (insert "\n\n" (format-time-string (car org-time-stamp-formats) (seconds-to-time start)) "----------------\n") (org-dblock-write:clocktable (plist-put (plist-put params :tstart (format-time-string (car org-time-stamp-formats) (seconds-to-time start))) :tend (format-time-string (car org-time-stamp-formats) (seconds-to-time end)))) (setq start (+ 86400 start))))))
After you load that code, you’ll be able to use a dynamic block of the form
#+BEGIN: rangereport :maxlevel 2 :tstart "<2007-12-25 Tue>" :tend "<2007-12-30 Sun>" ... #+END:
to see your time reported by date. Fill it in by moving your cursor within the block and typing C-c C-x C-u (org-dblock-update).
Org makes it easy to capture timeclock information by integrating the timeclock into your task list so that you don’t even have to think about it, and it can report this time by project or by date. You can use this information to bill clients, improve your time estimates, or reflect on the way you do things. All you have to do is clock in by marking a task as STARTED, and clock out by marking a task as WAITING or DONE. Don’t get discouraged if the time clock shows you do only a few hours of productive work each day. Use that to help you figure out how to do to things better!
Random Emacs symbol: term-previous-matching-input-string – Function: Return the string matching REGEXP ARG places along the input ring.
18 comments
Mark A. Hershberger
2007-12-31T06:31:17ZAwesome! I think I have a better idea of how to use Org to do what I want and having the summary on the page would definitely be a bonus.
I'll have to look back over your other post about a day in Org and see if I can use it.
Right now I'm enjoying a burst of productivity in Planner, though ;)
Sacha Chua
2008-01-01T19:29:16ZGlad you're getting the hang of Org. Timeclocking is definitely useful, but the built-in time reports aren't convenient when you're reporting daily or weekly totals. If you using the development version, you'd probably be interested in some code that I use to display the daily time report in my agenda view. I'll write about that soon. =)
Mark A. Hershberger
2008-01-02T23:59:56ZI adjusted your rangereport function to take :block arguments. Other
than that, it looks perfect for what I need.
Since you've used Remember and Planner to write your blog, posting
notes to each day, I'm wondering: how do you see that working with
Org?
With Planner's "copy where needed" form, remembered notes get put in
multiple places, so I can see what I was thinking on a given day or
all the notes I've made pertaining to a project.
Is there a parallel to this in Org?
Also, it seems to me that your organizer.org file could gather a lot
of cruft over time. I know from reading John
Wiegley’s Org-as-Day-Planner that there is an archive, but you
haven't addressed this yet AFAICT.
Thanks!
bruno
2008-03-19T18:49:38ZI have multiple task per day and I'm constantly switching from task to task. When using your "reporting time by days" method, the table gets all messed up! =(
No only it accumulates the daily hours as it reports timetrackings for the weekends (when I do not work).
Nevertheless, great work. =)
Red Campbell
2008-04-03T21:30:01ZHello,
I am looking for the actual time clocking scale which 60 minutes if based on 100.
Ning
2008-09-27T17:16:31ZI sometimes switch from one task to another, then switch back later. Is it possible to set up the org file such that if I clock in one task, it will automatically clock out the other? That is, at any given time there is at most one task clocked in?
This way, I don't have to find the previous task and clock it out.
Thanks!
Ning
2008-09-29T20:35:18ZSorry for my ignorance, but the defult behaviour is just what I described!
Helder Ribeiro
2008-11-10T02:10:07ZThanks a lot for this! I was looking everywhere for how to restrict org-clock-report to an interval. It's really badly documented. You saved me! :)
Brice
2011-07-20T16:07:25ZHello,
Do you have any tips to summarizes the reported time py properties ?
Thank you for the code which creates a custom dynamic block that breaks the reported time by date.
I tried it and it gives the time from the start (which steps forward of 1 day in the loop) to the end (which doesn't change).
For a day by day view, I would suggest :
| (while (<= start end)
| (save-excursion
| (setq startendday (+ 86400 start)) <<<<<<<<<<<<<<<<<<<<
| (insert "\n\n"
| .
| .
| .
| :tend
| (format-time-string (car org-time-stamp-formats)
| (seconds-to-time startendday)))) <<<<<<<<<<<<<<<<<<<<
|
Michael Gauland
2013-12-17T22:12:44ZThanks for this code, Sacha--it was /almost/ exactly what I was looking for. I found I needed to change the :tend parameter at the end to be based on the end of the current day (i.e., (+ 86400 start), rather than the end time of the block. I don't know if that's a bug, or I'm just expecting different behaviour than you intended.
sachac
2014-01-02T03:10:49ZThanks for sharing! Haven't poked this part of the code for a few years, so things have probably changed since I wrote about it. =)
dds
2014-01-08T18:19:29ZHi, for some reason in my emacs (aquamacs 3.0preview4, emacs 24.3.50.2, org-mode 7.9.3f) I needed to rename the state and last-state variables to org-state and org-last-state to get the auto-clock in/out trick to work. Thanks for sharing that code, it is most helpful !
trueOG
2016-07-23T21:03:16ZIs it possible to just show a report for the headlines associated to a single tag? I've tried putting :tags exampletagname but the table vanishes.
sachac
2016-07-24T03:10:06ZAh, is this related to http://stackoverflow.com/qu... ? Try it with :tags "exampletagname". =)
trueOG
2016-07-24T07:08:34ZYes, that was me on Stackoverflow. Thank you very much for your answer, it works beautifully.
Jonathan Vieker
2019-12-30T18:11:51ZThanks, Sasha--your blog helps me with my Lisp syntax yet again.
AstroFloyd
2020-04-17T08:01:55ZI clock in and out by pressing
I
orO
on an Agenda ot Todo item in my agenda view. It would be cool if that would set the TODO label to PROGRESS and WAITING, respectively. Would you know how to do it?sachac
2020-04-18T02:29:38ZSure, you can look into adding functions and org-clock-in-hook or org-clock-out-hook if you want to keep using the clocking functions to trigger it. Alternatively, if you want to switch to using the TODO state to trigger clocking, I wonder if this code might be useful too: https://lists.gnu.org/archi...