Browse Source

Use Org duration library

* contrib/lisp/org-depend.el (org-depend-trigger-todo): Use new
  functions.

* contrib/lisp/org-invoice.el (org-invoice-heading-info):
(org-invoice-info-to-table):
(org-invoice-list-to-table): Use new functions.

* contrib/lisp/ox-taskjuggler.el (org-taskjuggler--build-task): Use
  new functions.

* lisp/org-agenda.el (org-agenda-show-clocking-issues):
(org-agenda-format-item):
(org-agenda-filter-effort-form): Use new functions.

* lisp/org-clock.el (org-clock-get-clock-string):
(org-clock-modify-effort-estimate):
(org-clock-notify-once-if-expired):
(org-clock-out):
(org-clock-display):
(org-clock-put-overlay):
(org-clocktable-write-default): Use new functions.

* lisp/org-table.el (org-table-sort-lines): Use new functions.

* lisp/org.el (org-properties-postprocess-alist):
(org-refresh-effort-properties):
(org-set-effort):
(org-entry-properties):
(org-property-next-allowed-value): Use new functions.

(org-time-clocksum-format):
(org-time-clocksum-use-fractional):
(org-time-clocksum-use-fractional-format):
(org-time-clocksum-use-effort-durations): Declare as obsolete.  Move
to "org-compat.el".

(org-minutes-to-clocksum-string):
(org-hh:mm-string-to-minutes):
(org-duration-string-to-minutes): Declare as obsolete.  Move to
"org-compat.el".
(org-hours-to-clocksum-string): Remove function.

* lisp/org-colview.el (org-columns--collect-values): Use new
  functions.
(org-columns--duration-re): Remove variable.
(org-columns--time-to-seconds): Rename to...
(org-columns--age-to-minutes): ... this.
(org-columns--format-age): New function.
(org-columns--summary-apply-times):
(org-columns--summary-min-age):
(org-columns--summary-max-age):
(org-columns--summary-mean-age): Use new functions.

* testing/lisp/test-org-clock.el (test-org-clock-clocktable-contents-at-point):
* testing/lisp/test-org-colview.el (test-org-colview/columns-summary):
  Update tests.
Nicolas Goaziou 2 years ago
parent
commit
7e8cf5f4c2

+ 1 - 1
contrib/lisp/org-depend.el

