]> code.delx.au - gnu-emacs/blobdiff - lisp/calendar/timeclock.el
Merge from emacs-24; up to 2012-12-23T02:41:17Z!rgm@gnu.org
[gnu-emacs] / lisp / calendar / timeclock.el
index 4e6e4120404ac37342e336be12facc247c6978fb..a872e86dcf233165613c26515e2e993fbc731ae8 100644 (file)
@@ -1,19 +1,18 @@
 ;;; timeclock.el --- mode for keeping track of how much you work
 
-;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
 ;; Created: 25 Mar 1999
-;; Version: 2.6
+;; Version: 2.6.1
 ;; Keywords: calendar data
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +20,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;   (define-key ctl-x-map "to" 'timeclock-out)
 ;;   (define-key ctl-x-map "tc" 'timeclock-change)
 ;;   (define-key ctl-x-map "tr" 'timeclock-reread-log)
-;;   (define-key ctl-x-map "tu" 'timeclock-update-modeline)
+;;   (define-key ctl-x-map "tu" 'timeclock-update-mode-line)
 ;;   (define-key ctl-x-map "tw" 'timeclock-when-to-leave-string)
 
 ;; If you want Emacs to display the amount of time "left" to your
-;; workday in the modeline, you can either set the value of
-;; `timeclock-modeline-display' to t using M-x customize, or you
-;; can add this code to your .emacs file:
+;; workday in the mode-line, you can either set the value of
+;; `timeclock-mode-line-display' to t using M-x customize, or you can
+;; add this code to your init file:
 ;;
 ;;   (require 'timeclock)
-;;   (timeclock-modeline-display)
+;;   (timeclock-mode-line-display)
 ;;
-;; To cancel this modeline display at any time, just call
-;; `timeclock-modeline-display' again.
+;; To cancel this mode line display at any time, just call
+;; `timeclock-mode-line-display' again.
 
 ;; You may also want Emacs to ask you before exiting, if you are
 ;; currently working on a project.  This can be done either by setting
 ;; `timeclock-ask-before-exiting' to t using M-x customize (this is
-;; the default), or by adding the following to your .emacs file:
+;; the default), or by adding the following to your init file:
 ;;
 ;;   (add-hook 'kill-emacs-query-functions 'timeclock-query-out)
 
 ;;; User Variables:
 
 (defcustom timeclock-file (convert-standard-filename "~/.timelog")
-  "*The file used to store timeclock data in."
+  "The file used to store timeclock data in."
   :type 'file
   :group 'timeclock)
 
 (defcustom timeclock-workday (* 8 60 60)
-  "*The length of a work period."
+  "The length of a work period in seconds."
   :type 'integer
   :group 'timeclock)
 
 (defcustom timeclock-relative t
-  "*Whether to make reported time relative to `timeclock-workday'.
+  "Whether to make reported time relative to `timeclock-workday'.
 For example, if the length of a normal workday is eight hours, and you
 work four hours on Monday, then the amount of time \"remaining\" on
 Tuesday is twelve hours -- relative to an averaged work period of
 eight hours -- or eight hours, non-relative.  So relative time takes
 into account any discrepancy of time under-worked or over-worked on
-previous days.  This only affects the timeclock modeline display."
+previous days.  This only affects the timeclock mode line display."
   :type 'boolean
   :group 'timeclock)
 
 (defcustom timeclock-get-project-function 'timeclock-ask-for-project
-  "*The function used to determine the name of the current project.
+  "The function used to determine the name of the current project.
 When clocking in, and no project is specified, this function will be
 called to determine what is the current project to be worked on.
 If this variable is nil, no questions will be asked."
@@ -114,7 +111,7 @@ If this variable is nil, no questions will be asked."
   :group 'timeclock)
 
 (defcustom timeclock-get-reason-function 'timeclock-ask-for-reason
-  "*A function used to determine the reason for clocking out.
+  "A function used to determine the reason for clocking out.
 When clocking out, and no reason is specified, this function will be
 called to determine what is the reason.
 If this variable is nil, no questions will be asked."
@@ -122,7 +119,7 @@ If this variable is nil, no questions will be asked."
   :group 'timeclock)
 
 (defcustom timeclock-get-workday-function nil
-  "*A function used to determine the length of today's workday.
+  "A function used to determine the length of today's workday.
 The first time that a user clocks in each day, this function will be
 called to determine what is the length of the current workday.  If
 the return value is nil, or equal to `timeclock-workday', nothing special
@@ -133,7 +130,7 @@ that day has a length that is different from the norm."
   :group 'timeclock)
 
 (defcustom timeclock-ask-before-exiting t
-  "*If non-nil, ask if the user wants to clock out before exiting Emacs.
+  "If non-nil, ask if the user wants to clock out before exiting Emacs.
 This variable only has effect if set with \\[customize]."
   :set (lambda (symbol value)
         (if value
@@ -148,46 +145,46 @@ This variable only has effect if set with \\[customize]."
 
 ;; For byte-compiler.
 (defvar display-time-hook)
-(defvar timeclock-modeline-display)
+(defvar timeclock-mode-line-display)
 
 (defcustom timeclock-use-display-time t
-  "*If non-nil, use `display-time-hook' for doing modeline updates.
+  "If non-nil, use `display-time-hook' for doing mode line updates.
 The advantage of this is that one less timer has to be set running
-amok in Emacs' process space.  The disadvantage is that it requires
+amok in Emacs's process space.  The disadvantage is that it requires
 you to have `display-time' running.  If you don't want to use
-`display-time', but still want the modeline to show how much time is
+`display-time', but still want the mode line to show how much time is
 left, set this variable to nil.  Changing the value of this variable
-while timeclock information is being displayed in the modeline has no
-effect.  You should call the function `timeclock-modeline-display' with
+while timeclock information is being displayed in the mode line has no
+effect.  You should call the function `timeclock-mode-line-display' with
 a positive argument to force an update."
   :set (lambda (symbol value)
         (let ((currently-displaying
-               (and (boundp 'timeclock-modeline-display)
-                    timeclock-modeline-display)))
+               (and (boundp 'timeclock-mode-line-display)
+                    timeclock-mode-line-display)))
           ;; if we're changing to the state that
-          ;; `timeclock-modeline-display' is already using, don't
+          ;; `timeclock-mode-line-display' is already using, don't
           ;; bother toggling it.  This happens on the initial loading
           ;; of timeclock.el.
           (if (and currently-displaying
                    (or (and value
                             (boundp 'display-time-hook)
-                            (memq 'timeclock-update-modeline
+                            (memq 'timeclock-update-mode-line
                                   display-time-hook))
                        (and (not value)
                             timeclock-update-timer)))
               (setq currently-displaying nil))
           (and currently-displaying
-               (set-variable 'timeclock-modeline-display nil))
+               (set-variable 'timeclock-mode-line-display nil))
           (setq timeclock-use-display-time value)
           (and currently-displaying
-               (set-variable 'timeclock-modeline-display t))
+               (set-variable 'timeclock-mode-line-display t))
           timeclock-use-display-time))
   :type 'boolean
   :group 'timeclock
   :require 'time)
 
 (defcustom timeclock-first-in-hook nil
-  "*A hook run for the first \"in\" event each day.
+  "A hook run for the first \"in\" event each day.
 Note that this hook is run before recording any events.  Thus the
 value of `timeclock-hours-today', `timeclock-last-event' and the
 return value of function `timeclock-last-period' are relative previous
@@ -196,34 +193,34 @@ to today."
   :group 'timeclock)
 
 (defcustom timeclock-load-hook nil
-  "*Hook that gets run after timeclock has been loaded."
+  "Hook that gets run after timeclock has been loaded."
   :type 'hook
   :group 'timeclock)
 
 (defcustom timeclock-in-hook nil
-  "*A hook run every time an \"in\" event is recorded."
+  "A hook run every time an \"in\" event is recorded."
   :type 'hook
   :group 'timeclock)
 
 (defcustom timeclock-day-over-hook nil
-  "*A hook that is run when the workday has been completed.
+  "A hook that is run when the workday has been completed.
 This hook is only run if the current time remaining is being displayed
-in the modeline.  See the variable `timeclock-modeline-display'."
+in the mode line.  See the variable `timeclock-mode-line-display'."
   :type 'hook
   :group 'timeclock)
 
 (defcustom timeclock-out-hook nil
-  "*A hook run every time an \"out\" event is recorded."
+  "A hook run every time an \"out\" event is recorded."
   :type 'hook
   :group 'timeclock)
 
 (defcustom timeclock-done-hook nil
-  "*A hook run every time a project is marked as completed."
+  "A hook run every time a project is marked as completed."
   :type 'hook
   :group 'timeclock)
 
 (defcustom timeclock-event-hook nil
-  "*A hook run every time any event is recorded."
+  "A hook run every time any event is recorded."
   :type 'hook
   :group 'timeclock)
 
@@ -254,7 +251,7 @@ worked so far today.  Also, if `timeclock-relative' is nil, this value
 will be the same as `timeclock-discrepancy'.")
 
 (defvar timeclock-use-elapsed nil
-  "Non-nil if the modeline should display time elapsed, not remaining.")
+  "Non-nil if the mode line should display time elapsed, not remaining.")
 
 (defvar timeclock-last-period nil
   "Integer representing the number of seconds in the last period.
@@ -262,7 +259,7 @@ Note that you shouldn't access this value, but instead should use the
 function `timeclock-last-period'.")
 
 (defvar timeclock-mode-string nil
-  "The timeclock string (optionally) displayed in the modeline.
+  "The timeclock string (optionally) displayed in the mode line.
 The time is bracketed by <> if you are clocked in, otherwise by [].")
 
 (defvar timeclock-day-over nil
@@ -270,15 +267,18 @@ The time is bracketed by <> if you are clocked in, otherwise by [].")
 
 ;;; User Functions:
 
+(define-obsolete-function-alias 'timeclock-modeline-display
+  'timeclock-mode-line-display "24.3")
+
 ;;;###autoload
-(defun timeclock-modeline-display (&optional arg)
-  "Toggle display of the amount of time left today in the modeline.
+(defun timeclock-mode-line-display (&optional arg)
+  "Toggle display of the amount of time left today in the mode line.
 If `timeclock-use-display-time' is non-nil (the default), then
-the function `display-time-mode' must be active, and the modeline
+the function `display-time-mode' must be active, and the mode line
 will be updated whenever the time display is updated.  Otherwise,
 the timeclock will use its own sixty second timer to do its
-updating.  With prefix ARG, turn modeline display on if and only
-if ARG is positive.  Returns the new status of timeclock modeline
+updating.  With prefix ARG, turn mode line display on if and only
+if ARG is positive.  Returns the new status of timeclock mode line
 display (non-nil means on)."
   (interactive "P")
   ;; cf display-time-mode.
@@ -286,49 +286,52 @@ display (non-nil means on)."
   (or global-mode-string (setq global-mode-string '("")))
   (let ((on-p (if arg
                  (> (prefix-numeric-value arg) 0)
-               (not timeclock-modeline-display))))
+               (not timeclock-mode-line-display))))
     (if on-p
         (progn
           (or (memq 'timeclock-mode-string global-mode-string)
               (setq global-mode-string
                     (append global-mode-string '(timeclock-mode-string))))
-         (unless (memq 'timeclock-update-modeline timeclock-event-hook)
-           (add-hook 'timeclock-event-hook 'timeclock-update-modeline))
+         (add-hook 'timeclock-event-hook 'timeclock-update-mode-line)
          (when timeclock-update-timer
            (cancel-timer timeclock-update-timer)
            (setq timeclock-update-timer nil))
          (if (boundp 'display-time-hook)
-             (remove-hook 'display-time-hook 'timeclock-update-modeline))
+             (remove-hook 'display-time-hook 'timeclock-update-mode-line))
          (if timeclock-use-display-time
               (progn
                 ;; Update immediately so there is a visible change
                 ;; on calling this function.
-                (if display-time-mode (timeclock-update-modeline)
-                  (message "Activate `display-time-mode' to see \
-timeclock information"))
-                (add-hook 'display-time-hook 'timeclock-update-modeline))
+                (if display-time-mode
+                   (timeclock-update-mode-line)
+                  (message "Activate `display-time-mode' or turn off \
+`timeclock-use-display-time' to see timeclock information"))
+                (add-hook 'display-time-hook 'timeclock-update-mode-line))
            (setq timeclock-update-timer
-                 (run-at-time nil 60 'timeclock-update-modeline))))
+                 (run-at-time nil 60 'timeclock-update-mode-line))))
       (setq global-mode-string
            (delq 'timeclock-mode-string global-mode-string))
-      (remove-hook 'timeclock-event-hook 'timeclock-update-modeline)
+      (remove-hook 'timeclock-event-hook 'timeclock-update-mode-line)
       (if (boundp 'display-time-hook)
          (remove-hook 'display-time-hook
-                      'timeclock-update-modeline))
+                      'timeclock-update-mode-line))
       (when timeclock-update-timer
        (cancel-timer timeclock-update-timer)
        (setq timeclock-update-timer nil)))
     (force-mode-line-update)
-    (setq timeclock-modeline-display on-p)))
+    (setq timeclock-mode-line-display on-p)))
+
+(define-obsolete-variable-alias 'timeclock-modeline-display
+  'timeclock-mode-line-display "24.3")
 
 ;; This has to be here so that the function definition of
-;; `timeclock-modeline-display' is known to the "set" function.
-(defcustom timeclock-modeline-display nil
-  "Toggle modeline display of time remaining.
+;; `timeclock-mode-line-display' is known to the "set" function.
+(defcustom timeclock-mode-line-display nil
+  "Toggle mode line display of time remaining.
 You must modify via \\[customize] for this variable to have an effect."
   :set (lambda (symbol value)
-        (setq timeclock-modeline-display
-              (timeclock-modeline-display (or value 0))))
+        (setq timeclock-mode-line-display
+              (timeclock-mode-line-display (or value 0))))
   :type 'boolean
   :group 'timeclock
   :require 'timeclock)
@@ -341,7 +344,7 @@ You must modify via \\[customize] for this variable to have an effect."
 (defun timeclock-in (&optional arg project find-project)
   "Clock in, recording the current time moment in the timelog.
 With a numeric prefix ARG, record the fact that today has only that
-many hours in it to be worked.  If arg is a non-numeric prefix arg
+many hours in it to be worked.  If ARG is a non-numeric prefix argument
 \(non-nil, but not a number), 0 is assumed (working on a holiday or
 weekend).  *If not called interactively, ARG should be the number of
 _seconds_ worked today*.  This feature only has effect the first time
@@ -380,7 +383,8 @@ discover the name of the project."
                                               60 60.0) 60))))))
     (timeclock-log "i" (or project
                           (and timeclock-get-project-function
-                               (or find-project (interactive-p))
+                               (or find-project
+                                   (called-interactively-p 'interactive))
                                (funcall timeclock-get-project-function))))
     (run-hooks 'timeclock-in-hook)))
 
@@ -403,13 +407,13 @@ discover the reason."
      (if arg "O" "o")
      (or reason
         (and timeclock-get-reason-function
-             (or find-reason (interactive-p))
+             (or find-reason (called-interactively-p 'interactive))
              (funcall timeclock-get-reason-function))))
     (run-hooks 'timeclock-out-hook)
     (if arg
        (run-hooks 'timeclock-done-hook))))
 
-;; Should today-only be removed in favour of timeclock-relative? - gm
+;; Should today-only be removed in favor of timeclock-relative? - gm
 (defsubst timeclock-workday-remaining (&optional today-only)
   "Return the number of seconds until the workday is complete.
 The amount returned is relative to the value of `timeclock-workday'.
@@ -447,7 +451,7 @@ worked today, ignoring the time worked on previous days."
                  (if (> remainder 0)
                      "remaining" "over")
                  (timeclock-when-to-leave-string show-seconds today-only)))
-    (if (interactive-p)
+    (if (called-interactively-p 'interactive)
        (message "%s" status)
       status)))
 
@@ -460,7 +464,7 @@ time of changeover.  PROJECT is the name of the last project you were
 working on."
   (interactive "P")
   (timeclock-out arg)
-  (timeclock-in nil project (interactive-p)))
+  (timeclock-in nil project (called-interactively-p 'interactive)))
 
 ;;;###autoload
 (defun timeclock-query-out ()
@@ -479,8 +483,8 @@ Returns the new value of `timeclock-discrepancy'."
   (interactive)
   (setq timeclock-discrepancy nil)
   (timeclock-find-discrep)
-  (if (and timeclock-discrepancy timeclock-modeline-display)
-      (timeclock-update-modeline))
+  (if (and timeclock-discrepancy timeclock-mode-line-display)
+      (timeclock-update-mode-line))
   timeclock-discrepancy)
 
 (defun timeclock-seconds-to-string (seconds &optional show-seconds
@@ -518,7 +522,7 @@ See `timeclock-relative' for more information about the meaning of
   (let ((string (timeclock-seconds-to-string
                 (timeclock-workday-remaining today-only)
                 show-seconds t)))
-    (if (interactive-p)
+    (if (called-interactively-p 'interactive)
        (message "%s" string)
       string)))
 
@@ -540,23 +544,16 @@ non-nil, the amount returned will be relative to past time worked."
   (interactive)
   (let ((string (timeclock-seconds-to-string (timeclock-workday-elapsed)
                                             show-seconds)))
-    (if (interactive-p)
+    (if (called-interactively-p 'interactive)
        (message "%s" string)
       string)))
 
-(defsubst timeclock-time-to-seconds (time)
-  "Convert TIME to a floating point number."
-  (+ (* (car time) 65536.0)
-     (cadr time)
-     (/ (or (car (cdr (cdr time))) 0) 1000000.0)))
+(defalias 'timeclock-time-to-seconds (if (fboundp 'float-time) 'float-time
+                                      'time-to-seconds))
 
-(defsubst timeclock-seconds-to-time (seconds)
-  "Convert SECONDS (a floating point number) to an Emacs time structure."
-  (list (floor seconds 65536)
-       (floor (mod seconds 65536))
-       (floor (* (- seconds (ffloor seconds)) 1000000))))
+(defalias 'timeclock-seconds-to-time 'seconds-to-time)
 
-;; Should today-only be removed in favour of timeclock-relative? - gm
+;; Should today-only be removed in favor of timeclock-relative? - gm
 (defsubst timeclock-when-to-leave (&optional today-only)
   "Return a time value representing the end of today's workday.
 If TODAY-ONLY is non-nil, the value returned will be relative only to
@@ -578,27 +575,61 @@ This string is relative to the value of `timeclock-workday'.  If
 SHOW-SECONDS is non-nil, the value printed/returned will include
 seconds.  If TODAY-ONLY is non-nil, the value returned will be
 relative only to the time worked today, and not to past time."
-  ;; Should today-only be removed in favour of timeclock-relative? - gm
+  ;; Should today-only be removed in favor of timeclock-relative? - gm
   (interactive)
   (let* ((then (timeclock-when-to-leave today-only))
         (string
          (if show-seconds
              (format-time-string "%-I:%M:%S %p" then)
            (format-time-string "%-I:%M %p" then))))
-    (if (interactive-p)
+    (if (called-interactively-p 'interactive)
        (message "%s" string)
       string)))
 
+(defun timeclock-make-hours-explicit (old-default)
+  "Specify all workday lengths in `timeclock-file'.
+OLD-DEFAULT hours are set for every day that has no number indicated."
+  (interactive "P")
+  (if old-default (setq old-default (prefix-numeric-value old-default))
+    (error "`timelog-make-hours-explicit' requires an explicit argument"))
+  (let ((extant-timelog (find-buffer-visiting timeclock-file))
+       current-date)
+    (with-current-buffer (find-file-noselect timeclock-file t)
+      (unwind-protect
+         (save-excursion
+           (save-restriction
+             (widen)
+             (goto-char (point-min))
+             (while (progn (skip-chars-forward "\n") (not (eobp)))
+               ;; This is just a variant of `timeclock-moment-regexp'.
+               (unless (looking-at
+                        (concat "^\\([bhioO]\\) \\([0-9]+/[0-9]+/[0-9]+\\) "
+                                "\\([0-9]+:[0-9]+:[0-9]+\\)"))
+                 (error "Can't parse `%s'" timeclock-file))
+               (let ((this-date (match-string 2)))
+                 (unless (or (and current-date
+                                  (string= this-date current-date))
+                             (string= (match-string 1) "h"))
+                   (insert (format "h %s %s %s\n" (match-string 2)
+                                   (match-string 3) old-default)))
+                 (if (string-match "^[ih]" (match-string 1)) ; ignore logouts
+                     (setq current-date this-date)))
+               (forward-line))
+             (save-buffer)))
+       (unless extant-timelog (kill-buffer (current-buffer)))))))
+
 ;;; Internal Functions:
 
 (defvar timeclock-project-list nil)
 (defvar timeclock-last-project nil)
 
 (defun timeclock-completing-read (prompt alist &optional default)
-  "A version of `completing-read' that works on both Emacs and XEmacs."
+  "A version of `completing-read' that works on both Emacs and XEmacs.
+PROMPT, ALIST and DEFAULT are used for the PROMPT, COLLECTION and DEF
+arguments of `completing-read'."
   (if (featurep 'xemacs)
       (let ((str (completing-read prompt alist)))
-       (if (or (null str) (= (length str) 0))
+       (if (or (null str) (zerop (length str)))
            default
          str))
     (completing-read prompt alist nil nil nil nil default)))
@@ -620,8 +651,11 @@ relative only to the time worked today, and not to past time."
   (timeclock-completing-read "Reason for clocking out: "
                             (mapcar 'list timeclock-reason-list)))
 
-(defun timeclock-update-modeline ()
-  "Update the `timeclock-mode-string' displayed in the modeline.
+(define-obsolete-function-alias 'timeclock-update-modeline
+  'timeclock-update-mode-line "24.3")
+
+(defun timeclock-update-mode-line ()
+  "Update the `timeclock-mode-string' displayed in the mode line.
 The value of `timeclock-relative' affects the display as described in
 that variable's documentation."
   (interactive)
@@ -653,7 +687,7 @@ that variable's documentation."
 If PROJECT is a string, it represents the project which the event is
 being logged for.  Normally only \"in\" events specify a project."
   (let ((extant-timelog (find-buffer-visiting timeclock-file)))
-    (with-current-buffer (find-file-noselect timeclock-file)
+    (with-current-buffer (find-file-noselect timeclock-file t)
       (save-excursion
        (save-restriction
          (widen)
@@ -663,8 +697,7 @@ being logged for.  Normally only \"in\" events specify a project."
          (let ((now (current-time)))
            (insert code " "
                    (format-time-string "%Y/%m/%d %H:%M:%S" now)
-                   (or (and project
-                            (stringp project)
+                   (or (and (stringp project)
                             (> (length project) 0)
                             (concat " " project))
                        "")
@@ -679,8 +712,8 @@ being logged for.  Normally only \"in\" events specify a project."
                         timeclock-last-period)))
            (setq timeclock-last-event (list code now project)))))
       (save-buffer)
-      (run-hooks 'timeclock-event-hook)
-      (unless extant-timelog (kill-buffer (current-buffer))))))
+      (unless extant-timelog (kill-buffer (current-buffer)))))
+  (run-hooks 'timeclock-event-hook))
 
 (defvar timeclock-moment-regexp
   (concat "\\([bhioO]\\)\\s-+"
@@ -714,138 +747,159 @@ This is only provided for coherency when used by
     timeclock-last-period))
 
 (defsubst timeclock-entry-length (entry)
+  "Return the length of ENTRY in seconds."
   (- (timeclock-time-to-seconds (cadr entry))
      (timeclock-time-to-seconds (car entry))))
 
 (defsubst timeclock-entry-begin (entry)
+  "Return the start time of ENTRY."
   (car entry))
 
 (defsubst timeclock-entry-end (entry)
+  "Return the end time of ENTRY."
   (cadr entry))
 
 (defsubst timeclock-entry-project (entry)
+  "Return the project of ENTRY."
   (nth 2 entry))
 
 (defsubst timeclock-entry-comment (entry)
+  "Return the comment of ENTRY."
   (nth 3 entry))
 
-
 (defsubst timeclock-entry-list-length (entry-list)
+  "Return the total length of ENTRY-LIST in seconds."
   (let ((length 0))
-    (while entry-list
-      (setq length (+ length (timeclock-entry-length (car entry-list))))
-      (setq entry-list (cdr entry-list)))
+    (dolist (entry entry-list)
+      (setq length (+ length (timeclock-entry-length entry))))
     length))
 
 (defsubst timeclock-entry-list-begin (entry-list)
+  "Return the start time of the first element of ENTRY-LIST."
   (timeclock-entry-begin (car entry-list)))
 
 (defsubst timeclock-entry-list-end (entry-list)
+  "Return the end time of the last element of ENTRY-LIST."
   (timeclock-entry-end (car (last entry-list))))
 
 (defsubst timeclock-entry-list-span (entry-list)
+  "Return the total time in seconds spanned by ENTRY-LIST."
   (- (timeclock-time-to-seconds (timeclock-entry-list-end entry-list))
      (timeclock-time-to-seconds (timeclock-entry-list-begin entry-list))))
 
 (defsubst timeclock-entry-list-break (entry-list)
+  "Return the total break time (span - length) in ENTRY-LIST."
   (- (timeclock-entry-list-span entry-list)
      (timeclock-entry-list-length entry-list)))
 
 (defsubst timeclock-entry-list-projects (entry-list)
-  (let (projects)
-    (while entry-list
-      (let ((project (timeclock-entry-project (car entry-list))))
-       (if projects
-           (add-to-list 'projects project)
-         (setq projects (list project))))
-      (setq entry-list (cdr entry-list)))
+  "Return a list of all the projects in ENTRY-LIST."
+  (let (projects proj)
+    (dolist (entry entry-list)
+      (setq proj (timeclock-entry-project entry))
+      (if projects
+         (add-to-list 'projects proj)
+       (setq projects (list proj))))
     projects))
 
-
 (defsubst timeclock-day-required (day)
+  "Return the required length of DAY in seconds, default `timeclock-workday'."
   (or (car day) timeclock-workday))
 
 (defsubst timeclock-day-length (day)
+  "Return the actual length of DAY in seconds."
   (timeclock-entry-list-length (cdr day)))
 
 (defsubst timeclock-day-debt (day)
+  "Return the debt (required - actual) associated with DAY, in seconds."
   (- (timeclock-day-required day)
      (timeclock-day-length day)))
 
 (defsubst timeclock-day-begin (day)
+  "Return the start time of DAY."
   (timeclock-entry-list-begin (cdr day)))
 
 (defsubst timeclock-day-end (day)
+  "Return the end time of DAY."
   (timeclock-entry-list-end (cdr day)))
 
 (defsubst timeclock-day-span (day)
+  "Return the span of DAY."
   (timeclock-entry-list-span (cdr day)))
 
 (defsubst timeclock-day-break (day)
+  "Return the total break time of DAY."
   (timeclock-entry-list-break (cdr day)))
 
 (defsubst timeclock-day-projects (day)
-  (timeclock-entry-list-projects (cdr day)))
+  "Return a list of all the projects in DAY."
+  (timeclock-entry-list-projects (cddr day)))
 
 (defmacro timeclock-day-list-template (func)
+  "Template for summing the result of FUNC on each element of DAY-LIST."
   `(let ((length 0))
      (while day-list
-       (setq length (+ length (,(eval func) (car day-list))))
-       (setq day-list (cdr day-list)))
+       (setq length (+ length (,(eval func) (car day-list)))
+            day-list (cdr day-list)))
      length))
 
 (defun timeclock-day-list-required (day-list)
+  "Return total required length of DAY-LIST, in seconds."
   (timeclock-day-list-template 'timeclock-day-required))
 
 (defun timeclock-day-list-length (day-list)
+  "Return actual length of DAY-LIST, in seconds."
   (timeclock-day-list-template 'timeclock-day-length))
 
 (defun timeclock-day-list-debt (day-list)
+  "Return total debt (required - actual) of DAY-LIST."
   (timeclock-day-list-template 'timeclock-day-debt))
 
 (defsubst timeclock-day-list-begin (day-list)
+  "Return the start time of DAY-LIST."
   (timeclock-day-begin (car day-list)))
 
 (defsubst timeclock-day-list-end (day-list)
+  "Return the end time of DAY-LIST."
   (timeclock-day-end (car (last day-list))))
 
 (defun timeclock-day-list-span (day-list)
+  "Return the span of DAY-LIST."
   (timeclock-day-list-template 'timeclock-day-span))
 
 (defun timeclock-day-list-break (day-list)
+  "Return the total break of DAY-LIST."
   (timeclock-day-list-template 'timeclock-day-break))
 
 (defun timeclock-day-list-projects (day-list)
+  "Return a list of all the projects in DAY-LIST."
   (let (projects)
-    (while day-list
-      (let ((projs (timeclock-day-projects (car day-list))))
-       (while projs
-         (if projects
-             (add-to-list 'projects (car projs))
-           (setq projects (list (car projs))))
-         (setq projs (cdr projs))))
-      (setq day-list (cdr day-list)))
+    (dolist (day day-list)
+      (dolist (proj (timeclock-day-projects day))
+       (if projects
+           (add-to-list 'projects proj)
+         (setq projects (list proj)))))
     projects))
 
-
 (defsubst timeclock-current-debt (&optional log-data)
+  "Return the seconds debt from LOG-DATA, default `timeclock-log-data'."
   (nth 0 (or log-data (timeclock-log-data))))
 
 (defsubst timeclock-day-alist (&optional log-data)
+  "Return the date alist from LOG-DATA, default `timeclock-log-data'."
   (nth 1 (or log-data (timeclock-log-data))))
 
 (defun timeclock-day-list (&optional log-data)
-  (let ((alist (timeclock-day-alist log-data))
-       day-list)
-    (while alist
-      (setq day-list (cons (cdar alist) day-list)
-           alist (cdr alist)))
+  "Return a list of the cdrs of the date alist from LOG-DATA."
+  (let (day-list)
+    (dolist (date-list (timeclock-day-alist log-data))
+      (setq day-list (cons (cdr date-list) day-list)))
     day-list))
 
 (defsubst timeclock-project-alist (&optional log-data)
+  "Return the project alist from LOG-DATA, default `timeclock-log-data'."
   (nth 2 (or log-data (timeclock-log-data))))
 
-
 (defun timeclock-log-data (&optional recent-only filename)
   "Return the contents of the timelog file, in a useful format.
 If the optional argument RECENT-ONLY is non-nil, only show the contents
@@ -976,11 +1030,10 @@ lists:
   timeclock-current-debt LOG-DATA
 
 See the documentation for the given function if more info is needed."
-  (let* ((log-data (list 0.0 nil nil))
-        (now (current-time))
-        (todays-date (timeclock-time-to-date now))
-        last-date-limited last-date-seconds last-date
-        (line 0) last beg day entry event)
+  (let ((log-data (list 0.0 nil nil))
+       (now (current-time))
+       last-date-limited last-date-seconds last-date
+       (line 0) last beg day entry event)
     (with-temp-buffer
       (insert-file-contents (or filename timeclock-file))
       (when recent-only
@@ -1037,7 +1090,7 @@ See the documentation for the given function if more info is needed."
                   (if (null proj)
                       (setcar (cddr log-data)
                               (cons (cons desc (list entry))
-                                    (car (cddr log-data))))
+                                    (nth 2 log-data)))
                     (nconc (cdr proj) (list entry)))))))
        (forward-line))
       (if day
@@ -1066,7 +1119,7 @@ discrepancy, today's discrepancy, and the time worked today."
   (let* ((now (current-time))
         (todays-date (timeclock-time-to-date now))
         (first t) (accum 0) (elapsed 0)
-        event beg last-date avg
+        event beg last-date
         last-date-limited last-date-seconds)
     (unless timeclock-discrepancy
       (when (file-readable-p timeclock-file)
@@ -1152,23 +1205,22 @@ If optional argument TIME is non-nil, use that instead of the current time."
     (setcar (nthcdr 2 decoded) 0)
     (apply 'encode-time decoded)))
 
-(defun timeclock-geometric-mean (l)
-  "Compute the geometric mean of the values in the list L."
+(defun timeclock-mean (l)
+  "Compute the arithmetic mean of the values in the list L."
   (let ((total 0)
        (count 0))
-    (while l
-      (setq total (+ total (car l))
-           count (1+ count)
-           l (cdr l)))
-    (if (> count 0)
-       (/ total count)
-      0)))
+    (dolist (thisl l)
+      (setq total (+ total thisl)
+           count (1+ count)))
+    (if (zerop count)
+       0
+      (/ total count))))
 
 (defun timeclock-generate-report (&optional html-p)
   "Generate a summary report based on the current timelog file.
 By default, the report is in plain text, but if the optional argument
 HTML-P is non-nil, HTML markup is added."
-  (interactive)
+  (interactive "P")
   (let ((log (timeclock-log-data))
        (today (timeclock-day-base)))
     (if html-p (insert "<p>"))
@@ -1178,7 +1230,7 @@ HTML-P is non-nil, HTML markup is added."
          done)
       (if (timeclock-currently-in-p)
          (insert "IN")
-       (if (or (null project) (= (length project) 0))
+       (if (zerop (length project))
            (progn (insert "Done Working Today")
                   (setq done t))
          (insert "OUT")))
@@ -1265,22 +1317,22 @@ HTML-P is non-nil, HTML markup is added."
               (lengths  (vector '(0 0) thirty-days-ago three-months-ago
                                 six-months-ago one-year-ago)))
          ;; collect statistics from complete timelog
-         (while day-list
+         (dolist (day day-list)
            (let ((i 0) (l 5))
              (while (< i l)
                (unless (time-less-p
-                        (timeclock-day-begin (car day-list))
+                        (timeclock-day-begin day)
                         (aref lengths i))
                  (let ((base (timeclock-time-to-seconds
                               (timeclock-day-base
-                               (timeclock-day-begin (car day-list))))))
+                               (timeclock-day-begin day)))))
                    (nconc (aref time-in i)
                           (list (- (timeclock-time-to-seconds
-                                    (timeclock-day-begin (car day-list)))
+                                    (timeclock-day-begin day))
                                    base)))
-                   (let ((span (timeclock-day-span (car day-list)))
-                         (len (timeclock-day-length (car day-list)))
-                         (req (timeclock-day-required (car day-list))))
+                   (let ((span (timeclock-day-span day))
+                         (len (timeclock-day-length day))
+                         (req (timeclock-day-required day)))
                      ;; If the day's actual work length is less than
                      ;; 70% of its span, then likely the exit time
                      ;; and break amount are not worthwhile adding to
@@ -1289,25 +1341,20 @@ HTML-P is non-nil, HTML markup is added."
                                 (> (/ (float len) (float span)) 0.70))
                        (nconc (aref time-out i)
                               (list (- (timeclock-time-to-seconds
-                                        (timeclock-day-end (car day-list)))
+                                        (timeclock-day-end day))
                                        base)))
                        (nconc (aref breaks i) (list (- span len))))
                      (if req
                          (setq len (+ len (- timeclock-workday req))))
                      (nconc (aref workday i) (list len)))))
-               (setq i (1+ i))))
-           (setq day-list (cdr day-list)))
+               (setq i (1+ i)))))
          ;; average statistics
          (let ((i 0) (l 5))
            (while (< i l)
-             (aset time-in i (timeclock-geometric-mean
-                              (cdr (aref time-in i))))
-             (aset time-out i (timeclock-geometric-mean
-                               (cdr (aref time-out i))))
-             (aset breaks i (timeclock-geometric-mean
-                             (cdr (aref breaks i))))
-             (aset workday i (timeclock-geometric-mean
-                              (cdr (aref workday i))))
+             (aset time-in i (timeclock-mean (cdr (aref time-in i))))
+             (aset time-out i (timeclock-mean (cdr (aref time-out i))))
+             (aset breaks i (timeclock-mean (cdr (aref breaks i))))
+             (aset workday i (timeclock-mean (cdr (aref workday i))))
              (setq i (1+ i))))
          ;; Output the HTML table
          (insert "<tr>\n")
@@ -1372,5 +1419,4 @@ HTML-P is non-nil, HTML markup is added."
 (if (file-readable-p timeclock-file)
     (timeclock-reread-log))
 
-;;; arch-tag: a0be3377-deb6-44ec-b9a2-a7be28436a40
 ;;; timeclock.el ends here