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.
This commit is contained in:
Nicolas Goaziou 2017-02-08 22:24:32 +01:00
parent 1d39286183
commit 7e8cf5f4c2
12 changed files with 150 additions and 391 deletions

View File

@ -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)

View File

@ -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")))))

View File

@ -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))

View File

@ -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.

View File

@ -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.

View File

@ -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))))

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 ()

View File

@ -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))

View File

@ -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))))