@@ -270,7 +270,7 @@ This does two different kinds of triggers:
 			    (effort (when (or effort-up effort-down)
 				      (let ((effort (get-text-property (point) 'org-effort)))
 					(when effort
-					  (org-duration-string-to-minutes effort))))))
+					  (org-duration-to-minutes effort))))))
 			(push (list (point) todo-kwd priority tags effort)
 			      items))
 		      (unless (org-goto-sibling)

+ 6 - 4
contrib/lisp/org-invoice.el

@@ -55,6 +55,8 @@
   (require 'cl)
   (require 'org))
 
+(declare-function org-duration-from-minutes "org-duration" (minutes &optional fmt fractional))
+
 (defgroup org-invoice nil
   "OrgMode Invoice Helper"
   :tag "Org-Invoice" :group 'org)
@@ -159,7 +161,7 @@ looks like tree2, where the level is 2."
       (setq title (replace-match "" nil nil title)))
     (when (string-match "[ \t]+$" title)
       (setq title (replace-match "" nil nil title)))
-    (setq work (org-hh:mm-string-to-minutes work))
+    (setq work (org-duration-to-minutes work))
     (setq rate (string-to-number rate))
     (setq org-invoice-current-item (list (cons 'title title)
           (cons 'date date)
@@ -226,8 +228,8 @@ looks like tree2, where the level is 2."
       (setq
        org-invoice-total-time (+ org-invoice-total-time work)
        org-invoice-total-price (+ org-invoice-total-price price)))
-    (setq total (and total (org-minutes-to-clocksum-string total)))
-    (setq work  (and work  (org-minutes-to-clocksum-string work)))
+    (setq total (and total (org-duration-from-minutes total)))
+    (setq work  (and work  (org-duration-from-minutes work)))
     (insert-before-markers
      (concat "|" title
              (cond
@@ -251,7 +253,7 @@ looks like tree2, where the level is 2."
     (when with-summary
       (insert-before-markers
        (concat "|-\n|Total:|"
-               (org-minutes-to-clocksum-string org-invoice-total-time)
+               (org-duration-from-minutes org-invoice-total-time)
                (and with-price (concat "|" (format "%.2f" org-invoice-total-price)))
                "|\n")))))
 

+ 1 - 1
contrib/lisp/ox-taskjuggler.el

@@ -861,7 +861,7 @@ a unique id will be associated to it."
      (and complete (format "  complete %s\n" complete))
      (and effort
           (format "  effort %s\n"
-                  (let* ((minutes (org-duration-string-to-minutes effort))
+                  (let* ((minutes (org-duration-minutes effort))
                          (hours (/ minutes 60.0)))
                     (format "%.1fh" hours))))
      (and priority (format "  priority %s\n" priority))

+ 33 - 4
etc/ORG-NEWS

@@ -12,6 +12,15 @@ Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
 
 ** Incompatible changes
 
+*** Variables relative to clocksum duration are obsolete
+
+~org-time-clocksum-format~, ~org-time-clocksum-use-fractional~ and
+~org-time-clocksum-fractional-format~ are obsolete.  If you changed
+them, consider modifying ~org-duration-format~ instead.
+
+Variable ~org-time-clocksum-use-effort-durations~ is also obsolete.
+Consider setting ~org-duration-units~ instead.
+
 *** ~org-capture-templates~ no longer accepts S-expressions as file names
 
 Since functions are allowed there, a straightforward way to migrate
@@ -67,11 +76,12 @@ The optional argument is now a string to extract the repeater from.
 See docstring for details.
 
 ** New features
-*** ~org-edit-special~ can edit LaTeX environments
+*** New Org duration library
+This new library implements tools to read and print time durations in
+various formats (e.g., "H:MM", or "1d 2h 3min"...).
 
-Using ~C-c '~ on a LaTeX environment opens a sub-editing buffer.  By
-default, major mode in that buffer is ~latex-mode~, but it can be
-changed by configuring ~org-src-lang-modes~.
+See ~org-duration-to-minutes~ and ~org-duration-from-minutes~
+docstrings.
 
 *** Agenda
 **** New variable : ~org-agenda-show-future-repeats~
@@ -163,6 +173,12 @@ user to specify the name of the output file upon exporting the
 document.  This also has an effect on publishing.
 **** Horizontal rules are no longer ignored in LaTeX table math mode
 
+*** ~org-edit-special~ can edit LaTeX environments
+
+Using ~C-c '~ on a LaTeX environment opens a sub-editing buffer.  By
+default, major mode in that buffer is ~latex-mode~, but it can be
+changed by configuring ~org-src-lang-modes~.
+
 *** ~org-list-to-generic~ includes a new property: ~:ifmt~
 
 ~:ifmt~ is a function to be called on the body of each item.  See
@@ -189,6 +205,19 @@ Use ~org-agenda-skip-if~ instead.
 *** ~org-agenda-skip-entry-when-regexp-matches-in-subtree~ is obsolete
 
 Use ~org-agenda-skip-if~ instead.
+
+*** ~org-minutes-to-clocksum-string~ is obsolete
+
+Use ~org-duration-from-minutes~ instead.
+
+*** ~org-hh:mm-string-to-minutes~ is obsolete
+
+Use ~org-duration-to-minutes~ instead.
+
+*** ~org-duration-string-to-minutes~ is obsolete
+
+Use ~org-duration-to-minutes~ instead.
+
 ** Removed options
 
 *** ~org-agenda-repeating-timestamp-show-all~ is removed.

+ 18 - 20
lisp/org-agenda.el

@@ -5746,8 +5746,7 @@ then those holidays will be skipped."
   "Add overlays, showing issues with clocking.
 See also the user option `org-agenda-clock-consistency-checks'."
   (interactive)
-  (let* ((org-time-clocksum-use-effort-durations nil)
-	 (pl org-agenda-clock-consistency-checks)
+  (let* ((pl org-agenda-clock-consistency-checks)
 	 (re (concat "^[ \t]*"
 		     org-clock-string
 		     "[ \t]+"
@@ -5755,14 +5754,14 @@ See also the user option `org-agenda-clock-consistency-checks'."
 		     "\\(-\\{1,3\\}\\(\\[.*?\\]\\)\\)?")) ; group 3 is second
 	 (tlstart 0.)
 	 (tlend 0.)
-	 (maxtime (org-hh:mm-string-to-minutes
+	 (maxtime (org-duration-to-minutes
 		   (or (plist-get pl :max-duration) "24:00")))
-	 (mintime (org-hh:mm-string-to-minutes
+	 (mintime (org-duration-to-minutes
 		   (or (plist-get pl :min-duration) 0)))
-	 (maxgap  (org-hh:mm-string-to-minutes
+	 (maxgap  (org-duration-to-minutes
 		   ;; default 30:00 means never complain
 		   (or (plist-get pl :max-gap) "30:00")))
-	 (gapok (mapcar 'org-hh:mm-string-to-minutes
+	 (gapok (mapcar #'org-duration-to-minutes
 			(plist-get pl :gap-ok-around)))
 	 (def-face (or (plist-get pl :default-face)
 		       '((:background "DarkRed") (:foreground "white"))))
@@ -5796,14 +5795,12 @@ See also the user option `org-agenda-clock-consistency-checks'."
 	 ((> dt (* 60 maxtime))
 	  ;; a very long clocking chunk
 	  (setq issue (format "Clocking interval is very long: %s"
-			      (org-minutes-to-clocksum-string
-			       (floor (/ (float dt) 60.))))
+			      (org-duration-from-minutes (floor (/ 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-clocksum-string
-			       (floor (/ (float dt) 60.))))
+			      (org-duration-from-minutes (floor (/ dt 60.))))
 		face (or (plist-get pl :short-face) face)))
 	 ((and (> tlend 0) (< ts tlend))
 	  ;; Two clock entries are overlapping
@@ -6405,18 +6402,19 @@ Any match of REMOVE-RE will be removed from TXT."
 	  (if s1 (setq s1 (org-get-time-of-day s1 'string t)))
 	  (if s2 (setq s2 (org-get-time-of-day s2 'string t)))
 
-	  ;; Try to set s2 if s1 and `org-agenda-default-appointment-duration' are set
-	  (let (org-time-clocksum-use-effort-durations)
-	    (when (and s1 (not s2) org-agenda-default-appointment-duration)
-	      (setq s2
-		    (org-minutes-to-clocksum-string
-		     (+ (org-hh:mm-string-to-minutes s1)
-			org-agenda-default-appointment-duration)))))
+	  ;; Try to set s2 if s1 and
+	  ;; `org-agenda-default-appointment-duration' are set
+	  (when (and s1 (not s2) org-agenda-default-appointment-duration)
+	    (setq s2
+		  (org-duration-from-minutes
+		   (+ (org-duration-to-minutes s1 t)
+		      org-agenda-default-appointment-duration)
+		   nil t)))
 
 	  ;; Compute the duration
 	  (when s2
-	    (setq duration (- (org-hh:mm-string-to-minutes s2)
-			      (org-hh:mm-string-to-minutes s1)))))
+	    (setq duration (- (org-duration-to-minutes s2)
+			      (org-duration-to-minutes s1)))))
 
 	(when (string-match "\\([ \t]+\\)\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$" txt)
 	  ;; Tags are in the string
@@ -7548,7 +7546,7 @@ E looks like \"+<2:25\"."
 		   ((equal op ??) op)
 		   (t '=)))
     (list 'org-agenda-compare-effort (list 'quote op)
-	  (org-duration-string-to-minutes e))))
+	  (org-duration-to-minutes e))))
 
 (defun org-agenda-compare-effort (op value)
   "Compare the effort of the current line with VALUE, using OP.

+ 14 - 17
lisp/org-clock.el

@@ -665,20 +665,19 @@ If an effort estimate was defined for the current item, use
 If not, show simply the clocked time like 01:50."
   (let ((clocked-time (org-clock-get-clocked-time)))
     (if org-clock-effort
-	(let* ((effort-in-minutes
-		(org-duration-string-to-minutes org-clock-effort))
+	(let* ((effort-in-minutes (org-duration-to-minutes org-clock-effort))
 	       (work-done-str
 		(propertize
-		 (org-minutes-to-clocksum-string clocked-time)
+		 (org-duration-from-minutes clocked-time)
 		 'face (if (and org-clock-task-overrun (not org-clock-task-overrun-text))
 			   'org-mode-line-clock-overrun 'org-mode-line-clock)))
-	       (effort-str (org-minutes-to-clocksum-string effort-in-minutes))
+	       (effort-str (org-duration-from-minutes effort-in-minutes))
 	       (clockstr (propertize
 			  (concat  " [%s/" effort-str
 				   "] (" (replace-regexp-in-string "%" "%%" org-clock-heading) ")")
 			  'face 'org-mode-line-clock)))
 	  (format clockstr work-done-str))
-      (propertize (concat " [" (org-minutes-to-clocksum-string clocked-time)
+      (propertize (concat " [" (org-duration-from-minutes clocked-time)
 			  "]" (format " (%s)" org-clock-heading))
 		  'face 'org-mode-line-clock))))
 
@@ -748,15 +747,15 @@ clocked item, and the value displayed in the mode line."
 	  ;; A string.  See if it is a delta
 	  (setq sign (string-to-char value))
 	  (if (member sign '(?- ?+))
-	      (setq current (org-duration-string-to-minutes current)
+	      (setq current (org-duration-to-minutes current)
 		    value (substring value 1))
 	    (setq current 0))
-	  (setq value (org-duration-string-to-minutes value))
+	  (setq value (org-duration-to-minutes value))
 	  (if (equal ?- sign)
 	      (setq value (- current value))
 	    (if (equal ?+ sign) (setq value (+ current value)))))
 	(setq value (max 0 value)
-	      org-clock-effort (org-minutes-to-clocksum-string value))
+	      org-clock-effort (org-duration-from-minutes value))
 	(org-entry-put org-clock-marker "Effort" org-clock-effort)
 	(org-clock-update-mode-line)
 	(message "Effort is now %s" org-clock-effort))
@@ -769,7 +768,7 @@ clocked item, and the value displayed in the mode line."
   "Show notification if we spent more time than we estimated before.
 Notification is shown only once."
   (when (org-clocking-p)
-    (let ((effort-in-minutes (org-duration-string-to-minutes org-clock-effort))
+    (let ((effort-in-minutes (org-duration-to-minutes org-clock-effort))
 	  (clocked-time (org-clock-get-clocked-time)))
       (if (setq org-clock-task-overrun
 		(if (or (null effort-in-minutes) (zerop effort-in-minutes))
@@ -1631,7 +1630,7 @@ to, overriding the existing value of `org-clock-out-switch-to-state'."
 		  (org-todo org-clock-out-switch-to-state))))))
 	  (force-mode-line-update)
 	  (message (concat "Clock stopped at %s after "
-			   (org-minutes-to-clocksum-string (+ (* 60 h) m)) "%s")
+			   (org-duration-from-minutes (+ (* 60 h) m)) "%s")
 		   te (if remove " => LINE REMOVED" ""))
 	  (run-hooks 'org-clock-out-hook)
 	  (unless (org-clocking-p)
@@ -1935,7 +1934,7 @@ Use `\\[org-clock-remove-overlays]' to remove the subtree times."
 			     (cond (todayp " for today")
 				   (customp " (custom)")
 				   (t "")))
-		     (org-minutes-to-clocksum-string
+		     (org-duration-from-minutes
 		      org-clock-file-total-minutes)
 		     " (%d hours and %d minutes)")
 	     h m)))
@@ -1960,7 +1959,7 @@ will be easy to remove."
 				    (length (org-get-at-bol 'line-prefix)))) ?·)
 			 '(face shadow))
 		     (org-add-props
-			 (format " %9s " (org-minutes-to-clocksum-string time))
+			 (format " %9s " (org-duration-from-minutes time))
 			 '(face org-clock-overlay))
 		     ""))
     (overlay-put ov 'display tx)
@@ -2463,8 +2462,6 @@ from the dynamic block definition."
 	 (maxlevel (or (plist-get params :maxlevel) 3))
 	 (emph (plist-get params :emphasize))
 	 (level-p (plist-get params :level))
-	 (org-time-clocksum-use-effort-durations
-	  (plist-get params :effort-durations))
 	 (timestamp (plist-get params :timestamp))
 	 (properties (plist-get params :properties))
 	 (ntcol (max 1 (or (plist-get params :tcolumns) 100)))
@@ -2558,7 +2555,7 @@ from the dynamic block definition."
      (make-string (length properties) ?|)  ; properties columns, maybe
      (concat (format org-clock-total-time-cell-format (nth 7 lwords))  "| ") ; instead of a headline
      (format org-clock-total-time-cell-format
-	     (org-minutes-to-clocksum-string (or total-time 0))) ;time
+	     (org-duration-from-minutes (or total-time 0))) ;time
      "|"
      (make-string (1- (min maxlevel (or ntcol 100))) ?|)
      (cond ((not (eq formula '%)) "")
@@ -2587,7 +2584,7 @@ from the dynamic block definition."
 		     (if level-p   "| " "") ; level column, maybe
 		     (if timestamp "| " "") ; timestamp column, maybe
 		     (if properties (make-string (length properties) ?|) "")  ;properties columns, maybe
-		     (org-minutes-to-clocksum-string (nth 1 tbl))))) ; the time
+		     (org-duration-from-minutes (nth 1 tbl))))) ; the time
 
 	  ;; Get the list of node entries and iterate over it
 	  (setq entries (nth 2 tbl))
@@ -2619,7 +2616,7 @@ from the dynamic block definition."
 	     (if indent (org-clocktable-indent-string level) "") ; indentation
 	     hlc headline hlc "|"                                ; headline
 	     (make-string (1- (min ntcol level)) ?|) ; empty fields for higher levels
-	     hlc (org-minutes-to-clocksum-string (nth 3 entry)) hlc ; time
+	     hlc (org-duration-from-minutes (nth 3 entry)) hlc ; time
 	     (make-string (1+ (- maxlevel level)) ?|)
 	     (if (eq formula '%)
 		 (format "%.1f |" (* 100 (/ (nth 3 entry) (float total-time))))

+ 34 - 60
lisp/org-colview.el

@@ -261,7 +261,7 @@ possible to override it with optional argument COMPILED-FMT."
 			     org-agenda-columns-add-appointments-to-effort-sum
 			     (string= p (upcase org-effort-property))
 			     (get-text-property (point) 'duration)
-			     (propertize (org-minutes-to-clocksum-string
+			     (propertize (org-duration-from-minutes
 					  (get-text-property (point) 'duration))
 					 'face 'org-warning))
 			"")))
@@ -1056,63 +1056,40 @@ This function updates `org-columns-current-fmt-compiled'."
 
 ;;;; Column View Summary
 
-(defconst org-columns--duration-re
-  (concat "[0-9.]+ *" (regexp-opt (mapcar #'car org-effort-durations)))
-  "Regexp matching a duration.")
-
-(defun org-columns--time-to-seconds (s)
-  "Turn time string S into a number of seconds.
-A time is expressed as HH:MM, HH:MM:SS, or with units defined in
-`org-effort-durations'.  Plain numbers are considered as hours."
-  (cond
-   ((string-match-p org-columns--duration-re s)
-    (* 60 (org-duration-string-to-minutes s)))
-   ((string-match "\\`\\([0-9]+\\):\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?\\'" s)
-    (+ (* 3600 (string-to-number (match-string 1 s)))
-       (* 60 (string-to-number (match-string 2 s)))
-       (if (match-end 3) (string-to-number (match-string 3 s)) 0)))
-   (t (* 3600 (string-to-number s)))))
-
-(defun org-columns--age-to-seconds (s)
-  "Turn age string S into a number of seconds.
+(defun org-columns--age-to-minutes (s)
+  "Turn age string S into a number of minutes.
 An age is either computed from a given time-stamp, or indicated
-as days/hours/minutes/seconds."
+as a canonical duration, i.e., using units defined in
+`org-duration-canonical-units'."
   (cond
    ((string-match-p org-ts-regexp s)
-    (floor
-     (- org-columns--time
-	(float-time (apply #'encode-time (org-parse-time-string s))))))
-   ;; Match own output for computations in upper levels.
-   ((string-match "\\([0-9]+\\)d \\([0-9]+\\)h \\([0-9]+\\)m \\([0-9]+\\)s" s)
-    (+ (* 86400 (string-to-number (match-string 1 s)))
-       (* 3600 (string-to-number (match-string 2 s)))
-       (* 60 (string-to-number (match-string 3 s)))
-       (string-to-number (match-string 4 s))))
+    (/ (- org-columns--time
+	  (float-time (apply #'encode-time (org-parse-time-string s))))
+       60))
+   ((org-duration-p s) (org-duration-to-minutes s t)) ;skip user units
    (t (user-error "Invalid age: %S" s))))
 
+(defun org-columns--format-age (minutes)
+  "Format MINUTES float as an age string."
+  (org-duration-from-minutes minutes
+			     '(("d" . nil) ("h" . nil) ("min" . nil))
+			     t))	;ignore user's custom units
+
 (defun org-columns--summary-apply-times (fun times)
   "Apply FUN to time values TIMES.
-If TIMES contains any time value expressed as a duration, return
-the result as a duration.  If it contains any H:M:S, use that
-format instead.  Otherwise, use H:M format."
-  (let* ((hms-flag nil)
-	 (duration-flag nil)
-	 (seconds
-	  (apply fun
-		 (mapcar
-		  (lambda (time)
-		    (cond
-		     (duration-flag)
-		     ((string-match-p org-columns--duration-re time)
-		      (setq duration-flag t))
-		     (hms-flag)
-		     ((string-match-p "\\`[0-9]+:[0-9]+:[0-9]+\\'" time)
-		      (setq hms-flag t)))
-		    (org-columns--time-to-seconds time))
-		  times))))
-    (cond (duration-flag (org-minutes-to-clocksum-string (/ seconds 60.0)))
-	  (hms-flag (format-seconds "%h:%.2m:%.2s" seconds))
-	  (t (format-seconds "%h:%.2m" seconds)))))
+Return the result as a duration."
+  (org-duration-from-minutes
+   (apply fun
+	  (mapcar (lambda (time)
+		    ;; Unlike to `org-duration-to-minutes' standard
+		    ;; behavior, we want to consider plain numbers as
+		    ;; hours.  As a consequence, we treat them
+		    ;; differently.
+		    (if (string-match-p "\\`[0-9]+\\(?:\\.[0-9]*\\)?\\'" time)
+			(* 60 (string-to-number time))
+		      (org-duration-to-minutes time)))
+		  times))
+   (org-duration-h:mm-only-p times)))
 
 (defun org-columns--compute-spec (spec &optional update)
   "Update tree according to SPEC.
@@ -1271,21 +1248,18 @@ When PRINTF is non-nil, use it to format the result."
 
 (defun org-columns--summary-min-age (ages _)
   "Compute the minimum time among AGES."
-  (format-seconds
-   "%dd %.2hh %mm %ss"
-   (apply #'min (mapcar #'org-columns--age-to-seconds ages))))
+  (org-columns--format-age
+   (apply #'min (mapcar #'org-columns--age-to-minutes ages))))
 
 (defun org-columns--summary-max-age (ages _)
   "Compute the maximum time among AGES."
-  (format-seconds
-   "%dd %.2hh %mm %ss"
-   (apply #'max (mapcar #'org-columns--age-to-seconds ages))))
+  (org-columns--format-age
+   (apply #'max (mapcar #'org-columns--age-to-minutes ages))))
 
 (defun org-columns--summary-mean-age (ages _)
   "Compute the minimum time among AGES."
-  (format-seconds
-   "%dd %.2hh %mm %ss"
-   (/ (apply #'+ (mapcar #'org-columns--age-to-seconds ages))
+  (org-columns--format-age
+   (/ (apply #'+ (mapcar #'org-columns--age-to-minutes ages))
       (float (length ages)))))
 
 (defun org-columns--summary-estimate (estimates printf)

+ 22 - 0
lisp/org-compat.el

@@ -341,6 +341,28 @@ use of this function is for the stuck project list."
       (setq skip (re-search-forward org-agenda-skip-regexp end t)))
     (and skip entry-end)))
 
+(define-obsolete-function-alias 'org-minutes-to-clocksum-string
+  'org-duration-from-minutes "Org 9.1")
+
+(define-obsolete-function-alias 'org-hh:mm-string-to-minutes
+  'org-duration-to-minutes "Org 9.1")
+
+(define-obsolete-function-alias 'org-duration-string-to-minutes
+  'org-duration-to-minutes "Org 9.1")
+
+(define-obsolete-variable-alias 'org-time-clocksum-format
+  'org-duration-format "Org 9.1")
+
+(define-obsolete-variable-alias 'org-time-clocksum-use-fractional
+  'org-duration-format "Org 9.1")
+
+(define-obsolete-variable-alias 'org-time-clocksum-fractional-format
+  'org-duration-format "Org 9.1")
+
+(define-obsolete-variable-alias 'org-time-clocksum-use-effort-durations
+  'org-duration-units "Org 9.1")
+
+
 ;;;; Obsolete link types
 
 (eval-after-load 'org

+ 1 - 1
lisp/org-table.el

@@ -1720,7 +1720,7 @@ numeric compare based on the type of the first key in the table."
 			  (float-time
 			   (org-time-string-to-time (match-string 0 f))))
 			 ((string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" f)
-			  (org-hh:mm-string-to-minutes f))
+			  (org-duration-to-minutes (match-string 0 f)))
 			 (t 0))))
 		((?f ?F)
 		 (or getkey-func

+ 6 - 263
lisp/org.el

@@ -3250,135 +3250,6 @@ commands, if custom time display is turned on at the time of export."
 	(concat "[" (substring f 1 -1) "]")
       f)))
 
-(defcustom org-time-clocksum-format
-  '(:days "%dd " :hours "%d" :require-hours t :minutes ":%02d" :require-minutes t)
-  "The format string used when creating CLOCKSUM lines.
-This is also used when Org mode generates a time duration.
-
-The value can be a single format string containing two
-%-sequences, which will be filled with the number of hours and
-minutes in that order.
-
-Alternatively, the value can be a plist associating any of the
-keys :years, :months, :weeks, :days, :hours or :minutes with
-format strings.  The time duration is formatted using only the
-time components that are needed and concatenating the results.
-If a time unit in absent, it falls back to the next smallest
-unit.
-
-The keys :require-years, :require-months, :require-days,
-:require-weeks, :require-hours, :require-minutes are also
-meaningful.  A non-nil value for these keys indicates that the
-corresponding time component should always be included, even if
-its value is 0.
-
-
-For example,
-
-   (:days \"%dd\" :hours \"%d\" :require-hours t :minutes \":%02d\"
-    :require-minutes t)
-
-means durations longer than a day will be expressed in days,
-hours and minutes, and durations less than a day will always be
-expressed in hours and minutes (even for durations less than an
-hour).
-
-The value
-
-  (:days \"%dd\" :minutes \"%dm\")
-
-means durations longer than a day will be expressed in days and
-minutes, and durations less than a day will be expressed entirely
-in minutes (even for durations longer than an hour)."
-  :group 'org-time
-  :group 'org-clock
-  :version "24.4"
-  :package-version '(Org . "8.0")
-  :type '(choice (string :tag "Format string")
-		 (set :tag "Plist"
-		      (group :inline t (const :tag "Years" :years)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show years" :require-years)
-			     (const t))
-		      (group :inline t (const :tag "Months" :months)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show months" :require-months)
-			     (const t))
-		      (group :inline t (const :tag "Weeks" :weeks)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show weeks" :require-weeks)
-			     (const t))
-		      (group :inline t (const :tag "Days" :days)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show days" :require-days)
-			     (const t))
-		      (group :inline t (const :tag "Hours" :hours)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show hours" :require-hours)
-			     (const t))
-		      (group :inline t (const :tag "Minutes" :minutes)
-			     (string :tag "Format string"))
-		      (group :inline t
-			     (const :tag "Always show minutes" :require-minutes)
-			     (const t)))))
-
-(defcustom org-time-clocksum-use-fractional nil
-  "When non-nil, `\\[org-clock-display]' uses fractional times.
-See `org-time-clocksum-format' for more on time clock formats."
-  :group 'org-time
-  :group 'org-clock
-  :version "24.3"
-  :type 'boolean)
-
-(defcustom org-time-clocksum-use-effort-durations nil
-  "When non-nil, `\\[org-clock-display]' uses effort durations.
-E.g. by default, one day is considered to be a 8 hours effort,
-so a task that has been clocked for 16 hours will be displayed
-as during 2 days in the clock display or in the clocktable.
-
-See `org-effort-durations' on how to set effort durations
-and `org-time-clocksum-format' for more on time clock formats."
-  :group 'org-time
-  :group 'org-clock
-  :version "24.4"
-  :package-version '(Org . "8.0")
-  :type 'boolean)
-
-(defcustom org-time-clocksum-fractional-format "%.2f"
-  "The format string used when creating CLOCKSUM lines,
-or when Org mode generates a time duration, if
-`org-time-clocksum-use-fractional' is enabled.
-
-The value can be a single format string containing one
-%-sequence, which will be filled with the number of hours as
-a float.
-
-Alternatively, the value can be a plist associating any of the
-keys :years, :months, :weeks, :days, :hours or :minutes with
-a format string.  The time duration is formatted using the
-largest time unit which gives a non-zero integer part.  If all
-specified formats have zero integer part, the smallest time unit
-is used."
-  :group 'org-time
-  :type '(choice (string :tag "Format string")
-		 (set (group :inline t (const :tag "Years" :years)
-			     (string :tag "Format string"))
-		      (group :inline t (const :tag "Months" :months)
-			     (string :tag "Format string"))
-		      (group :inline t (const :tag "Weeks" :weeks)
-			     (string :tag "Format string"))
-		      (group :inline t (const :tag "Days" :days)
-			     (string :tag "Format string"))
-		      (group :inline t (const :tag "Hours" :hours)
-			     (string :tag "Format string"))
-		      (group :inline t (const :tag "Minutes" :minutes)
-			     (string :tag "Format string")))))
-
 (defcustom org-deadline-warning-days 14
   "Number of days before expiration during which a deadline becomes active.
 This variable governs the display in sparse trees and in the agenda.
@@ -3789,7 +3660,7 @@ and the clock summary:
 
  ((\"Remaining\" (lambda(value)
                    (let ((clocksum (org-clock-sum-current-item))
-                         (effort (org-duration-string-to-minutes
+                         (effort (org-duration-to-minutes
                                    (org-entry-get (point) \"Effort\"))))
                      (org-minutes-to-clocksum-string (- effort clocksum))))))"
   :group 'org-properties
@@ -9744,7 +9615,7 @@ sub-tree if optional argument INHERIT is non-nil."
   (org-refresh-properties
    org-effort-property
    '((effort . identity)
-     (effort-minutes . org-duration-string-to-minutes))))
+     (effort-minutes . org-duration-to-minutes))))
 
 ;;;; Link Stuff
 
@@ -15725,7 +15596,7 @@ When INCREMENT is non-nil, set the property to the next allowed value."
       (org-entry-put nil prop val))
     (org-refresh-property
      '((effort . identity)
-       (effort-minutes . org-duration-string-to-minutes))
+       (effort-minutes . org-duration-to-minutes))
      val)
     (when (equal heading (bound-and-true-p org-clock-current-task))
       (setq org-clock-effort (get-text-property (point-at-bol) 'effort))
@@ -15763,8 +15634,7 @@ strings."
 	    (when (or (not specific) (string= specific "CLOCKSUM"))
 	      (let ((clocksum (get-text-property (point) :org-clock-minutes)))
 		(when clocksum
-		  (push (cons "CLOCKSUM"
-			      (org-minutes-to-clocksum-string clocksum))
+		  (push (cons "CLOCKSUM" (org-duration-from-minutes clocksum))
 			props)))
 	      (when specific (throw 'exit props)))
 	    (when (or (not specific) (string= specific "CLOCKSUM_T"))
@@ -15772,7 +15642,7 @@ strings."
 						  :org-clock-minutes-today)))
 		(when clocksumt
 		  (push (cons "CLOCKSUM_T"
-			      (org-minutes-to-clocksum-string clocksumt))
+			      (org-duration-from-minutes clocksumt))
 			props)))
 	      (when specific (throw 'exit props)))
 	    (when (or (not specific) (string= specific "ITEM"))
@@ -16607,7 +16477,7 @@ completion."
     (when (equal prop org-effort-property)
       (org-refresh-property
        '((effort . identity)
-	 (effort-minutes . org-duration-string-to-minutes))
+	 (effort-minutes . org-duration-to-minutes))
        nval)
       (when (string= org-clock-current-task heading)
 	(setq org-clock-effort nval)
@@ -18316,113 +18186,6 @@ effort string \"2hours\" is equivalent to 120 minutes."
   :type '(alist :key-type (string :tag "Modifier")
 		:value-type (number :tag "Minutes")))
 
-(defun org-minutes-to-clocksum-string (m)
-  "Format number of minutes as a clocksum string.
-The format is determined by `org-time-clocksum-format',
-`org-time-clocksum-use-fractional' and
-`org-time-clocksum-fractional-format' and
-`org-time-clocksum-use-effort-durations'."
-  (let ((clocksum "")
-	(m (round m)) ; Don't allow fractions of minutes
-	h d w mo y fmt n)
-    (setq h (if org-time-clocksum-use-effort-durations
-		(cdr (assoc "h" org-effort-durations)) 60)
-	  d (if org-time-clocksum-use-effort-durations
-		(/ (cdr (assoc "d" org-effort-durations)) h) 24)
-	  w (if org-time-clocksum-use-effort-durations
-		(/ (cdr (assoc "w" org-effort-durations)) (* d h)) 7)
-	  mo (if org-time-clocksum-use-effort-durations
-		 (/ (cdr (assoc "m" org-effort-durations)) (* d h)) 30)
-	  y (if org-time-clocksum-use-effort-durations
-		(/ (cdr (assoc "y" org-effort-durations)) (* d h)) 365))
-    ;; fractional format
-    (if org-time-clocksum-use-fractional
-	(cond
-	 ;; single format string
-	 ((stringp org-time-clocksum-fractional-format)
-	  (format org-time-clocksum-fractional-format (/ m (float h))))
-	 ;; choice of fractional formats for different time units
-	 ((and (setq fmt (plist-get org-time-clocksum-fractional-format :years))
-	       (> (/ (truncate m) (* y d h)) 0))
-	  (format fmt (/ m (* y d (float h)))))
-	 ((and (setq fmt (plist-get org-time-clocksum-fractional-format :months))
-	       (> (/ (truncate m) (* mo d h)) 0))
-	  (format fmt (/ m (* mo d (float h)))))
-	 ((and (setq fmt (plist-get org-time-clocksum-fractional-format :weeks))
-	       (> (/ (truncate m) (* w d h)) 0))
-	  (format fmt (/ m (* w d (float h)))))
-	 ((and (setq fmt (plist-get org-time-clocksum-fractional-format :days))
-	       (> (/ (truncate m) (* d h)) 0))
-	  (format fmt (/ m (* d (float h)))))
-	 ((and (setq fmt (plist-get org-time-clocksum-fractional-format :hours))
-	       (> (/ (truncate m) h) 0))
-	  (format fmt (/ m (float h))))
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :minutes))
-	  (format fmt m))
-	 ;; fall back to smallest time unit with a format
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :hours))
-	  (format fmt (/ m (float h))))
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :days))
-	  (format fmt (/ m (* d (float h)))))
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :weeks))
-	  (format fmt (/ m (* w d (float h)))))
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :months))
-	  (format fmt (/ m (* mo d (float h)))))
-	 ((setq fmt (plist-get org-time-clocksum-fractional-format :years))
-	  (format fmt (/ m (* y d (float h))))))
-      ;; standard (non-fractional) format, with single format string
-      (if (stringp org-time-clocksum-format)
-	  (format org-time-clocksum-format (setq n (/ m h)) (- m (* h n)))
-	;; separate formats components
-	(and (setq fmt (plist-get org-time-clocksum-format :years))
-	     (or (> (setq n (/ (truncate m) (* y d h))) 0)
-		 (plist-get org-time-clocksum-format :require-years))
-	     (setq clocksum (concat clocksum (format fmt n))
-		   m (- m (* n y d h))))
-	(and (setq fmt (plist-get org-time-clocksum-format :months))
-	     (or (> (setq n (/ (truncate m) (* mo d h))) 0)
-		 (plist-get org-time-clocksum-format :require-months))
-	     (setq clocksum (concat clocksum (format fmt n))
-		   m (- m (* n mo d h))))
-	(and (setq fmt (plist-get org-time-clocksum-format :weeks))
-	     (or (> (setq n (/ (truncate m) (* w d h))) 0)
-		 (plist-get org-time-clocksum-format :require-weeks))
-	     (setq clocksum (concat clocksum (format fmt n))
-		   m (- m (* n w d h))))
-	(and (setq fmt (plist-get org-time-clocksum-format :days))
-	     (or (> (setq n (/ (truncate m) (* d h))) 0)
-		 (plist-get org-time-clocksum-format :require-days))
-	     (setq clocksum (concat clocksum (format fmt n))
-		   m (- m (* n d h))))
-	(and (setq fmt (plist-get org-time-clocksum-format :hours))
-	     (or (> (setq n (/ (truncate m) h)) 0)
-		 (plist-get org-time-clocksum-format :require-hours))
-	     (setq clocksum (concat clocksum (format fmt n))
-		   m (- m (* n h))))
-	(and (setq fmt (plist-get org-time-clocksum-format :minutes))
-	     (or (> m 0) (plist-get org-time-clocksum-format :require-minutes))
-	     (setq clocksum (concat clocksum (format fmt m))))
-	;; return formatted time duration
-	clocksum))))
-
-(defun org-hours-to-clocksum-string (n)
-  (org-minutes-to-clocksum-string (* n 60)))
-
-(defun org-hh:mm-string-to-minutes (s)
-  "Convert a string H:MM to a number of minutes.
-If the string is just a number, interpret it as minutes.
-In fact, the first hh:mm or number in the string will be taken,
-there can be extra stuff in the string.
-If no number is found, the return value is 0."
-  (cond
-   ((integerp s) s)
-   ((string-match "\\([0-9]+\\):\\([0-9]+\\)" s)
-    (+ (* (string-to-number (match-string 1 s)) 60)
-       (string-to-number (match-string 2 s))))
-   ((string-match "\\([0-9]+\\)" s)
-    (string-to-number (match-string 1 s)))
-   (t 0)))
-
 (defcustom org-image-actual-width t
   "Should we use the actual width of images when inlining them?
 
@@ -18477,26 +18240,6 @@ The value is a list, with zero or more of the symbols `effort', `appt',
   :package-version '(Org . "8.3")
   :group 'org-agenda)
 
-(defun org-duration-string-to-minutes (s &optional output-to-string)
-  "Convert a duration string S to minutes.
-
-A bare number is interpreted as minutes, modifiers can be set by
-customizing `org-effort-durations' (which see).
-
-Entries containing a colon are interpreted as H:MM by
-`org-hh:mm-string-to-minutes'."
-  (let ((result 0)
-	(re (concat "\\([0-9.]+\\) *\\("
-		    (regexp-opt (mapcar 'car org-effort-durations))
-		    "\\)")))
-    (while (string-match re s)
-      (cl-incf result (* (cdr (assoc (match-string 2 s) org-effort-durations))
-			 (string-to-number (match-string 1 s))))
-      (setq s (replace-match "" nil t s)))
-    (setq result (floor result))
-    (cl-incf result (org-hh:mm-string-to-minutes s))
-    (if output-to-string (number-to-string result) result)))
-
 ;;;; Files
 
 (defun org-save-all-org-buffers ()

+ 1 - 4
testing/lisp/test-org-clock.el

@@ -67,10 +67,7 @@ contents.  The clocktable doesn't appear in the buffer."
     (insert "#+END:\n"))
   (unwind-protect
       (save-excursion
-	(let ((org-time-clocksum-format
-	       '(:hours "%d" :require-hours t :minutes ":%02d"
-			:require-minutes t)))
-	  (org-update-dblock))
+	(let ((org-duration-format 'h:mm)) (org-update-dblock))
 	(forward-line)
 	;; Skip caption.
 	(when (looking-at "#\\+CAPTION:") (forward-line))

+ 13 - 16
testing/lisp/test-org-colview.el

@@ -415,7 +415,7 @@
   ;; {@min}, {@max} and {@mean} apply to ages.
   (should
    (equal
-    "0d 00h 0m 0s"
+    "0min"
     (cl-letf (((symbol-function 'current-time)
 	       (lambda ()
 		 (apply #'encode-time
@@ -434,7 +434,7 @@
 	(get-char-property (point) 'org-columns-value-modified)))))
   (should
    (equal
-    "705d 01h 0m 0s"
+    "705d 1h"
     (cl-letf (((symbol-function 'current-time)
 	       (lambda ()
 		 (apply #'encode-time
@@ -453,7 +453,7 @@
 	(get-char-property (point) 'org-columns-value-modified)))))
   (should
    (equal
-    "352d 12h 30m 0s"
+    "352d 12h 30min"
     (cl-letf (((symbol-function 'current-time)
 	       (lambda ()
 		 (apply #'encode-time
@@ -475,7 +475,7 @@
   ;; combinations of duration and H:MM:SS patterns.
   (should
    (equal
-    "1d 4:20"
+    "3d 4:20"
     (org-test-with-temp-text
 	"* H
 ** S1
@@ -487,9 +487,8 @@
 :A: 1:20
 :END:"
       (let ((org-columns-default-format "%A{:}")
-	    (org-time-clocksum-use-fractional nil)
-	    (org-time-clocksum-format
-	     '(:days "%dd " :hours "%d" :minutes ":%02d")))
+	    (org-duration-units '(("d" . 1440) ("h" . 60)))
+	    (org-duration-format '(("d" . nil) (special . h:mm))))
 	(org-columns))
       (get-char-property (point) 'org-columns-value-modified))))
   (should
@@ -509,7 +508,7 @@
       (get-char-property (point) 'org-columns-value-modified))))
   (should
    (equal
-    "1d 4:20"
+    "3d 4:20"
     (org-test-with-temp-text
 	"* H
 ** S1
@@ -521,25 +520,23 @@
 :A: 0d 1:20
 :END:"
       (let ((org-columns-default-format "%A{:}")
-	    (org-time-clocksum-use-fractional nil)
-	    (org-time-clocksum-format
-	     '(:days "%dd " :hours "%d" :minutes ":%02d")))
+	    (org-duration-units '(("d" . 1440) ("h" . 60)))
+	    (org-duration-format '(("d" . nil) (special . h:mm))))
 	(org-columns))
       (get-char-property (point) 'org-columns-value-modified))))
-  ;; @min, @max and @mean also accept regular duration in
-  ;; a "?d ?h ?m ?s" format.
+  ;; @min, @max and @mean also accept regular duration.
   (should
    (equal
-    "1d 10h 0m 0s"
+    "1d 10h"
     (org-test-with-temp-text
 	"* H
 ** S1
 :PROPERTIES:
-:A: 1d 10h 0m 0s
+:A: 1d 10h 0min
 :END:
 ** S1
 :PROPERTIES:
-:A: 5d 3h 0m 0s
+:A: 5d 3h
 :END:"
       (let ((org-columns-default-format "%A{@min}")) (org-columns))
       (get-char-property (point) 'org-columns-value-modified))))