Add 2010 to copyright years.
[bpt/emacs.git] / lisp / calendar / timeclock.el
index 19191fd..32e0bf6 100644 (file)
@@ -1,18 +1,19 @@
 ;;; timeclock.el --- mode for keeping track of how much you work
 
-;; Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
+;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+;;   2008, 2009, 2010  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
@@ -20,9 +21,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:
 
 ;;; 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 maken 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
@@ -105,7 +104,7 @@ previous days.  This only affects the timeclock modeline display."
   :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."
@@ -113,7 +112,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."
@@ -121,7 +120,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
@@ -132,7 +131,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
@@ -150,7 +149,7 @@ This variable only has effect if set with \\[customize]."
 (defvar timeclock-modeline-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 modeline 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
 you to have `display-time' running.  If you don't want to use
@@ -186,7 +185,7 @@ a positive argument to force an update."
   :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
@@ -195,34 +194,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'."
   :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)
 
@@ -250,7 +249,10 @@ each day.")
 This value is not accurate enough to be useful by itself.  Rather,
 call `timeclock-workday-elapsed', to determine how much time has been
 worked so far today.  Also, if `timeclock-relative' is nil, this value
-will be the same as `timeclock-discrepancy'.") ; ? gm
+will be the same as `timeclock-discrepancy'.")
+
+(defvar timeclock-use-elapsed nil
+  "Non-nil if the modeline should display time elapsed, not remaining.")
 
 (defvar timeclock-last-period nil
   "Integer representing the number of seconds in the last period.
@@ -300,8 +302,8 @@ display (non-nil means on)."
                 ;; 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"))
+                  (message "Activate `display-time-mode' or turn off \
+`timeclock-use-display-time' to see timeclock information"))
                 (add-hook 'display-time-hook 'timeclock-update-modeline))
            (setq timeclock-update-timer
                  (run-at-time nil 60 'timeclock-update-modeline))))
@@ -337,7 +339,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
@@ -371,11 +373,13 @@ discover the name of the project."
        (setq timeclock-discrepancy
              (- (or timeclock-discrepancy 0) workday))
        (if (not (= workday timeclock-workday))
-           (timeclock-log "h" (and (numberp arg)
-                                   (number-to-string arg))))))
+           (timeclock-log "h" (number-to-string
+                               (/ workday (if (zerop (% workday (* 60 60)))
+                                              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)))
 
@@ -398,7 +402,7 @@ 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
@@ -423,7 +427,9 @@ If SHOW-SECONDS is non-nil, display second resolution.
 If TODAY-ONLY is non-nil, the display will be relative only to time
 worked today, ignoring the time worked on previous days."
   (interactive "P")
-  (let ((remainder (timeclock-workday-remaining)) ; today-only?
+  (let ((remainder (timeclock-workday-remaining
+                   (or today-only
+                       (not timeclock-relative))))
         (last-in (equal (car timeclock-last-event) "i"))
         status)
     (setq status
@@ -440,8 +446,8 @@ 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)
-       (message status)
+    (if (called-interactively-p 'interactive)
+       (message "%s" status)
       status)))
 
 ;;;###autoload
@@ -453,7 +459,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 ()
@@ -511,8 +517,8 @@ 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)
-       (message string)
+    (if (called-interactively-p 'interactive)
+       (message "%s" string)
       string)))
 
 (defsubst timeclock-workday-elapsed ()
@@ -533,15 +539,15 @@ 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)
-       (message string)
+    (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)))
+     (/ (or (nth 2 time) 0) 1000000.0)))
 
 (defsubst timeclock-seconds-to-time (seconds)
   "Convert SECONDS (a floating point number) to an Emacs time structure."
@@ -578,20 +584,54 @@ relative only to the time worked today, and not to past time."
          (if show-seconds
              (format-time-string "%-I:%M:%S %p" then)
            (format-time-string "%-I:%M %p" then))))
-    (if (interactive-p)
-       (message string)
+    (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)))
@@ -599,7 +639,7 @@ relative only to the time worked today, and not to past time."
 (defun timeclock-ask-for-project ()
   "Ask the user for the project they are clocking into."
   (timeclock-completing-read
-   (format "Clock into which project (default \"%s\"): "
+   (format "Clock into which project (default %s): "
           (or timeclock-last-project
               (car timeclock-project-list)))
    (mapcar 'list timeclock-project-list)
@@ -618,7 +658,10 @@ relative only to the time worked today, and not to past time."
 The value of `timeclock-relative' affects the display as described in
 that variable's documentation."
   (interactive)
-  (let ((remainder (timeclock-workday-remaining (not timeclock-relative)))
+  (let ((remainder
+        (if timeclock-use-elapsed
+            (timeclock-workday-elapsed)
+          (timeclock-workday-remaining (not timeclock-relative))))
         (last-in (equal (car timeclock-last-event) "i")))
     (when (and (< remainder 0)
               (not (and timeclock-day-over
@@ -642,31 +685,34 @@ that variable's documentation."
   "Log the event CODE to the timeclock log, at the time of call.
 If PROJECT is a string, it represents the project which the event is
 being logged for.  Normally only \"in\" events specify a project."
-  (with-current-buffer (find-file-noselect timeclock-file)
-    (goto-char (point-max))
-    (if (not (bolp))
-       (insert "\n"))
-    (let ((now (current-time)))
-      (insert code " "
-             (format-time-string "%Y/%m/%d %H:%M:%S" now)
-             (or (and project
-                      (stringp project)
-                      (> (length project) 0)
-                      (concat " " project))
-                 "")
-             "\n")
-      (if (equal (downcase code) "o")
-         (setq timeclock-last-period
-               (- (timeclock-time-to-seconds now)
-                  (timeclock-time-to-seconds
-                   (cadr timeclock-last-event)))
-               timeclock-discrepancy
-               (+ timeclock-discrepancy
-                  timeclock-last-period)))
-      (setq timeclock-last-event (list code now project)))
-    (save-buffer)
-    (run-hooks 'timeclock-event-hook)
-    (kill-buffer (current-buffer))))
+  (let ((extant-timelog (find-buffer-visiting timeclock-file)))
+    (with-current-buffer (find-file-noselect timeclock-file t)
+      (save-excursion
+       (save-restriction
+         (widen)
+         (goto-char (point-max))
+         (if (not (bolp))
+             (insert "\n"))
+         (let ((now (current-time)))
+           (insert code " "
+                   (format-time-string "%Y/%m/%d %H:%M:%S" now)
+                   (or (and (stringp project)
+                            (> (length project) 0)
+                            (concat " " project))
+                       "")
+                   "\n")
+           (if (equal (downcase code) "o")
+               (setq timeclock-last-period
+                     (- (timeclock-time-to-seconds now)
+                        (timeclock-time-to-seconds
+                         (cadr timeclock-last-event)))
+                     timeclock-discrepancy
+                     (+ timeclock-discrepancy
+                        timeclock-last-period)))
+           (setq timeclock-last-event (list code now project)))))
+      (save-buffer)
+      (unless extant-timelog (kill-buffer (current-buffer)))))
+  (run-hooks 'timeclock-event-hook))
 
 (defvar timeclock-moment-regexp
   (concat "\\([bhioO]\\)\\s-+"
@@ -700,138 +746,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
@@ -1023,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
@@ -1138,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>"))
@@ -1164,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")))
@@ -1251,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
@@ -1275,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")
@@ -1358,5 +1419,5 @@ 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
+;; arch-tag: a0be3377-deb6-44ec-b9a2-a7be28436a40
 ;;; timeclock.el ends here