+(defun org-agenda-show-clocking-issues ()
+ "Add overlays, showing issues with clocking.
+See also the user option `org-agenda-clock-consistency-checks'."
+ (interactive)
+ (let* ((pl org-agenda-clock-consistency-checks)
+ (re (concat "^[ \t]*"
+ org-clock-string
+ "[ \t]+"
+ "\\(\\[.*?\\]\\)" ; group 1 is first stamp
+ "\\(-\\{1,3\\}\\(\\[.*?\\]\\)\\)?")) ; group 3 is second
+ (tlstart 0.)
+ (tlend 0.)
+ (maxtime (org-hh:mm-string-to-minutes
+ (or (plist-get pl :max-duration) "24:00")))
+ (mintime (org-hh:mm-string-to-minutes
+ (or (plist-get pl :min-duration) 0)))
+ (maxgap (org-hh:mm-string-to-minutes
+ ;; default 30:00 means never complain
+ (or (plist-get pl :max-gap) "30:00")))
+ (gapok (mapcar 'org-hh:mm-string-to-minutes
+ (plist-get pl :gap-ok-around)))
+ (def-face (or (plist-get pl :default-face)
+ '((:background "DarkRed") (:foreground "white"))))
+ issue face m te ts dt ov)
+ (goto-char (point-min))
+ (while (re-search-forward " Clocked: +(-\\|\\([0-9]+:[0-9]+\\))" nil t)
+ (setq issue nil face def-face)
+ (catch 'next
+ (setq m (org-get-at-bol 'org-marker)
+ te nil ts nil)
+ (unless (and m (markerp m))
+ (setq issue "No valid clock line") (throw 'next t))
+ (org-with-point-at m
+ (save-excursion
+ (goto-char (point-at-bol))
+ (unless (looking-at re)
+ (error "No valid Clock line")
+ (throw 'next t))
+ (unless (match-end 3)
+ (setq issue "No end time"
+ face (or (plist-get pl :no-end-time-face) face))
+ (throw 'next t))
+ (setq ts (match-string 1)
+ te (match-string 3)
+ ts (org-float-time
+ (apply 'encode-time (org-parse-time-string ts)))
+ te (org-float-time
+ (apply 'encode-time (org-parse-time-string te)))
+ dt (- te ts))))
+ (cond
+ ((> dt (* 60 maxtime))
+ ;; a very long clocking chunk
+ (setq issue (format "Clocking interval is very long: %s"
+ (org-minutes-to-hh:mm-string
+ (floor (/ (float dt) 60.))))
+ face (or (plist-get pl :long-face) face)))
+ ((< dt (* 60 mintime))
+ ;; a very short clocking chunk
+ (setq issue (format "Clocking interval is very short: %s"
+ (org-minutes-to-hh:mm-string
+ (floor (/ (float dt) 60.))))
+ face (or (plist-get pl :short-face) face)))
+ ((and (> tlend 0) (< ts tlend))
+ ;; Two clock entries are overlapping
+ (setq issue (format "Clocking overlap: %d minutes"
+ (/ (- tlend ts) 60))
+ face (or (plist-get pl :overlap-face) face)))
+ ((and (> tlend 0) (> ts (+ tlend (* 60 maxgap))))
+ ;; There is a gap, lets see if we need to report it
+ (unless (org-agenda-check-clock-gap tlend ts gapok)
+ (setq issue (format "Clocking gap: %d minutes"
+ (/ (- ts tlend) 60))
+ face (or (plist-get pl :gap-face) face))))
+ (t nil)))
+ (setq tlend (or te tlend) tlstart (or ts tlstart))
+ (when issue
+ ;; OK, there was some issue, add an overlay to show the issue
+ (setq ov (make-overlay (point-at-bol) (point-at-eol)))
+ (overlay-put ov 'before-string
+ (concat
+ (org-add-props
+ (format "%-43s" (concat " " issue))
+ nil
+ 'face face)
+ "\n"))
+ (overlay-put ov 'evaporate t)))))
+
+(defun org-agenda-check-clock-gap (t1 t2 ok-list)
+ "Check if gap T1 -> T2 contains one of the OK-LIST time-of-day values."
+ (catch 'exit
+ (unless ok-list
+ ;; there are no OK times for gaps...
+ (throw 'exit nil))
+ (if (> (- (/ t2 36000) (/ t1 36000)) 24)
+ ;; This is more than 24 hours, so it is OK.
+ ;; because we have at least one OK time, that must be in the
+ ;; 24 hour interval.
+ (throw 'exit t))
+ ;; We have a shorter gap.
+ ;; Now we have to get the minute of the day when these times are
+ (let* ((t1dec (decode-time (seconds-to-time t1)))
+ (t2dec (decode-time (seconds-to-time t2)))
+ ;; compute the minute on the day
+ (min1 (+ (nth 1 t1dec) (* 60 (nth 2 t1dec))))
+ (min2 (+ (nth 1 t2dec) (* 60 (nth 2 t2dec)))))
+ (when (< min2 min1)
+ ;; if min2 is smaller than min1, this means it is on the next day.
+ ;; Wrap it to after midnight.
+ (setq min2 (+ min2 1440)))
+ ;; Now check if any of the OK times is in the gap
+ (mapc (lambda (x)
+ ;; Wrap the time to after midnight if necessary
+ (if (< x min1) (setq x (+ x 1440)))
+ ;; Check if in interval
+ (and (<= min1 x) (>= min2 x) (throw 'exit t)))
+ ok-list)
+ ;; Nope, this gap is not OK
+ nil)))
+