From 617f70bc7d6402872faa7621ce20fd3a573518b8 Mon Sep 17 00:00:00 2001 From: Lars Ingebrigtsen Date: Fri, 30 Oct 2020 12:10:17 +0100 Subject: [PATCH] `format-time' can now do sub-second times * doc/lispref/os.texi (Time Parsing): Document it. * lisp/calendar/time-date.el (format-seconds): Allow formatting sub-second times. --- doc/lispref/os.texi | 5 +++- etc/NEWS | 5 ++++ lisp/calendar/time-date.el | 40 +++++++++++++++++++++------ test/lisp/calendar/time-date-tests.el | 9 +++++- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 504f0dfb23e..2c30d8ad892 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1901,7 +1901,10 @@ The integer number of hours. The integer number of minutes. @item %s @itemx %S -The integer number of seconds. +The number of seconds. If the optional @samp{,} parameter is used, +it's a floating point number, and the number after the @samp{,} +specifies how many decimals to be used. @samp{%,2s} means ``use two +decimals''. @item %z Non-printing control flag. When it is used, other specifiers must be given in the order of decreasing size, i.e., years before days, hours diff --git a/etc/NEWS b/etc/NEWS index 4b1272d18bb..8504186208c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1204,6 +1204,11 @@ list. ** Miscellaneous ++++ +*** 'format-seconds' can now be used for sub-second times. +The new optional "," parameter has been added, and +(format-seconds "%mm %,1ss" 66.4) will now result in "1m 6.4s". + --- *** 'global-display-fill-column-indicator-mode' skips some buffers. By default, turning on 'global-display-fill-column-indicator-mode' diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el index f60c9c2ffd3..cf6c20afbd2 100644 --- a/lisp/calendar/time-date.el +++ b/lisp/calendar/time-date.el @@ -278,6 +278,10 @@ Lower-case specifiers return only the unit. optional leading \".\" for zero-padding. For example, \"%.3Y\" will return something of the form \"001 year\". +The \"%s\" spec takes an additional optional parameter, +introduced by the \",\" character, to say how many decimals to +use. \"%,1s\" means \"use one decimal\". + The \"%z\" specifier does not print anything. When it is used, specifiers must be given in order of decreasing size. To the left of \"%z\", nothing is output until the first non-zero unit is encountered." @@ -289,10 +293,11 @@ is output until the first non-zero unit is encountered." ("s" "second" 1) ("z"))) (case-fold-search t) - spec match usedunits zeroflag larger prev name unit num zeropos) - (while (string-match "%\\.?[0-9]*\\(.\\)" string start) + spec match usedunits zeroflag larger prev name unit num zeropos + fraction) + (while (string-match "%\\.?[0-9]*\\(,[0-9]\\)?\\(.\\)" string start) (setq start (match-end 0) - spec (match-string 1 string)) + spec (match-string 2 string)) (unless (string-equal spec "%") (or (setq match (assoc (downcase spec) units)) (error "Bad format specifier: `%s'" spec)) @@ -307,12 +312,17 @@ is output until the first non-zero unit is encountered." (push match usedunits))) (and zeroflag larger (error "Units are not in decreasing order of size")) - (setq seconds (time-convert seconds 'integer)) + (unless (numberp seconds) + (setq seconds (float-time seconds))) + (setq fraction (mod seconds 1) + seconds (round seconds)) (dolist (u units) (setq spec (car u) name (cadr u) unit (nth 2 u)) - (when (string-match (format "%%\\(\\.?[0-9]+\\)?\\(%s\\)" spec) string) + (when (string-match + (format "%%\\(\\.?[0-9]+\\)?\\(,[0-9]+\\)?\\(%s\\)" spec) + string) (if (string-equal spec "z") ; must be last in units (setq string (replace-regexp-in-string @@ -327,9 +337,23 @@ is output until the first non-zero unit is encountered." (setq zeropos (unless (zerop num) (match-beginning 0)))) (setq string (replace-match - (format (concat "%" (match-string 1 string) "d%s") num - (if (string-equal (match-string 2 string) spec) - "" ; lower-case, no unit-name + (format (if (match-string 2 string) + (concat + "%" + (and (match-string 1 string) + (if (= (elt (match-string 1 string) 0) ?.) + (concat "0" (substring + (match-string 1 string) 1)) + (match-string 1 string))) + (concat "." (substring + (match-string 2 string) 1)) + "f%s") + (concat "%" (match-string 1 string) "d%s")) + (if (= unit 1) + (+ num fraction) + num) + (if (string-equal (match-string 3 string) spec) + "" ; lower-case, no unit-name (format " %s%s" name (if (= num 1) "" "s")))) t t string)))))) diff --git a/test/lisp/calendar/time-date-tests.el b/test/lisp/calendar/time-date-tests.el index 3f8954a8ff9..76a5641f34d 100644 --- a/test/lisp/calendar/time-date-tests.el +++ b/test/lisp/calendar/time-date-tests.el @@ -81,7 +81,14 @@ (ert-deftest test-format-seconds () (should (equal (format-seconds "%y %d %h %m %s %%" 0) "0 0 0 0 0 %")) (should (equal (format-seconds "%y %d %h %m %s %%" 9999999) "0 115 17 46 39 %")) - (should (equal (format-seconds "%y %d %h %m %z %s %%" 1) " 1 %"))) + (should (equal (format-seconds "%y %d %h %m %z %s %%" 1) " 1 %")) + (should (equal (format-seconds "%mm %ss" 66) "1m 6s")) + (should (equal (format-seconds "%mm %5ss" 66) "1m 6s")) + (should (equal (format-seconds "%mm %.5ss" 66.4) "1m 00006s")) + + (should (equal (format-seconds "%mm %,1ss" 66.4) "1m 6.4s")) + (should (equal (format-seconds "%mm %5,1ss" 66.4) "1m 6.4s")) + (should (equal (format-seconds "%mm %.5,1ss" 66.4) "1m 006.4s"))) (ert-deftest test-ordinal () (should (equal (date-ordinal-to-time 2008 271) -- 2.39.2