Clocking Time with Emacs Org

2Many 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 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
     (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)))
     (add-hook 'org-after-todo-state-change-hook
     (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))
		 (not (string= last-state state)))
    (add-hook 'org-after-todo-state-change-hook

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 ~/ 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 ~/ 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 ~/ 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))))
    (setq params (plist-put params :tstart nil))
    (setq params (plist-put params :end nil))
    (while (<= start end)
        (insert "\n\n"
                (format-time-string (car org-time-stamp-formats)
                                    (seconds-to-time start))
           (format-time-string (car org-time-stamp-formats)
                               (seconds-to-time start)))
          (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>"

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!

On Technorati: , ,

Random Emacs symbol: term-previous-matching-input-string – Function: Return the string matching REGEXP ARG places along the input ring.

  • Awesome! 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 ;)

  • Glad 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. =)

  • I 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

    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 file could gather a lot
    of cruft over time. I know from reading John
    ’s Org-as-Day-Planner that there is an archive, but you
    haven’t addressed this yet AFAICT.


  • bruno

    I 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

    I am looking for the actual time clocking scale which 60 minutes if based on 100.

  • Ning

    I 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.


    • Ning

      Sorry for my ignorance, but the defult behaviour is just what I described!

  • Thanks 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


    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

    Thanks 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.

    • Thanks 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

    Hi, for some reason in my emacs (aquamacs 3.0preview4, emacs, 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 !

    • Thanks! Things have moved a fair bit since 2007, and that’s a great thing. =)