From: JD Smith <jdtsmith@gmail.com>
To: john muhl <jm@pub.pink>
Cc: Adam Porter <adam@alphapapa.net>,
71572@debbugs.gnu.org, Eli Zaretskii <eliz@gnu.org>,
Jonas Bernoulli <jonas@bernoul.li>,
Paul Eggert <eggert@cs.ucla.edu>
Subject: bug#71572: [PATCH] seconds-to-string-approximate
Date: Sun, 15 Dec 2024 13:07:52 -0500 [thread overview]
Message-ID: <1CCC10A3-F76D-477C-9E04-AA0903853EAC@gmail.com> (raw)
In-Reply-To: <871pyhu6t4.fsf@pub.pink>
[-- Attachment #1: Type: text/plain, Size: 6221 bytes --]
>> On Dec 8, 2024, at 4:17 AM, john muhl <jm@pub.pink> wrote:
>>
>> Thanks for working on this. I gave a quick try and noticed some
>> amounts aren’t pluralized how I expect; e.g.
>>
>> (seconds-to-string 36541462 'expanded nil 1)
>> ;; "1 year 1.9 month"
Thanks again. This was another small bug in the units for the first expanded term. Corrected (final?) patch attached. Could you please give another quick test?
For posterity I also include below a more complete test script and its output (see new column s2s-e1).
++
Delay (s) s2s s2s-r s2s-ra s2s-ra1 s2s-rah s2s-e s2s-e1 s2s-ea s2s-ea1 s2s-ea3 s2s-eah
0.000 0s 0 seconds 0s 0s 0s 0 seconds 0 seconds 0s 0s 0s 0s
0.450 450.00ms 0 seconds 0s 0.4s 0.5s 0 seconds 0.4 seconds 0s 0.4s 0.450s 0.5s
0.855 855.00ms 1 second 1s 0.9s 1s 1 second 0.9 seconds 1s 0.9s 0.855s 1s
1.624 1.62s 2 seconds 2s 1.6s 1.5s 2 seconds 1.6 seconds 2s 1.6s 1.624s 1.5s
3.087 3.09s 3 seconds 3s 3.1s 3s 3 seconds 3.1 seconds 3s 3.1s 3.087s 3s
5.864 5.86s 6 seconds 6s 5.9s 6s 6 seconds 5.9 seconds 6s 5.9s 5.864s 6s
11.142 11.14s 11 seconds 11s 11.1s 11s 11 seconds 11.1 seconds 11s 11.1s 11.142s 11s
21.171 21.17s 21 seconds 21s 21.2s 21s 21 seconds 21.2 seconds 21s 21.2s 21.171s 21s
40.224 40.22s 40 seconds 40s 40.2s 40s 40 seconds 40.2 seconds 40s 40.2s 40.224s 40s
76.426 76.43s 1 minute 1m 1.3m 1.5m 1 minute 16 seconds 1 minute 16.4 seconds 1m 16s 1m 16.4s 1m 16.426s 1m 16.5s
145.209 2.42m 2 minutes 2m 2.4m 2.5m 2 minutes 25 seconds 2 minutes 25.2 seconds 2m 25s 2m 25.2s 2m 25.209s 2m 25s
275.898 4.60m 5 minutes 5m 4.6m 4.5m 4 minutes 36 seconds 4 minutes 35.9 seconds 4m 36s 4m 35.9s 4m 35.898s 4m 36s
524.206 8.74m 9 minutes 9m 8.7m 8.5m 8 minutes 44 seconds 8 minutes 44.2 seconds 8m 44s 8m 44.2s 8m 44.206s 8m 44s
995.992 16.60m 17 minutes 17m 16.6m 16.5m 16 minutes 36 seconds 16 minutes 36 seconds 16m 36s 16m 36s 16m 35.992s 16m 36s
1892.384 31.54m 32 minutes 32m 31.5m 31.5m 31 minutes 32 seconds 31 minutes 32.4 seconds 31m 32s 31m 32.4s 31m 32.384s 31m 32.5s
3595.530 59.93m 60 minutes 60m 59.9m 60m 59 minutes 56 seconds 59 minutes 55.5 seconds 59m 56s 59m 55.5s 59m 55.530s 59m 55.5s
6831.507 1.90h 2 hours 2h 1.9h 2h 1 hour 54 minutes 1 hour 53.9 minutes 1h 54m 1h 53.9m 1h 53.858m 1h 54m
12979.864 3.61h 4 hours 4h 3.6h 3.5h 3 hours 36 minutes 3 hours 36.3 minutes 3h 36m 3h 36.3m 3h 36.331m 3h 36.5m
24661.741 6.85h 7 hours 7h 6.9h 7h 6 hours 51 minutes 6 hours 51 minutes 6h 51m 6h 51m 6h 51.029m 6h 51m
46857.308 13.02h 13 hours 13h 13h 13h 13 hours 1 minute 13 hours 1 minute 13h 1m 13h 1m 13h 0.955m 13h 1m
89028.885 24.73h 1 day 1d 1d 1d 1 day 1 hour 1 day 0.7 hours 1d 1h 1d 0.7h 1d 0.730h 1d 0.5h
169154.881 1.96d 2 days 2d 2d 2d 1 day 23 hours 1 day 23 hours 1d 23h 1d 23h 1d 22.987h 1d 23h
321394.273 3.72d 4 days 4d 3.7d 3.5d 3 days 17 hours 3 days 17.3 hours 3d 17h 3d 17.3h 3d 17.276h 3d 17.5h
610649.119 7.07d 1 week 1w 1w 1w 1 week 1 week 0.1 days 1w 1w 0.1d 1w 0.068d 1w
1160233.326 13.43d 2 weeks 2w 1.9w 2w 1 week 6 days 1 week 6.4 days 1w 6d 1w 6.4d 1w 6.429d 1w 6.5d
2204443.319 25.51d 4 weeks 4w 3.6w 3.5w 3 weeks 5 days 3 weeks 4.5 days 3w 5d 3w 4.5d 3w 4.514d 3w 4.5d
4188442.306 48.48d 2 months 2M 1.6M 1.5M 1 month 3 weeks 1 month 2.6 weeks 1M 3w 1M 2.6w 1M 2.577w 1M 2.5w
7958040.381 92.11d 3 months 3M 3M 3M 3 months 3 months 0.1 weeks 3M 3M 0.1w 3M 0.114w 3M
15120276.725 175.00d 6 months 6M 5.7M 5.5M 5 months 3 weeks 5 months 3.3 weeks 5M 3w 5M 3.3w 5M 3.260w 5M 3.5w
28728525.777 332.51d 11 months 11M 10.9M 11M 10 months 4 weeks 10 months 4 weeks 10M 4w 10M 4w 10M 4.020w 10M 4w
54584198.976 1.73y 2 years 2Y 1.7Y 1.5Y 1 year 9 months 1 year 8.8 months 1Y 9M 1Y 8.8M 1Y 8.756M 1Y 9M
103709978.054 3.29y 3 years 3Y 3.3Y 3.5Y 3 years 3 months 3 years 3.4 months 3Y 3M 3Y 3.4M 3Y 3.437M 3Y 3.5M
197048958.302 6.24y 6 years 6Y 6.2Y 6Y 6 years 3 months 6 years 2.9 months 6Y 3M 6Y 2.9M 6Y 2.931M 6Y 3M
374393020.774 11.86y 12 years 12Y 11.9Y 12Y 11 years 10 months 11 years 10.4 months 11Y 10M 11Y 10.4M 11Y 10.369M 11Y 10.5M
711346739.471 22.54y 23 years 23Y 22.5Y 22.5Y 22 years 7 months 22 years 6.5 months 22Y 7M 22Y 6.5M 22Y 6.500M 22Y 6.5M

[-- Attachment #2.1: Type: text/html, Size: 14024 bytes --]
[-- Attachment #2.2: 0001-seconds-to-string-new-optional-arguments-for-readabl.patch --]
[-- Type: application/octet-stream, Size: 5854 bytes --]
From e72354cdf2bc0394890c42a28fe8e892a0e6f9a8 Mon Sep 17 00:00:00 2001
From: JD Smith <93749+jdtsmith@users.noreply.github.com>
Date: Thu, 11 Jul 2024 16:24:17 -0400
Subject: [PATCH] seconds-to-string: new optional arguments for readable
strings
---
doc/lispref/os.texi | 6 +++
etc/NEWS | 7 ++++
lisp/calendar/time-date.el | 82 ++++++++++++++++++++++++++++++++++++--
3 files changed, 91 insertions(+), 4 deletions(-)
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3ba3da459bf..ac9d5acdf3d 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -2155,6 +2155,12 @@ Time Calculations
structure. For instance, the 120th day in 2004 is April 29th.
@end defun
+@defun seconds-to-string delay &optional readable abbrev precision
+Return a string describing a given @var{delay} (in seconds). Optional
+arguments can be used to configure a human readable delay using various
+formats. For example, a delay of 9861.5 seconds with @var{readable} set
+to the symbol @code{expanded} returns @samp{2 hours 44 minutes}.
+
@node Timers
@section Timers for Delayed Execution
@cindex timers
diff --git a/etc/NEWS b/etc/NEWS
index f10f9ae4d65..1fd2a9404bb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -30,6 +30,13 @@ applies, and please also update docstrings as needed.
\f
* Changes in Emacs 31.1
+** Time & Date
+
++++
+*** 'seconds-to-string' includes new formatting options.
+Options are provided to produce human-readable delay strings in a
+variety of formats, for example "6 months 3 weeks" or "5m 52.5s".
+
\f
* Editing Changes in Emacs 31.1
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el
index eca80f1e8b6..c3206d1aa54 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -409,11 +409,85 @@ seconds-to-string
(list (* 3600 24 400) "d" (* 3600.0 24.0))
(list nil "y" (* 365.25 24 3600)))
"Formatting used by the function `seconds-to-string'.")
+
+(defvar seconds-to-string-readable
+ `(("Y" "year" "years" ,(round (* 60 60 24 365.2425)))
+ ("M" "month" "months" ,(round (* 60 60 24 30.436875)))
+ ("w" "week" "weeks" ,(* 60 60 24 7))
+ ("d" "day" "days" ,(* 60 60 24))
+ ("h" "hour" "hours" ,(* 60 60))
+ ("m" "minute" "minutes" 60)
+ ("s" "second" "seconds" 1))
+ "Formatting used by the function `seconds-to-string' with READABLE set.
+The format is an alist, with string keys ABBREV-UNIT, and elements like:
+
+ (ABBREV-UNIT UNIT UNIT-PLURAL SECS)
+
+where UNIT is a unit of time, ABBREV-UNIT is the abreviated form of
+UNIT, UNIT-PLURAL is the plural form of UNIT, and SECS is the number of
+seconds per UNIT.")
+
;;;###autoload
-(defun seconds-to-string (delay)
- ;; FIXME: There's a similar (tho fancier) function in mastodon.el!
- "Convert the time interval in seconds to a short string."
- (cond ((> 0 delay) (concat "-" (seconds-to-string (- delay))))
+(defun seconds-to-string (delay &optional readable abbrev precision)
+ "Convert time interval DELAY (in seconds) to a string.
+By default, the returned string is formatted as a float in the smallest
+unit from the variable `seconds-to-string' that is longer than DELAY,
+and a precision of two. If READABLE is non-nil, convert DELAY into a
+readable string, using the information provided in the variable
+`seconds-to-string-readable'. If it is the symbol `expanded', use two
+units to describe DELAY, if appropriate. E.g. \"1 hour 32 minutes\".
+If ABBREV is non-nil, abbreviate the readable units. If PRECISION is a
+whole number, round the value associated with the smallest displayed
+unit to that many digits after the decimal. If it is a non-negative
+float less than 1.0, round to that value."
+ (cond ((< delay 0)
+ (concat "-" (seconds-to-string (- delay) readable precision)))
+ (readable
+ (let* ((stsa seconds-to-string-readable)
+ (expanded (eq readable 'expanded))
+ digits
+ (round-to (cond ((wholenump precision)
+ (setq digits precision)
+ (expt 10 (- precision)))
+ ((and (floatp precision) (< precision 1.))
+ (setq digits (- (floor (log precision 10))))
+ precision)
+ (t (setq digits 0) 1)))
+ (dformat (if (> digits 0) (format "%%0.%df" digits)))
+ (padding (if abbrev "" " "))
+ here cnt cnt-pre here-pre cnt-val isfloatp)
+ (if (= (round delay round-to) 0)
+ (format "0%s" (if abbrev "s" " seconds"))
+ (while (and (setq here (pop stsa)) stsa
+ (< (/ delay (nth 3 here)) 1)))
+ (or (and
+ expanded stsa ; smaller unit remains
+ (progn
+ (setq
+ here-pre here here (car stsa)
+ cnt-pre (floor (/ (float delay) (nth 3 here-pre)))
+ cnt (round
+ (/ (- (float delay) (* cnt-pre (nth 3 here-pre)))
+ (nth 3 here))
+ round-to))
+ (if (> cnt 0) t (setq cnt cnt-pre here here-pre here-pre nil))))
+ (setq cnt (round (/ (float delay) (nth 3 here)) round-to)))
+ (setq cnt-val (* cnt round-to)
+ isfloatp (and (> digits 0)
+ (> (- cnt-val (floor cnt-val)) 0.)))
+ (cl-labels
+ ((unit (val here &optional plural)
+ (cond (abbrev (car here))
+ ((and (not plural) (<= (floor val) 1)) (nth 1 here))
+ (t (nth 2 here)))))
+ (concat
+ (when here-pre
+ (concat (number-to-string cnt-pre) padding
+ (unit cnt-pre here-pre) " "))
+ (if isfloatp (format dformat cnt-val)
+ (number-to-string (floor cnt-val)))
+ padding
+ (unit cnt-val here isfloatp)))))) ; float formats are always plural
((= 0 delay) "0s")
(t (let ((sts seconds-to-string) here)
(while (and (car (setq here (pop sts)))
--
2.45.2
[-- Attachment #2.3: Type: text/html, Size: 242 bytes --]
[-- Attachment #2.4: s2s_test.el --]
[-- Type: application/octet-stream, Size: 1863 bytes --]
(require 'cl-lib)
(defun s2s/example ()
(interactive)
(with-temp-buffer-window "s2s/example" nil nil
(princ
(concat
(format "%14s %10s %10s %6s %7s %7s %21s %23s %7s %9s %11s %9s\n"
"Delay (s)" "s2s" "s2s-r" "s2s-ra" "s2s-ra1" "s2s-rah" "s2s-e" "s2s-e1" "s2s-ea" "s2s-ea1"
"s2s-ea3" "s2s-eah")
(cl-loop for s = 0.0 then (if (zerop s) 0.45 (* s 1.9))
while (< s (* 365.25 24 3600 40))
concat (format "%14.3f %10s %10s %6s %7s %7s %21s %23s %7s %9s %11s %9s\n" s
(seconds-to-string s)
(seconds-to-string s 'readable)
(seconds-to-string s 'readable 'abbrev)
(seconds-to-string s 'readable 'abbrev 1)
(seconds-to-string s 'readable 'abbrev 0.5)
(seconds-to-string s 'expanded)
(seconds-to-string s 'expanded nil 1)
(seconds-to-string s 'expanded 'abbrev)
(seconds-to-string s 'expanded 'abbrev 1)
(seconds-to-string s 'expanded 'abbrev 3)
(seconds-to-string s 'expanded 'abbrev 0.5)))))))
(defun s2s/benchmark ()
(interactive)
(let* ((ndelays 100000)
(delays (cl-loop for i from 1 to ndelays
with max = (* 365.25 24 3600 40)
collect (cl-random max)))
(bsmpl (benchmark-run nil
(cl-loop for d in delays
do (seconds-to-string d))))
(brdbl (benchmark-run nil
(cl-loop for d in delays
do (seconds-to-string d t t 0.1)))))
(with-temp-buffer-window "s2s/benchmarks" nil nil
(princ "seconds-to-string benchmarks\n")
(princ (format " default timing: %0.2fµs\n\t%S\n"
(/ (car bsmpl) ndelays 1e-6) bsmpl))
(princ (format " readable timing: %0.2fµs\n\t%S\n\n"
(/ (car brdbl) ndelays 1e-6) brdbl))
(princ (format "readable/default: %0.2f\n" (/ (car brdbl) (car bsmpl)))))))
[-- Attachment #2.5: Type: text/html, Size: 246 bytes --]
next prev parent reply other threads:[~2024-12-15 18:07 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-06-15 17:24 bug#71573: [PATCH] seconds-to-string-approximate JD Smith
2024-06-15 17:36 ` Eli Zaretskii
2024-06-17 6:20 ` bug#71573: Related functions from ts.el Adam Porter
2024-06-22 10:55 ` Stefan Kangas
2024-06-22 21:54 ` Adam Porter
2024-06-22 8:45 ` bug#71572: [PATCH] seconds-to-string-approximate Eli Zaretskii
2024-06-22 21:56 ` Adam Porter
2024-06-22 23:42 ` Paul Eggert
2024-06-23 2:16 ` JD Smith
2024-07-04 5:29 ` Eli Zaretskii
2024-07-04 6:04 ` Eli Zaretskii
2024-07-04 7:09 ` Paul Eggert
2024-07-06 19:29 ` JD Smith
2024-07-06 21:09 ` Paul Eggert
2024-07-11 21:01 ` JD Smith
2024-11-30 18:58 ` JD Smith
2024-12-07 13:02 ` Eli Zaretskii
2024-12-07 17:52 ` JD Smith
2024-12-07 19:17 ` john muhl
2024-12-08 19:52 ` JD Smith
2024-12-08 22:51 ` john muhl
2024-12-15 18:07 ` JD Smith [this message]
2024-12-15 23:26 ` john muhl
2024-12-21 10:32 ` Eli Zaretskii
2024-07-04 15:27 ` JD Smith
2024-07-04 15:59 ` Eli Zaretskii
2024-07-04 17:16 ` JD Smith
2024-07-04 18:06 ` Eli Zaretskii
2024-07-04 16:36 ` Ihor Radchenko
2024-07-04 17:23 ` JD Smith
2024-07-04 17:57 ` Ihor Radchenko
2024-06-23 5:13 ` Eli Zaretskii
2024-07-03 20:32 ` JD Smith
2024-07-04 5:29 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1CCC10A3-F76D-477C-9E04-AA0903853EAC@gmail.com \
--to=jdtsmith@gmail.com \
--cc=71572@debbugs.gnu.org \
--cc=adam@alphapapa.net \
--cc=eggert@cs.ucla.edu \
--cc=eliz@gnu.org \
--cc=jm@pub.pink \
--cc=jonas@bernoul.li \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.