From 7e8cf5f4c202f51231d444f41735a4db06cb7052 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Wed, 8 Feb 2017 22:24:32 +0100 Subject: [PATCH] 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. --- contrib/lisp/org-depend.el | 2 +- contrib/lisp/org-invoice.el | 10 +- contrib/lisp/ox-taskjuggler.el | 2 +- etc/ORG-NEWS | 37 ++++- lisp/org-agenda.el | 38 +++-- lisp/org-clock.el | 31 ++-- lisp/org-colview.el | 94 ++++------- lisp/org-compat.el | 22 +++ lisp/org-table.el | 2 +- lisp/org.el | 269 +------------------------------ testing/lisp/test-org-clock.el | 5 +- testing/lisp/test-org-colview.el | 29 ++-- 12 files changed, 150 insertions(+), 391 deletions(-) diff --git a/contrib/lisp/org-depend.el b/contrib/lisp/org-depend.el index b6f3d2702..cd570c553 100644 --- a/contrib/lisp/org-depend.el +++ b/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) diff --git a/contrib/lisp/org-invoice.el b/contrib/lisp/org-invoice.el index 4c83d2e0a..686889411 100644 --- a/contrib/lisp/org-invoice.el +++ b/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"))))) diff --git a/contrib/lisp/ox-taskjuggler.el b/contrib/lisp/ox-taskjuggler.el index d856878e9..2144b041c 100644 --- a/contrib/lisp/ox-taskjuggler.el +++ b/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)) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index ad70cc1a0..4068f413a 100644 --- a/etc/ORG-NEWS +++ b/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. diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index c81221b2f..4f18f4b34 100644 --- a/lisp/org-agenda.el +++ b/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. diff --git a/lisp/org-clock.el b/lisp/org-clock.el index 26f360c6f..35dd8a0c3 100644 --- a/lisp/org-clock.el +++ b/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)))) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 6a75f70d3..1d068d32a 100644 --- a/lisp/org-colview.el +++ b/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) diff --git a/lisp/org-compat.el b/lisp/org-compat.el index 6c6c2562d..7e3fee7fd 100644 --- a/lisp/org-compat.el +++ b/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 diff --git a/lisp/org-table.el b/lisp/org-table.el index b7a49f304..6a0bf48d1 100644 --- a/lisp/org-table.el +++ b/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 diff --git a/lisp/org.el b/lisp/org.el index 8155122a3..61dbad6c6 100644 --- a/lisp/org.el +++ b/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 () diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el index e0f23ead0..60b32ebf8 100644 --- a/testing/lisp/test-org-clock.el +++ b/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)) diff --git a/testing/lisp/test-org-colview.el b/testing/lisp/test-org-colview.el index 8b312ab12..1e42c1221 100644 --- a/testing/lisp/test-org-colview.el +++ b/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))))