* bug#71573: [PATCH] seconds-to-string-approximate
@ 2024-06-15 17:24 JD Smith
2024-06-15 17:36 ` Eli Zaretskii
` (2 more replies)
0 siblings, 3 replies; 31+ messages in thread
From: JD Smith @ 2024-06-15 17:24 UTC (permalink / raw)
To: 71573; +Cc: Adam Porter, jonas
[-- Attachment #1: Type: text/plain, Size: 827 bytes --]
A very useful and widely used time operation is to approximate a given delay or age (in seconds) using a human-readable unit — think "2 hours", "5 days", "3 weeks", or "7 months". We have `seconds-to-string', but it provides more precision than is often required, skips some meaningful "human readable" duration units like weeks and months, and uses abbreviated units exclusively.
For those familiar with magit, the `magit--age' function has provided this capability for quite some time (e.g. for short commit age), and other packages have adapted it. It would be useful to have a version in core.
This patch provides a `seconds-to-string-approximate' function based loosely on `magit--age' and `seconds-to-string'. It allows using abbreviated or full units, and can optionally round to the nearest half-unit.
[-- Attachment #2: seconds-to-string-approximate.patch --]
[-- Type: application/octet-stream, Size: 2089 bytes --]
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el
index eca80f1e8b6..079001bafe2 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -420,6 +420,37 @@ seconds-to-string
(<= (car here) delay)))
(concat (format "%.2f" (/ delay (car (cddr here)))) (cadr here))))))
+(defvar seconds-to-string-approximate
+ `(("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-approximate'.")
+;;;###autoload
+(defun seconds-to-string-approximate (delay &optional abbreviate half)
+ "Convert the time interval DELAY in seconds to a string approximation.
+Abbreviate the units if ABBREVIATE is non-nil. If HALF is non-nil,
+round to the nearest half-unit, otherwise round to the nearest unit."
+ (cond ((> 0 delay)
+ (concat "-" (seconds-to-string-approximate (- delay) abbreviate half)))
+ ((= (round delay (if half 0.5 1.)) 0)
+ (format "0%s" (if abbreviate "s" " seconds")))
+ (t (let ((stsa seconds-to-string-approximate) here cnt)
+ (while (and (setq here (pop stsa)) stsa
+ (< (/ delay (nth 3 here)) 1)))
+ (setq cnt (round (/ (float delay) (nth 3 here)) (if half 0.5 1.)))
+ (concat
+ (let ((c (if half (/ cnt 2) cnt)))
+ (if (> c 0) (number-to-string c) ""))
+ (if (and half (= (mod cnt 2) 1)) "½" "")
+ (if abbreviate "" " ")
+ (cond (abbreviate (car here))
+ ((<= cnt (if half 2 1)) (nth 1 here))
+ (t (nth 2 here))))))))
+
(defun date-days-in-month (year month)
"The number of days in MONTH in YEAR."
(unless (and (numberp month) (<= 1 month 12))
^ permalink raw reply related [flat|nested] 31+ messages in thread
* bug#71573: [PATCH] seconds-to-string-approximate
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 8:45 ` bug#71572: [PATCH] seconds-to-string-approximate Eli Zaretskii
2 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-06-15 17:36 UTC (permalink / raw)
To: JD Smith; +Cc: 71573, adam, jonas
merge 71573 71572
thanks
> Cc: Adam Porter <adam@alphapapa.net>, jonas@bernoul.li
> From: JD Smith <jdtsmith@gmail.com>
> Date: Sat, 15 Jun 2024 13:24:00 -0400
> A very useful and widely used time operation is to approximate a given delay or age (in seconds) using a human-readable unit — think "2 hours", "5 days", "3 weeks", or "7 months". We have `seconds-to-string', but it provides more precision than is often required, skips some meaningful "human readable" duration units like weeks and months, and uses abbreviated units exclusively.
>
> For those familiar with magit, the `magit--age' function has provided this capability for quite some time (e.g. for short commit age), and other packages have adapted it. It would be useful to have a version in core.
>
> This patch provides a `seconds-to-string-approximate' function based loosely on `magit--age' and `seconds-to-string'. It allows using abbreviated or full units, and can optionally round to the nearest half-unit.
This is a duplicate of bug#71572, merging.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71573: Related functions from ts.el
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 ` Adam Porter
2024-06-22 10:55 ` Stefan Kangas
2024-06-22 8:45 ` bug#71572: [PATCH] seconds-to-string-approximate Eli Zaretskii
2 siblings, 1 reply; 31+ messages in thread
From: Adam Porter @ 2024-06-17 6:20 UTC (permalink / raw)
To: 71573
Hi all,
FWIW, my ts.el timestamp library has the related functions
`ts-human-duration' and `ts-human-format-duration'. See
<https://github.com/alphapapa/ts.el/blob/552936017cfdec89f7fc20c254ae6b37c3f22c5b/ts.el#L440-L491>
and code below.
They work a bit differently, but I've found them very useful in my other
Elisp projects, and my profiling has shown that they perform very well
relative to, e.g. the existing `format-seconds' function in terms of
runtime and GC (see benchmarks in source comments).
If any of the code in ts.el would be helpful, I'd be glad to contribute
it to Emacs (some discussion about upstreaming parts of ts.el has also
been going on in other, Org-related contexts).
--Adam
Elisp follows:
(defun ts-human-duration (seconds)
"Return plist describing duration SECONDS.
List includes years, days, hours, minutes, and seconds. This is
a simple calculation that does not account for leap years, leap
seconds, etc."
;; TODO: Add weeks.
(cl-macrolet ((dividef (place divisor)
;; Divide PLACE by DIVISOR, set PLACE to the
remainder, and return the quotient.
`(prog1 (/ ,place ,divisor)
(setf ,place (% ,place ,divisor)))))
(let* ((seconds (floor seconds))
(years (dividef seconds 31536000))
(days (dividef seconds 86400))
(hours (dividef seconds 3600))
(minutes (dividef seconds 60)))
(list :years years :days days :hours hours :minutes minutes
:seconds seconds))))
;; See also the built-in function `format-seconds', which I seem to have
;; overlooked before writing this. However, a quick benchmark, run
;; 100,000 times, shows that, when controllable formatting is not needed,
;; `ts-human-format-duration' is much faster and generates less garbage:
;; | Form | x faster than next | Total runtime | #
of GCs | Total GC runtime |
;;
|--------------------------+--------------------+---------------+----------+------------------|
;; | ts-human-format-duration | 5.82 | 0.832945 |
3 | 0.574929 |
;; | format-seconds | slowest | 4.848253 |
17 | 3.288799 |
(cl-defun ts-human-format-duration (seconds &optional abbreviate)
"Return human-formatted string describing duration SECONDS.
If SECONDS is less than 1, returns \"0 seconds\". If ABBREVIATE
is non-nil, return a shorter version, without spaces. This is a
simple calculation that does not account for leap years, leap
seconds, etc."
;; FIXME: Doesn't work with negative values, even though
`ts-human-duration' does.
(if (< seconds 1)
(if abbreviate "0s" "0 seconds")
(cl-macrolet ((format> (place)
;; When PLACE is greater than 0, return
formatted string using its symbol name.
`(when (> ,place 0)
(format "%d%s%s" ,place
(if abbreviate "" " ")
(if abbreviate
,(substring (symbol-name
place) 0 1)
,(symbol-name place)))))
(join-places (&rest places)
;; Return string joining the names and
values of PLACES.
`(->> (list ,@(cl-loop for place in places
collect `(format>
,place)))
-non-nil
(s-join (if abbreviate "" ", ")))))
(-let* (((&plist :years :days :hours :minutes :seconds)
(ts-human-duration seconds)))
(join-places years days hours minutes seconds)))))
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
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 8:45 ` Eli Zaretskii
2024-06-22 21:56 ` Adam Porter
2024-06-22 23:42 ` Paul Eggert
2 siblings, 2 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-06-22 8:45 UTC (permalink / raw)
To: JD Smith, Paul Eggert; +Cc: adam, 71572, jonas
> Cc: Adam Porter <adam@alphapapa.net>, jonas@bernoul.li
> From: JD Smith <jdtsmith@gmail.com>
> Date: Sat, 15 Jun 2024 13:24:00 -0400
>
> A very useful and widely used time operation is to approximate a given delay or age (in seconds) using a human-readable unit — think "2 hours", "5 days", "3 weeks", or "7 months". We have `seconds-to-string', but it provides more precision than is often required, skips some meaningful "human readable" duration units like weeks and months, and uses abbreviated units exclusively.
>
> For those familiar with magit, the `magit--age' function has provided this capability for quite some time (e.g. for short commit age), and other packages have adapted it. It would be useful to have a version in core.
>
> This patch provides a `seconds-to-string-approximate' function based loosely on `magit--age' and `seconds-to-string'. It allows using abbreviated or full units, and can optionally round to the nearest half-unit.
Paul, any comments to the patch?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71573: Related functions from ts.el
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
0 siblings, 1 reply; 31+ messages in thread
From: Stefan Kangas @ 2024-06-22 10:55 UTC (permalink / raw)
To: Adam Porter, 71573
Adam Porter <adam@alphapapa.net> writes:
> ;; See also the built-in function `format-seconds', which I seem to have
> ;; overlooked before writing this. However, a quick benchmark, run
> ;; 100,000 times, shows that, when controllable formatting is not needed,
> ;; `ts-human-format-duration' is much faster and generates less garbage:
>
> ;; | Form | x faster than next | Total runtime | #
> of GCs | Total GC runtime |
> ;;
> |--------------------------+--------------------+---------------+----------+------------------|
> ;; | ts-human-format-duration | 5.82 | 0.832945 |
> 3 | 0.574929 |
> ;; | format-seconds | slowest | 4.848253 |
> 17 | 3.288799 |
Is this used a lot in hot loops? IOW, is it worth optimizing?
If yes, how about adding something like what you have as an optimization
to `format-seconds` for when the format is very simple? Would that
remove the need for `ts-human-format-duration'?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71573: Related functions from ts.el
2024-06-22 10:55 ` Stefan Kangas
@ 2024-06-22 21:54 ` Adam Porter
0 siblings, 0 replies; 31+ messages in thread
From: Adam Porter @ 2024-06-22 21:54 UTC (permalink / raw)
To: Stefan Kangas, 71573
On 6/22/24 05:55, Stefan Kangas wrote:
> Adam Porter <adam@alphapapa.net> writes:
>
>> ;; See also the built-in function `format-seconds', which I seem to have
>> ;; overlooked before writing this. However, a quick benchmark, run
>> ;; 100,000 times, shows that, when controllable formatting is not needed,
>> ;; `ts-human-format-duration' is much faster and generates less garbage:
>>
>> ;; | Form | x faster than next | Total runtime | #
>> of GCs | Total GC runtime |
>> ;;
>> |--------------------------+--------------------+---------------+----------+------------------|
>> ;; | ts-human-format-duration | 5.82 | 0.832945 |
>> 3 | 0.574929 |
>> ;; | format-seconds | slowest | 4.848253 |
>> 17 | 3.288799 |
>
> Is this used a lot in hot loops? IOW, is it worth optimizing?
It can be. Imagine formatting timestamps for thousands of items in a
vtable. And imagine that happening frequently, e.g. if the vtable is
redrawn automatically to account for data having arrived over the network.
> If yes, how about adding something like what you have as an optimization
> to `format-seconds` for when the format is very simple? Would that
> remove the need for `ts-human-format-duration'?
I don't know what form such an optimization would take. Perhaps someone
could profile it and optimize some hot spots in it, but I'll have to
decline that to-do for now, as my list is much too long already. :)
BTW, please note that I don't claim that ts-human-format-duration is
superior to format-seconds, because the latter is different and has some
additional features. Rather, ts-human-format-duration is an alternative
that can sometimes be worth using instead. I present it as food for
thought when considering to implement related functionality.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
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
1 sibling, 0 replies; 31+ messages in thread
From: Adam Porter @ 2024-06-22 21:56 UTC (permalink / raw)
To: Eli Zaretskii, JD Smith, Paul Eggert; +Cc: 71572, jonas
May I also recommend that the function be benchmarked, and potentially
that it be profiled and optimized if needed? A function like this may
be used on hundreds or even thousands of items in a single operation
(e.g. formatting a long list of items into a vtable), so it's important
that it not be too slow. I don't think I've seen mention of performance
yet (forgive me if I missed it).
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
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-06-23 5:13 ` Eli Zaretskii
1 sibling, 2 replies; 31+ messages in thread
From: Paul Eggert @ 2024-06-22 23:42 UTC (permalink / raw)
To: Eli Zaretskii, JD Smith; +Cc: adam, 71572, jonas
On 6/22/24 04:45, Eli Zaretskii wrote:
>> Cc: Adam Porter <adam@alphapapa.net>, jonas@bernoul.li
>> From: JD Smith <jdtsmith@gmail.com>
>> Date: Sat, 15 Jun 2024 13:24:00 -0400
>>
>> A very useful and widely used time operation is to approximate a given delay or age (in seconds) using a human-readable unit — think "2 hours", "5 days", "3 weeks", or "7 months". We have `seconds-to-string', but it provides more precision than is often required, skips some meaningful "human readable" duration units like weeks and months, and uses abbreviated units exclusively.
>>
>> For those familiar with magit, the `magit--age' function has provided this capability for quite some time (e.g. for short commit age), and other packages have adapted it. It would be useful to have a version in core.
>>
>> This patch provides a `seconds-to-string-approximate' function based loosely on `magit--age' and `seconds-to-string'. It allows using abbreviated or full units, and can optionally round to the nearest half-unit.
>
> Paul, any comments to the patch?
For starters:
Why define a new function, instead of adding optional arguments to the
existing one?
Why not look at what mastodon.el does, as the comment in
seconds-to-string suggests? For example, mastodon-tl--human-duration
lets you specify whatever resolution you want, instead of limiting you
to either 0.5 or 1 as in the proposed patch.
Isn't the master branch was in an long-term sort-of-frozen state, until
a branch is created for Emacs 30? If so I imagine changes in this area
should wait.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-06-22 23:42 ` Paul Eggert
@ 2024-06-23 2:16 ` JD Smith
2024-07-04 5:29 ` Eli Zaretskii
2024-06-23 5:13 ` Eli Zaretskii
1 sibling, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-06-23 2:16 UTC (permalink / raw)
To: Paul Eggert; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas
[-- Attachment #1: Type: text/plain, Size: 5234 bytes --]
> On Jun 22, 2024, at 7:42 PM, Paul Eggert <eggert@cs.ucla.edu> wrote:
>
> On 6/22/24 04:45, Eli Zaretskii wrote:
>>>
>> Paul, any comments to the patch?
>
> For starters:
Thanks for the comment.
> Why define a new function, instead of adding optional arguments to the existing one?
No real reason; new patch doing so attached.
> Why not look at what mastodon.el does, as the comment in seconds-to-string suggests? For example, mastodon-tl--human-duration lets you specify whatever resolution you want, instead of limiting you to either 0.5 or 1 as in the proposed patch.
I see that mastodon is a package in ELPA, so doesn't satisfy the need in core. I took a look at this function. The RESOLUTION mentioned is not equivalent to the HALF argument, it is the minimum resolution in seconds. So setting it to e.g. 3600 results in truncating to the hour, but changes nothing below the hour. Setting it to the number of seconds in a year gives something quite similar to magit--age (though I notice the mastodon function truncates, instead of rounds; see, e.g., 2.98y in the table below).
Here's a comparison among:
- the current seconds-to-string
- mastodon-tl--human-duration
- mastodon with a 3600s resolution
- mastodon with 1yr "resolution"
- the new seconds-to-string with option READABLE=t
- new seconds-to-string with abbreviated units and half unit resolution
Delay (s) s-to-s mastodon mastodon (3600s) mast (1yr) s-to-s (rdb) s-to-s (rdb=abbrev, half)
0.5 450.00ms 0 sec 0 sec 0 sec 0 seconds ½s
1.0 1.03s 1 sec 1 sec 1 sec 1 second 1s
2.4 2.38s 2 secs 2 secs 2 secs 2 seconds 2½s
5.5 5.48s 5 secs 5 secs 5 secs 5 seconds 5½s
12.6 12.59s 12 secs 12 secs 12 secs 13 seconds 12½s
29.0 28.96s 28 secs 28 secs 28 secs 29 seconds 29s
66.6 66.62s 1 min 1 min 1 min 1 minute 1m
153.2 2.55m 2 mins 2 mins 2 mins 3 minutes 2½m
352.4 5.87m 5 mins 5 mins 5 mins 6 minutes 6m
810.5 13.51m 13 mins 13 mins 13 mins 14 minutes 13½m
1864.2 31.07m 31 mins 31 mins 31 mins 31 minutes 31m
4287.6 71.46m 1 hour, 11 mins 1 hour 1 hour 1 hour 1h
9861.6 2.74h 2 hours, 44 mins 2 hours 2 hours 3 hours 2½h
22681.6 6.30h 6 hours, 18 mins 6 hours 6 hours 6 hours 6½h
52167.8 14.49h 14 hours, 29 mins 14 hours 14 hours 14 hours 14½h
119985.9 1.39d 1 day, 9 hours 1 day, 9 hours 1 day 1 day 1½d
275967.5 3.19d 3 days, 4 hours 3 days, 4 hours 3 days 3 days 3d
634725.2 7.35d 1 week 1 week 1 week 1 week 1w
1459867.9 16.90d 2 weeks, 2 days 2 weeks, 2 days 2 weeks 2 weeks 2½w
3357696.2 38.86d 1 month, 1 week 1 month, 1 week 1 month 1 month 1½M
7722701.2 89.38d 2 months, 4 weeks 2 months, 4 weeks 2 months 3 months 3M
17762212.9 205.58d 6 months, 3 weeks 6 months, 3 weeks 6 months 7 months 7M
40853089.6 1.29y 1 year, 3 months 1 year, 3 months 1 year 1 year 1½Y
93962106.0 2.98y 2 years, 11 months 2 years, 11 months 2 years 3 years 3Y
216112843.8 6.85y 6 years, 10 months 6 years, 10 months 6 years 7 years 7Y
497059540.7 15.75y 15 years, 9 months 15 years, 9 months 15 years 16 years 16Y
The last column is obviously the most compact while still conveying a good amount of information, but the 1yr mastodon and normal READABLE s-to-s are also quite good (effectively equivalent to magit--age) for a quick glance and maintaining ~constant widths. I do find it awkward to set the RESOLUTION argument to >30 million seconds to achieve this. It took me a bit to understand what this argument does.
Code use to produce:
(concat
(format "%11s %10s %18s %18s %12s %12s %s\n" "Delay (s)" "s-to-s"
"mastodon" "mastodon (3600s)" "mast (1yr)" "s-to-s (rdb)" "s-to-s (rdb=abbrev, half)")
(cl-loop for s = 0.45 then (* s 2.3) while (< s (* 365.25 24 3600 22))
concat (format "%11.1f %10s %18s %18s %12s %12s %s\n" s
(seconds-to-string s)
(car (mastodon-tl--human-duration s))
(car (mastodon-tl--human-duration s 3600))
(car (mastodon-tl--human-duration s (* 365.25 24 3600)))
(seconds-to-string s t)
(seconds-to-string s 'abbrev 'half))))

[-- Attachment #2.1: Type: text/html, Size: 11225 bytes --]
[-- Attachment #2.2: time-data-readable-seconds.patch --]
[-- Type: application/octet-stream, Size: 2217 bytes --]
--- time-date.el 2024-06-22 21:51:21
+++ time-date_new.el 2024-06-22 21:53:13
@@ -406,10 +406,41 @@
(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.")
+
;;;###autoload
-(defun seconds-to-string (delay)
- "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 half)
+ "Convert the time interval in seconds to a short string.
+If READABLE is non-nil, convert DELAY into a readable string. If it is
+the value `abbrev', abbreviate the units. If HALF is set, round to the
+nearest half unit."
+ (cond ((> 0 delay) (concat "-" (seconds-to-string (- delay) readable half)))
+ (readable
+ (let ((abbrev (eq readable 'abbrev))
+ (stsa seconds-to-string-readable)
+ here cnt)
+ (if (= (round delay (if half 0.5 1.)) 0)
+ (format "0%s" (if abbrev "s" " seconds"))
+ (while (and (setq here (pop stsa)) stsa
+ (< (/ delay (nth 3 here)) 1)))
+ (setq cnt (round (/ (float delay) (nth 3 here)) (if half 0.5 1.)))
+ (concat
+ (let ((c (if half (/ cnt 2) cnt)))
+ (if (> c 0) (number-to-string c) ""))
+ (if (and half (= (mod cnt 2) 1)) "½" "")
+ (if abbrev "" " ")
+ (cond (abbrev (car here))
+ ((<= cnt (if half 2 1)) (nth 1 here))
+ (t (nth 2 here)))))))
((= 0 delay) "0s")
(t (let ((sts seconds-to-string) here)
(while (and (car (setq here (pop sts)))
[-- Attachment #2.3: Type: text/html, Size: 212 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-06-22 23:42 ` Paul Eggert
2024-06-23 2:16 ` JD Smith
@ 2024-06-23 5:13 ` Eli Zaretskii
2024-07-03 20:32 ` JD Smith
1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2024-06-23 5:13 UTC (permalink / raw)
To: Paul Eggert; +Cc: adam, 71572, jonas, jdtsmith
> Date: Sat, 22 Jun 2024 19:42:25 -0400
> Cc: 71572@debbugs.gnu.org, adam@alphapapa.net, jonas@bernoul.li
> From: Paul Eggert <eggert@cs.ucla.edu>
>
> Isn't the master branch was in an long-term sort-of-frozen state, until
> a branch is created for Emacs 30? If so I imagine changes in this area
> should wait.
Yes, this is for Emacs 31.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-06-23 5:13 ` Eli Zaretskii
@ 2024-07-03 20:32 ` JD Smith
2024-07-04 5:29 ` Eli Zaretskii
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-07-03 20:32 UTC (permalink / raw)
To: Eli Zaretskii, Paul Eggert, 71572; +Cc: Adam Porter, jonas
> On Jun 23, 2024, at 1:13 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> Date: Sat, 22 Jun 2024 19:42:25 -0400
>> Cc: 71572@debbugs.gnu.org, adam@alphapapa.net, jonas@bernoul.li
>> From: Paul Eggert <eggert@cs.ucla.edu>
>>
>> Isn't the master branch was in an long-term sort-of-frozen state, until
>> a branch is created for Emacs 30? If so I imagine changes in this area
>> should wait.
>
> Yes, this is for Emacs 31.
Any further thoughts on this approach?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-06-23 2:16 ` JD Smith
@ 2024-07-04 5:29 ` Eli Zaretskii
2024-07-04 6:04 ` Eli Zaretskii
` (2 more replies)
0 siblings, 3 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-07-04 5:29 UTC (permalink / raw)
To: JD Smith; +Cc: adam, 71572, jonas, eggert
> From: JD Smith <jdtsmith@gmail.com>
> Date: Sat, 22 Jun 2024 22:16:40 -0400
> Cc: Eli Zaretskii <eliz@gnu.org>,
> 71572@debbugs.gnu.org,
> Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li
>
> > Why not look at what mastodon.el does, as the comment in seconds-to-string suggests? For example, mastodon-tl--human-duration lets you specify whatever resolution you want, instead of limiting you to either 0.5 or 1 as in the proposed patch.
>
> I see that mastodon is a package in ELPA, so doesn't satisfy the need in core. I took a look at this function. The RESOLUTION mentioned is not equivalent to the HALF argument, it is the minimum resolution in seconds. So setting it to e.g. 3600 results in truncating to the hour, but changes nothing below the hour. Setting it to the number of seconds in a year gives something quite similar to magit--age (though I notice the mastodon function truncates, instead of rounds; see, e.g., 2.98y in the table below).
>
> Here's a comparison among:
>
> - the current seconds-to-string
> - mastodon-tl--human-duration
> - mastodon with a 3600s resolution
> - mastodon with 1yr "resolution"
> - the new seconds-to-string with option READABLE=t
> - new seconds-to-string with abbreviated units and half unit resolution
>
> Delay (s) s-to-s mastodon mastodon (3600s) mast (1yr) s-to-s (rdb) s-to-s (rdb=abbrev, half)
> 0.5 450.00ms 0 sec 0 sec 0 sec 0 seconds ½s
> 1.0 1.03s 1 sec 1 sec 1 sec 1 second 1s
> 2.4 2.38s 2 secs 2 secs 2 secs 2 seconds 2½s
> 5.5 5.48s 5 secs 5 secs 5 secs 5 seconds 5½s
> 12.6 12.59s 12 secs 12 secs 12 secs 13 seconds 12½s
> 29.0 28.96s 28 secs 28 secs 28 secs 29 seconds 29s
> 66.6 66.62s 1 min 1 min 1 min 1 minute 1m
> 153.2 2.55m 2 mins 2 mins 2 mins 3 minutes 2½m
> 352.4 5.87m 5 mins 5 mins 5 mins 6 minutes 6m
> 810.5 13.51m 13 mins 13 mins 13 mins 14 minutes 13½m
> 1864.2 31.07m 31 mins 31 mins 31 mins 31 minutes 31m
> 4287.6 71.46m 1 hour, 11 mins 1 hour 1 hour 1 hour 1h
> 9861.6 2.74h 2 hours, 44 mins 2 hours 2 hours 3 hours 2½h
> 22681.6 6.30h 6 hours, 18 mins 6 hours 6 hours 6 hours 6½h
> 52167.8 14.49h 14 hours, 29 mins 14 hours 14 hours 14 hours 14½h
> 119985.9 1.39d 1 day, 9 hours 1 day, 9 hours 1 day 1 day 1½d
> 275967.5 3.19d 3 days, 4 hours 3 days, 4 hours 3 days 3 days 3d
> 634725.2 7.35d 1 week 1 week 1 week 1 week 1w
> 1459867.9 16.90d 2 weeks, 2 days 2 weeks, 2 days 2 weeks 2 weeks 2½w
> 3357696.2 38.86d 1 month, 1 week 1 month, 1 week 1 month 1 month 1½M
> 7722701.2 89.38d 2 months, 4 weeks 2 months, 4 weeks 2 months 3 months 3M
> 17762212.9 205.58d 6 months, 3 weeks 6 months, 3 weeks 6 months 7 months 7M
> 40853089.6 1.29y 1 year, 3 months 1 year, 3 months 1 year 1 year 1½Y
> 93962106.0 2.98y 2 years, 11 months 2 years, 11 months 2 years 3 years 3Y
> 216112843.8 6.85y 6 years, 10 months 6 years, 10 months 6 years 7 years 7Y
> 497059540.7 15.75y 15 years, 9 months 15 years, 9 months 15 years 16 years 16Y
Basically, this shows that:
. mastodon truncates where seconds-to-string rounds
. seconds-to-string lacks the "1 hour 11 min" output format
. seconds-to-string sometimes produces inaccurate results, as in
5.5 => 5.48s
The last item worries me: can we fix this, please?
The second item sounds like a useful feature, so maybe an optional
behavior could provide it as well?
Thanks.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-03 20:32 ` JD Smith
@ 2024-07-04 5:29 ` Eli Zaretskii
0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-07-04 5:29 UTC (permalink / raw)
To: JD Smith; +Cc: adam, 71572, jonas, eggert
> From: JD Smith <jdtsmith@gmail.com>
> Date: Wed, 3 Jul 2024 16:32:46 -0400
> Cc: Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li
>
>
> > On Jun 23, 2024, at 1:13 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> >
> >> Date: Sat, 22 Jun 2024 19:42:25 -0400
> >> Cc: 71572@debbugs.gnu.org, adam@alphapapa.net, jonas@bernoul.li
> >> From: Paul Eggert <eggert@cs.ucla.edu>
> >>
> >> Isn't the master branch was in an long-term sort-of-frozen state, until
> >> a branch is created for Emacs 30? If so I imagine changes in this area
> >> should wait.
> >
> > Yes, this is for Emacs 31.
>
> Any further thoughts on this approach?
I've just sent a few.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 5:29 ` Eli Zaretskii
@ 2024-07-04 6:04 ` Eli Zaretskii
2024-07-04 7:09 ` Paul Eggert
2024-07-04 15:27 ` JD Smith
2 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-07-04 6:04 UTC (permalink / raw)
To: jdtsmith; +Cc: adam, 71572, jonas, eggert
> Cc: adam@alphapapa.net, 71572@debbugs.gnu.org, jonas@bernoul.li,
> eggert@cs.ucla.edu
> Date: Thu, 04 Jul 2024 08:29:03 +0300
> From: Eli Zaretskii <eliz@gnu.org>
>
> Basically, this shows that:
>
> . mastodon truncates where seconds-to-string rounds
> . seconds-to-string lacks the "1 hour 11 min" output format
> . seconds-to-string sometimes produces inaccurate results, as in
> 5.5 => 5.48s
>
> The last item worries me: can we fix this, please?
> The second item sounds like a useful feature, so maybe an optional
> behavior could provide it as well?
And one more nit: should this function be described in the ELisp
manual?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
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-04 15:27 ` JD Smith
2 siblings, 1 reply; 31+ messages in thread
From: Paul Eggert @ 2024-07-04 7:09 UTC (permalink / raw)
To: Eli Zaretskii, JD Smith; +Cc: adam, 71572, jonas
On 7/4/24 06:29, Eli Zaretskii wrote:
> Basically, this shows that:
>
> . mastodon truncates where seconds-to-string rounds
For high precision timestamps it's often better to truncate, for various
reasons. That's what the C code does with timestamps, anyway.
seconds-to-string historically has rounded several times which of course
is not best but apparently is good enough for its intended application area.
> . seconds-to-string lacks the "1 hour 11 min" output format
That format could be confusing with negative delays, e.g., "-1 hour 11 min".
> . seconds-to-string sometimes produces inaccurate results, as in
> 5.5 => 5.48s
No, it's the other way round: seconds-to-string is more accurate than
the alternatives. That's merely a misfeature in the test script.
seconds-to-string is passed the argument 5.475149999999998, and formats
it as "5.48" whereas the test script formats it as "5.5".
I'm not sold on the "half" argument; seems like a cuteness rather than a
feature that's all that useful (among other things, it assumes Unicode
or something like it). What's really going on here is that there's an
optional argument specifying style and I imagine that style preferences
will differ (Mastodon style, etc.).
I imagine that style preferences could proliferate. Is there an ISO or
similar standard for this sort of thing?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 5:29 ` Eli Zaretskii
2024-07-04 6:04 ` Eli Zaretskii
2024-07-04 7:09 ` Paul Eggert
@ 2024-07-04 15:27 ` JD Smith
2024-07-04 15:59 ` Eli Zaretskii
2024-07-04 16:36 ` Ihor Radchenko
2 siblings, 2 replies; 31+ messages in thread
From: JD Smith @ 2024-07-04 15:27 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Adam Porter, 71572, jonas, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 4718 bytes --]
> On Jul 4, 2024, at 1:29 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>
>> Here's a comparison among:
>>
>> - the current seconds-to-string
>> - mastodon-tl--human-duration
>> - mastodon with a 3600s resolution
>> - mastodon with 1yr "resolution"
>> - the new seconds-to-string with option READABLE=t
>> - new seconds-to-string with abbreviated units and half unit resolution
<snip>
>
> Basically, this shows that:
>
> . mastodon truncates where seconds-to-string rounds
I definitely think rounding makes more sense for a readable unit; consider 2.98y.
> . seconds-to-string lacks the "1 hour 11 min" output format
> . seconds-to-string sometimes produces inaccurate results, as in
> 5.5 => 5.48s
> The last item worries me: can we fix this, please?
The latter issue results simply from my truncation of the initial delay column to 1 decimal digit. E.g. .45s * 2.3^3 = 5.47515s, so s2s has it right.
> The second item sounds like a useful feature, so maybe an optional
> behavior could provide it as well?
I've expanded the call format to (delay &optional readable abbrev half) , with READABLE='expanded an option to get the "1 hour 11 min" expanded format, i.e. one larger and one smaller unit (if appropriate). Patch below.
Updated output at the same delays for the various new options (r: readable, e: readable=expanded, a: abbrev, h: half):
Delay (s) s2s s2s-r s2s-ra s2s-rah s2s-e s2s-ea s2s-eah
0.45 450.00ms 0 seconds 0s ½s 0 seconds 0s ½s
1.03 1.03s 1 second 1s 1s 1 second 1s 1s
2.38 2.38s 2 seconds 2s 2½s 2 seconds 2s 2½s
5.48 5.48s 5 seconds 5s 5½s 5 seconds 5s 5½s
12.59 12.59s 13 seconds 13s 12½s 13 seconds 13s 12½s
28.96 28.96s 29 seconds 29s 29s 29 seconds 29s 29s
66.62 66.62s 1 minute 1m 1m 1 minute 7 seconds 1m 7s 1m 6½s
153.22 2.55m 3 minutes 3m 2½m 2 minutes 33 seconds 2m 33s 2m 33s
352.40 5.87m 6 minutes 6m 6m 5 minutes 52 seconds 5m 52s 5m 52½s
810.52 13.51m 14 minutes 14m 13½m 13 minutes 31 seconds 13m 31s 13m 30½s
1864.19 31.07m 31 minutes 31m 31m 31 minutes 4 seconds 31m 4s 31m 4s
4287.64 71.46m 1 hour 1h 1h 1 hour 11 minutes 1h 11m 1h 11½m
9861.58 2.74h 3 hours 3h 2½h 2 hours 44 minutes 2h 44m 2h 44½m
22681.64 6.30h 6 hours 6h 6½h 6 hours 18 minutes 6h 18m 6h 18m
52167.76 14.49h 14 hours 14h 14½h 14 hours 29 minutes 14h 29m 14h 29½m
119985.86 1.39d 1 day 1d 1½d 1 day 9 hours 1d 9h 1d 9½h
275967.47 3.19d 3 days 3d 3d 3 days 5 hours 3d 5h 3d 4½h
634725.18 7.35d 1 week 1w 1w 7 days 7d 1w ½d
1459867.91 16.90d 2 weeks 2w 2½w 2 weeks 3 days 2w 3d 2w 3d
3357696.19 38.86d 1 month 1M 1½M 1 month 1 week 1M 1w 1M 1w
7722701.24 89.38d 3 months 3M 3M 2 months 4 weeks 2M 4w 2M 4w
17762212.85 205.58d 7 months 7M 7M 6 months 3 weeks 6M 3w 6M 3½w
40853089.56 1.29y 1 year 1Y 1½Y 1 year 4 months 1Y 4M 1Y 3½M
93962106.00 2.98y 3 years 3Y 3Y 2 years 12 months 2Y 12M 2Y 11½M
216112843.80 6.85y 7 years 7Y 7Y 6 years 10 months 6Y 10M 6Y 10M
497059540.74 15.75y 16 years 16Y 16Y 15 years 9 months 15Y 9M 15Y 9M
This is produced with:
(concat
(format "%12s %10s %10s %6s %7s %21s %7s %s\n"
"Delay (s)" "s2s" "s2s-r" "s2s-ra" "s2s-rah" "s2s-e" "s2s-ea" "s2s-eah" )
(cl-loop for s = 0.45 then (* s 2.3) while (< s (* 365.25 24 3600 22))
concat (format "%12.2f %10s %10s %6s %7s %21s %7s %s\n" s
(seconds-to-string s)
(seconds-to-string s 'readable)
(seconds-to-string s 'readable 'abbrev)
(seconds-to-string s 'readable 'abbrev 'half)
(seconds-to-string s 'expanded)
(seconds-to-string s 'expanded 'abbrev)
(seconds-to-string s 'expanded 'abbrev 'half))))

[-- Attachment #2.1: Type: text/html, Size: 10366 bytes --]
[-- Attachment #2.2: time-data-readable-seconds-2.patch --]
[-- Type: application/octet-stream, Size: 2991 bytes --]
--- time-date.el 2024-06-22 21:51:21
+++ time-date_new.el 2024-07-04 11:16:22
@@ -406,10 +406,62 @@
(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.")
+
;;;###autoload
-(defun seconds-to-string (delay)
- "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 half)
+ "Convert time interval DELAY (in seconds) to a short string.
+By default, the returned string has two decimal precision in the
+smallest unit that is larger than DELAY from the variable
+`seconds-to-string'. If READABLE is non-nil, convert DELAY into
+a readable string, using the information 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 HALF is non-nil, round the smallest unit displayed to
+the nearest half unit."
+ (cond ((> 0 delay) (concat "-" (seconds-to-string (- delay) readable half)))
+ (readable
+ (let* ((stsa seconds-to-string-readable)
+ (expanded (eq readable 'expanded))
+ (round-to (if half 0.5 1))
+ (padding (if abbrev "" " "))
+ here cnt cnt-pre here-pre)
+ (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
+ (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 here-pre nil))))
+ (setq cnt (round (/ (float delay) (nth 3 here)) round-to)))
+ (cl-labels
+ ((unit (cnt here &optional half)
+ (cond (abbrev (car here))
+ ((<= cnt (if half 2 1)) (nth 1 here))
+ (t (nth 2 here)))))
+ (concat
+ (when here-pre
+ (concat (number-to-string cnt-pre) padding
+ (unit cnt-pre here-pre) " "))
+ (let ((c (if half (/ cnt 2) cnt)))
+ (if (> c 0) (number-to-string c) ""))
+ (if (and half (= (mod cnt 2) 1)) "½" "")
+ padding (unit cnt here half))))))
((= 0 delay) "0s")
(t (let ((sts seconds-to-string) here)
(while (and (car (setq here (pop sts)))
[-- Attachment #2.3: Type: text/html, Size: 212 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 15:27 ` JD Smith
@ 2024-07-04 15:59 ` Eli Zaretskii
2024-07-04 17:16 ` JD Smith
2024-07-04 16:36 ` Ihor Radchenko
1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2024-07-04 15:59 UTC (permalink / raw)
To: JD Smith; +Cc: adam, 71572, jonas, eggert
> From: JD Smith <jdtsmith@gmail.com>
> Date: Thu, 4 Jul 2024 11:27:41 -0400
> Cc: Paul Eggert <eggert@cs.ucla.edu>,
> 71572@debbugs.gnu.org,
> Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li
>
> > The second item sounds like a useful feature, so maybe an optional
> > behavior could provide it as well?
>
> I've expanded the call format to (delay &optional readable abbrev half) , with READABLE='expanded an option to get the "1 hour 11 min" expanded format, i.e. one larger and one smaller unit (if appropriate). Patch below.
Still no documentation...
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 15:27 ` JD Smith
2024-07-04 15:59 ` Eli Zaretskii
@ 2024-07-04 16:36 ` Ihor Radchenko
2024-07-04 17:23 ` JD Smith
1 sibling, 1 reply; 31+ messages in thread
From: Ihor Radchenko @ 2024-07-04 16:36 UTC (permalink / raw)
To: JD Smith; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas, Paul Eggert
JD Smith <jdtsmith@gmail.com> writes:
> The latter issue results simply from my truncation of the initial delay column to 1 decimal digit. E.g. .45s * 2.3^3 = 5.47515s, so s2s has it right.
>
>> The second item sounds like a useful feature, so maybe an optional
>> behavior could provide it as well?
>
> I've expanded the call format to (delay &optional readable abbrev half) , with READABLE='expanded an option to get the "1 hour 11 min" expanded format, i.e. one larger and one smaller unit (if appropriate). Patch below.
Have you seen `org-duration-from-minutes'?
In Org mode, we have a rather complex (but flexible) system to customize
the duration format:
(defcustom org-duration-format '(("d" . nil) (special . h:mm))
"Format definition for a duration.
The value can be set to, respectively, the symbols `h:mm:ss' or
`h:mm', which means a duration is expressed as, respectively,
a \"H:MM:SS\" or \"H:MM\" string.
Alternatively, the value can be a list of entries following the
pattern:
(UNIT . REQUIRED?)
UNIT is a unit string, as defined in `org-duration-units'. The
time duration is formatted using only the time components that
are specified here.
Units with a zero value are skipped, unless REQUIRED? is non-nil.
In that case, the unit is always used.
The list can also contain one of the following special entries:
(special . h:mm)
(special . h:mm:ss)
Units shorter than an hour are ignored. The hours and
minutes part of the duration is expressed unconditionally
with H:MM, or H:MM:SS, pattern.
(special . PRECISION)
A duration is expressed with a single unit, PRECISION being
the number of decimal places to show. The unit chosen is the
first one required or with a non-zero integer part. If there
is no such unit, the smallest one is used.
Eventually, if the list contains the symbol `compact', the
duration is expressed in a compact form, without any white space
between units.
For example,
((\"d\" . nil) (\"h\" . t) (\"min\" . t))
means a duration longer than a day is expressed in days, hours
and minutes, whereas a duration shorter than a day is always
expressed in hours and minutes, even when shorter than an hour.
On the other hand, the value
((\"d\" . nil) (\"min\" . nil))
means a duration longer than a day is expressed in days and
minutes, whereas a duration shorter than a day is expressed
entirely in minutes, even when longer than an hour.
The following format
((\"d\" . nil) (special . h:mm))
means that any duration longer than a day is expressed with both
a \"d\" unit and a \"H:MM\" part, whereas a duration shorter than
a day is expressed only as a \"H:MM\" string.
Eventually,
((\"d\" . nil) (\"h\" . nil) (special . 2))
expresses a duration longer than a day as a decimal number, with
a 2-digits fractional part, of \"d\" unit. A duration shorter
than a day uses \"h\" unit instead."
--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 15:59 ` Eli Zaretskii
@ 2024-07-04 17:16 ` JD Smith
2024-07-04 18:06 ` Eli Zaretskii
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-07-04 17:16 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Adam Porter, 71572, jonas, Paul Eggert
> On Jul 4, 2024, at 11:59 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> From: JD Smith <jdtsmith@gmail.com>
>> Date: Thu, 4 Jul 2024 11:27:41 -0400
>> Cc: Paul Eggert <eggert@cs.ucla.edu>,
>> 71572@debbugs.gnu.org,
>> Adam Porter <adam@alphapapa.net>,
>> jonas@bernoul.li
>>
>>> The second item sounds like a useful feature, so maybe an optional
>>> behavior could provide it as well?
>>
>> I've expanded the call format to (delay &optional readable abbrev half) , with READABLE='expanded an option to get the "1 hour 11 min" expanded format, i.e. one larger and one smaller unit (if appropriate). Patch below.
>
> Still no documentation...
It seems sensible to finalize the design before documenting. Where do you think the documentation should go?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 16:36 ` Ihor Radchenko
@ 2024-07-04 17:23 ` JD Smith
2024-07-04 17:57 ` Ihor Radchenko
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-07-04 17:23 UTC (permalink / raw)
To: Ihor Radchenko; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 748 bytes --]
> On Jul 4, 2024, at 12:36 PM, Ihor Radchenko <yantar92@posteo.net> wrote:
>
> Have you seen `org-duration-from-minutes'?
> In Org mode, we have a rather complex (but flexible) system to customize
> the duration format:
I had not seen that function, thanks for pointing it out. Looks useful and rather flexible. Would you be able to re-use the example code I posted earlier to show how it formats various ages with typical configuration?
For activities (which is the package where the conversation about this need started), you can have ages from seconds to many years, so you need something that accommodates that dynamic range well. magit--age was the inspiration, and we're in fact just reusing Jonas' configuration variable.
[-- Attachment #2: Type: text/html, Size: 2894 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 17:23 ` JD Smith
@ 2024-07-04 17:57 ` Ihor Radchenko
0 siblings, 0 replies; 31+ messages in thread
From: Ihor Radchenko @ 2024-07-04 17:57 UTC (permalink / raw)
To: JD Smith; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas, Paul Eggert
JD Smith <jdtsmith@gmail.com> writes:
> I had not seen that function, thanks for pointing it out. Looks useful and rather flexible. Would you be able to re-use the example code I posted earlier to show how it formats various ages with typical configuration?
>
> For activities (which is the package where the conversation about this need started), you can have ages from seconds to many years, so you need something that accommodates that dynamic range well. magit--age was the inspiration, and we're in fact just reusing Jonas' configuration variable.
Delay (s) s-to-s mastodon mastodon (3600s) mast (1yr) s-to-s (rdb) Org Org (frac) s-to-s (rdb=abbrev, half)
0.5 450.00ms 0 sec 0 sec 0 sec 0s 0min 0.01min ½s
1.0 1.03s 1 sec 1 sec 1 sec 1s 0min 0.02min 1s
2.4 2.38s 2 secs 2 secs 2 secs 2s 0min 0.04min 2½s
5.5 5.48s 5 secs 5 secs 5 secs 5s 0min 0.09min 5½s
12.6 12.59s 12 secs 12 secs 12 secs 13s 0min 0.21min 12½s
29.0 28.96s 28 secs 28 secs 28 secs 29s 0min 0.48min 29s
66.6 66.62s 1 min 1 min 1 min 1m 1min 1.11min 1m
153.2 2.55m 2 mins 2 mins 2 mins 3m 2min 2.55min 2½m
352.4 5.87m 5 mins 5 mins 5 mins 6m 5min 5.87min 6m
810.5 13.51m 13 mins 13 mins 13 mins 14m 13min 13.51min 13½m
1864.2 31.07m 31 mins 31 mins 31 mins 31m 31min 31.07min 31m
4287.6 71.46m 1 hour, 11 mins 1 hour 1 hour 1h 1h 11min 1.19h 1h
9861.6 2.74h 2 hours, 44 mins 2 hours 2 hours 3h 2h 44min 2.74h 2½h
22681.6 6.30h 6 hours, 18 mins 6 hours 6 hours 6h 6h 18min 6.30h 6½h
52167.8 14.49h 14 hours, 29 mins 14 hours 14 hours 14h 14h 29min 14.49h 14½h
119985.9 1.39d 1 day, 9 hours 1 day, 9 hours 1 day 1d 1d 9h 19min 1.39d 1½d
275967.5 3.19d 3 days, 4 hours 3 days, 4 hours 3 days 3d 3d 4h 39min 3.19d 3d
634725.2 7.35d 1 week 1 week 1 week 1w 7d 8h 18min 7.35d 1w
1459867.9 16.90d 2 weeks, 2 days 2 weeks, 2 days 2 weeks 2w 16d 21h 31min 16.90d 2½w
3357696.2 38.86d 1 month, 1 week 1 month, 1 week 1 month 1M 1m 8d 20h 41min 1.30m 1½M
7722701.2 89.38d 2 months, 4 weeks 2 months, 4 weeks 2 months 3M 2m 29d 9h 11min 2.98m 3M
17762212.9 205.58d 6 months, 3 weeks 6 months, 3 weeks 6 months 7M 6m 25d 13h 56min 6.85m 7M
40853089.6 1.29y 1 year, 3 months 1 year, 3 months 1 year 1Y 1y 3m 17d 14h 4min 1.29y 1½Y
93962106.0 2.98y 2 years, 11 months 2 years, 11 months 2 years 3Y 2y 11m 27d 35min 2.98y 3Y
216112843.8 6.85y 6 years, 10 months 6 years, 10 months 6 years 7Y 6y 10m 9d 19h 20min 6.85y 7Y
497059540.7 15.75y 15 years, 9 months 15 years, 9 months 15 years 16Y 15y 9m 4d 6h 5min 15.75y 16Y
(concat
(format "%11s %10s %18s %18s %12s %12s %20s %14s %s\n" "Delay (s)" "s-to-s"
"mastodon" "mastodon (3600s)" "mast (1yr)" "s-to-s (rdb)" "Org" "Org (frac)" "s-to-s (rdb=abbrev, half)")
(cl-loop for s = 0.45 then (* s 2.3) while (< s (* 365.25 24 3600 22))
concat (format "%11.1f %10s %18s %18s %12s %12s %20s %14s %s\n" s
(seconds-to-string s)
(car (mastodon-tl--human-duration s))
(car (mastodon-tl--human-duration s 3600))
(car (mastodon-tl--human-duration s (* 365.25 24 3600)))
(seconds-to-string-approximate s t)
(org-duration-from-minutes (/ s 60) '(("y" . nil) ("m" . nil) ("d" . nil) ("h" . nil) ("min" . nil)))
(org-duration-from-minutes (/ s 60) '(("y" . nil) ("m" . nil) ("d" . nil) ("h" . nil) ("min" . nil) (special . 2) compact))
(seconds-to-string-approximate s 'abbrev 'half))))
--
Ihor Radchenko // yantar92,
Org mode contributor,
Learn more about Org mode at <https://orgmode.org/>.
Support Org development at <https://liberapay.com/org-mode>,
or support my work at <https://liberapay.com/yantar92>
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 17:16 ` JD Smith
@ 2024-07-04 18:06 ` Eli Zaretskii
0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2024-07-04 18:06 UTC (permalink / raw)
To: JD Smith; +Cc: adam, 71572, jonas, eggert
> From: JD Smith <jdtsmith@gmail.com>
> Date: Thu, 4 Jul 2024 13:16:32 -0400
> Cc: Paul Eggert <eggert@cs.ucla.edu>,
> 71572@debbugs.gnu.org,
> Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li
>
> > Still no documentation...
>
> It seems sensible to finalize the design before documenting. Where do you think the documentation should go?
"Time Calculations", I'd say.
It should also be mentioned in NEWS.
Thanks.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-04 7:09 ` Paul Eggert
@ 2024-07-06 19:29 ` JD Smith
2024-07-06 21:09 ` Paul Eggert
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-07-06 19:29 UTC (permalink / raw)
To: Paul Eggert; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas
> On Jul 4, 2024, at 3:09 AM, Paul Eggert <eggert@cs.ucla.edu> wrote:
>
> On 7/4/24 06:29, Eli Zaretskii wrote:
>
>> . seconds-to-string lacks the "1 hour 11 min" output format
>
> That format could be confusing with negative delays, e.g., "-1 hour 11 min".
Does anyone share this concern for negative delays? I wouldn't have trouble interpreting that (and negative delays are likely the rare case).
> I'm not sold on the "half" argument; seems like a cuteness rather than a feature that's all that useful (among other things, it assumes Unicode or something like it).
Is this an actual problem, i.e. do we universally avoid unicode in core files? We could drop the HALF, but it's to me quite useful when rounding to just a few weeks or months.
> What's really going on here is that there's an optional argument specifying style and I imagine that style preferences will differ (Mastodon style, etc.).
There are 1000 different ways to come at this; we currently offer 17 "styles": 16 "readable" plus the original seconds-to-string style.
> Is there an ISO or similar standard for this sort of thing?
I've never seen one if so.
Any other thoughts?
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-06 19:29 ` JD Smith
@ 2024-07-06 21:09 ` Paul Eggert
2024-07-11 21:01 ` JD Smith
0 siblings, 1 reply; 31+ messages in thread
From: Paul Eggert @ 2024-07-06 21:09 UTC (permalink / raw)
To: JD Smith; +Cc: Adam Porter, 71572, Eli Zaretskii, jonas
On 7/6/24 21:29, JD Smith wrote:
> do we universally avoid unicode in core files?
Not in comments, but even today it's wise to be cautious about
generating user-visible Unicode characters like "½" when there's a
simple ASCII substitute like ".5". Plus, why stop with ½? Why not also
do ¼ and ¾?
It might be better to have an optional precision argument, defaulting to
0, specifying the number of digits of precision after the decimal point.
Or something like that.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-06 21:09 ` Paul Eggert
@ 2024-07-11 21:01 ` JD Smith
2024-11-30 18:58 ` JD Smith
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-07-11 21:01 UTC (permalink / raw)
To: 71572; +Cc: Adam Porter, Eli Zaretskii, jonas, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 5121 bytes --]
> On Jul 6, 2024, at 5:09 PM, Paul Eggert <eggert@cs.ucla.edu> wrote:
>
> On 7/6/24 21:29, JD Smith wrote:
>> do we universally avoid unicode in core files?
>
> Not in comments, but even today it's wise to be cautious about generating user-visible Unicode characters like "½" when there's a simple ASCII substitute like ".5". Plus, why stop with ½? Why not also do ¼ and ¾?
>
> It might be better to have an optional precision argument, defaulting to 0, specifying the number of digits of precision after the decimal point. Or something like that.
Thanks for the feedback. Attached find an updated patch:
- HALF is dropped.
- PRECISION can now be specified as a whole-number of digits or a float <1.0 (e.g. 0.5)
- NEWS and doc entries.
Users who want ½ can always use precision=0.5 and edit the string after the fact.
Another attached file includes commands to produce and display a simple benchmark, as well as the example output (below).
I see about a 10x performance difference between the standard seconds-to-string and the "bells and whistles" readable version. It's still <35µs per delay for me, so formatting thousands of strings at once should be no problem. Happy to take performance improvement ideas.
Current example:
Delay (s) s2s s2s-r s2s-ra s2s-ra1 s2s-rah s2s-e s2s-ea s2s-ea1 s2s-ea3 s2s-eah
0.000 0s 0 seconds 0s 0s 0s 0 seconds 0s 0s 0s 0s
0.450 450.00ms 0 seconds 0s 0.4s 0.5s 0 seconds 0s 0.4s 0.450s 0.5s
1.035 1.03s 1 second 1s 1s 1s 1 second 1s 1s 1.035s 1s
2.380 2.38s 2 seconds 2s 2.4s 2.5s 2 seconds 2s 2.4s 2.380s 2.5s
5.475 5.48s 5 seconds 5s 5.5s 5.5s 5 seconds 5s 5.5s 5.475s 5.5s
12.593 12.59s 13 seconds 13s 12.6s 12.5s 13 seconds 13s 12.6s 12.593s 12.5s
28.964 28.96s 29 seconds 29s 29s 29s 29 seconds 29s 29s 28.964s 29s
66.616 66.62s 1 minute 1m 1.1m 1m 1 minute 7 seconds 1m 7s 1m 6.6s 1m 6.616s 1m 6.5s
153.217 2.55m 3 minutes 3m 2.6m 2.5m 2 minutes 33 seconds 2m 33s 2m 33.2s 2m 33.217s 2m 33s
352.399 5.87m 6 minutes 6m 5.9m 6m 5 minutes 52 seconds 5m 52s 5m 52.4s 5m 52.399s 5m 52.5s
810.519 13.51m 14 minutes 14m 13.5m 13.5m 13 minutes 31 seconds 13m 31s 13m 30.5s 13m 30.519s 13m 30.5s
1864.193 31.07m 31 minutes 31m 31.1m 31m 31 minutes 4 seconds 31m 4s 31m 4.2s 31m 4.193s 31m 4s
4287.644 71.46m 1 hour 1h 1.2h 1h 1 hour 11 minutes 1h 11m 1h 11.5m 1h 11.461m 1h 11.5m
9861.581 2.74h 3 hours 3h 2.7h 2.5h 2 hours 44 minutes 2h 44m 2h 44.4m 2h 44.360m 2h 44.5m
22681.636 6.30h 6 hours 6h 6.3h 6.5h 6 hours 18 minutes 6h 18m 6h 18m 6h 18.027m 6h 18m
52167.763 14.49h 14 hours 14h 14.5h 14.5h 14 hours 29 minutes 14h 29m 14h 29.5m 14h 29.463m 14h 29.5m
119985.856 1.39d 1 day 1d 1.4d 1.5d 1 day 9 hours 1d 9h 1d 9.3h 1d 9.329h 1d 9.5h
275967.469 3.19d 3 days 3d 3.2d 3d 3 days 5 hours 3d 5h 3d 4.7h 3d 4.658h 3d 4.5h
634725.178 7.35d 1 week 1w 1w 1w 1 week 1w 1w 0.3d 1w 0.346d 1w 0.5d
1459867.909 16.90d 2 weeks 2w 2.4w 2.5w 2 weeks 3 days 2w 3d 2w 2.9d 2w 2.897d 2w 3d
3357696.192 38.86d 1 month 1M 1.3M 1.5M 1 month 1 week 1M 1w 1M 1.2w 1M 1.204w 1M 1w
7722701.241 89.38d 3 months 3M 2.9M 3M 2 months 4 weeks 2M 4w 2M 4.1w 2M 4.073w 2M 4w
17762212.854 205.58d 7 months 7M 6.8M 7M 6 months 3 weeks 6M 3w 6M 3.3w 6M 3.280w 6M 3.5w
40853089.565 1.29y 1 year 1Y 1.3Y 1.5Y 1 year 4 months 1Y 4M 1Y 3.5M 1Y 3.535M 1Y 3.5M
93962105.999 2.98y 3 years 3Y 3Y 3Y 2 years 12 months 2Y 12M 2Y 11.7M 2Y 11.730M 2Y 11.5M
216112843.798 6.85y 7 years 7Y 6.8Y 7Y 6 years 10 months 6Y 10M 6Y 10.2M 6Y 10.180M 6Y 10M
497059540.736 15.75y 16 years 16Y 15.8Y 16Y 15 years 9 months 15Y 9M 15Y 9M 15Y 9.014M 15Y 9M
1143236943.694 36.23y 36 years 36Y 36.2Y 36Y 36 years 3 months 36Y 3M 36Y 2.7M 36Y 2.733M 36Y 2.5M

[-- Attachment #2.1: Type: text/html, Size: 10907 bytes --]
[-- Attachment #2.2: s2s_test.el --]
[-- Type: application/octet-stream, Size: 1794 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 %7s %9s %11s %9s\n"
"Delay (s)" "s2s" "s2s-r" "s2s-ra" "s2s-ra1" "s2s-rah" "s2s-e" "s2s-ea" "s2s-ea1"
"s2s-ea3" "s2s-eah")
(cl-loop for s = 0.0 then (if (zerop s) 0.45 (* s 2.3))
while (< s (* 365.25 24 3600 40))
concat (format "%14.3f %10s %10s %6s %7s %7s %21s %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 '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.3: Type: text/html, Size: 289 bytes --]
[-- Attachment #2.4: 0001-seconds-to-string-new-optional-arguments-for-readabl.patch --]
[-- Type: application/octet-stream, Size: 5345 bytes --]
From 4767735197fba78672e076737a033921637db1a2 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 | 75 ++++++++++++++++++++++++++++++++++++--
3 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3ba3da459bf..1e26c83de6a 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 "2 hours 44 minutes".
+
@node Timers
@section Timers for Delayed Execution
@cindex timers
diff --git a/etc/NEWS b/etc/NEWS
index f10f9ae4d65..38ab21288a3 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 arguments 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..f80aa2f980d 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -409,11 +409,78 @@ 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.")
+
;;;###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 short string.
+By default, the returned string has two decimal precision in the
+smallest unit that is larger than DELAY from the variable
+`seconds-to-string'. If READABLE is non-nil, convert DELAY into
+a readable string, using the information 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 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)
+ (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))
+ (cl-labels
+ ((unit (val here)
+ (cond (abbrev (car here))
+ ((<= (floor val) 1) (nth 1 here))
+ (t (nth 2 here)))))
+ (concat
+ (when here-pre
+ (concat (number-to-string cnt-pre) padding
+ (unit (* cnt-pre round-to) here-pre) " "))
+ (if (and (> digits 0)
+ (> (- cnt-val (floor cnt-val)) 0.))
+ (format dformat cnt-val)
+ (number-to-string (floor cnt-val)))
+ padding (unit cnt-val here))))))
((= 0 delay) "0s")
(t (let ((sts seconds-to-string) here)
(while (and (car (setq here (pop sts)))
--
2.43.0
[-- Attachment #2.5: Type: text/html, Size: 457 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-07-11 21:01 ` JD Smith
@ 2024-11-30 18:58 ` JD Smith
2024-12-07 13:02 ` Eli Zaretskii
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-11-30 18:58 UTC (permalink / raw)
To: 71572; +Cc: Adam Porter, Eli Zaretskii, jonas, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 5579 bytes --]
I was recently reminded of the need for a more capable seconds-to-string.
Anyone have any additional comments on this proposed patch? If not, I'd suggest someone with access merges.
JD
> On Jul 11, 2024, at 5:01 PM, JD Smith <jdtsmith@gmail.com> wrote:
>
>
>
>> On Jul 6, 2024, at 5:09 PM, Paul Eggert <eggert@cs.ucla.edu> wrote:
>>
>> On 7/6/24 21:29, JD Smith wrote:
>>> do we universally avoid unicode in core files?
>>
>> Not in comments, but even today it's wise to be cautious about generating user-visible Unicode characters like "½" when there's a simple ASCII substitute like ".5". Plus, why stop with ½? Why not also do ¼ and ¾?
>>
>> It might be better to have an optional precision argument, defaulting to 0, specifying the number of digits of precision after the decimal point. Or something like that.
>
> Thanks for the feedback. Attached find an updated patch:
>
> - HALF is dropped.
> - PRECISION can now be specified as a whole-number of digits or a float <1.0 (e.g. 0.5)
> - NEWS and doc entries.
>
> Users who want ½ can always use precision=0.5 and edit the string after the fact.
>
> Another attached file includes commands to produce and display a simple benchmark, as well as the example output (below).
>
> I see about a 10x performance difference between the standard seconds-to-string and the "bells and whistles" readable version. It's still <35µs per delay for me, so formatting thousands of strings at once should be no problem. Happy to take performance improvement ideas.
>
> Current example:
>
> Delay (s) s2s s2s-r s2s-ra s2s-ra1 s2s-rah s2s-e s2s-ea s2s-ea1 s2s-ea3 s2s-eah
> 0.000 0s 0 seconds 0s 0s 0s 0 seconds 0s 0s 0s 0s
> 0.450 450.00ms 0 seconds 0s 0.4s 0.5s 0 seconds 0s 0.4s 0.450s 0.5s
> 1.035 1.03s 1 second 1s 1s 1s 1 second 1s 1s 1.035s 1s
> 2.380 2.38s 2 seconds 2s 2.4s 2.5s 2 seconds 2s 2.4s 2.380s 2.5s
> 5.475 5.48s 5 seconds 5s 5.5s 5.5s 5 seconds 5s 5.5s 5.475s 5.5s
> 12.593 12.59s 13 seconds 13s 12.6s 12.5s 13 seconds 13s 12.6s 12.593s 12.5s
> 28.964 28.96s 29 seconds 29s 29s 29s 29 seconds 29s 29s 28.964s 29s
> 66.616 66.62s 1 minute 1m 1.1m 1m 1 minute 7 seconds 1m 7s 1m 6.6s 1m 6.616s 1m 6.5s
> 153.217 2.55m 3 minutes 3m 2.6m 2.5m 2 minutes 33 seconds 2m 33s 2m 33.2s 2m 33.217s 2m 33s
> 352.399 5.87m 6 minutes 6m 5.9m 6m 5 minutes 52 seconds 5m 52s 5m 52.4s 5m 52.399s 5m 52.5s
> 810.519 13.51m 14 minutes 14m 13.5m 13.5m 13 minutes 31 seconds 13m 31s 13m 30.5s 13m 30.519s 13m 30.5s
> 1864.193 31.07m 31 minutes 31m 31.1m 31m 31 minutes 4 seconds 31m 4s 31m 4.2s 31m 4.193s 31m 4s
> 4287.644 71.46m 1 hour 1h 1.2h 1h 1 hour 11 minutes 1h 11m 1h 11.5m 1h 11.461m 1h 11.5m
> 9861.581 2.74h 3 hours 3h 2.7h 2.5h 2 hours 44 minutes 2h 44m 2h 44.4m 2h 44.360m 2h 44.5m
> 22681.636 6.30h 6 hours 6h 6.3h 6.5h 6 hours 18 minutes 6h 18m 6h 18m 6h 18.027m 6h 18m
> 52167.763 14.49h 14 hours 14h 14.5h 14.5h 14 hours 29 minutes 14h 29m 14h 29.5m 14h 29.463m 14h 29.5m
> 119985.856 1.39d 1 day 1d 1.4d 1.5d 1 day 9 hours 1d 9h 1d 9.3h 1d 9.329h 1d 9.5h
> 275967.469 3.19d 3 days 3d 3.2d 3d 3 days 5 hours 3d 5h 3d 4.7h 3d 4.658h 3d 4.5h
> 634725.178 7.35d 1 week 1w 1w 1w 1 week 1w 1w 0.3d 1w 0.346d 1w 0.5d
> 1459867.909 16.90d 2 weeks 2w 2.4w 2.5w 2 weeks 3 days 2w 3d 2w 2.9d 2w 2.897d 2w 3d
> 3357696.192 38.86d 1 month 1M 1.3M 1.5M 1 month 1 week 1M 1w 1M 1.2w 1M 1.204w 1M 1w
> 7722701.241 89.38d 3 months 3M 2.9M 3M 2 months 4 weeks 2M 4w 2M 4.1w 2M 4.073w 2M 4w
> 17762212.854 205.58d 7 months 7M 6.8M 7M 6 months 3 weeks 6M 3w 6M 3.3w 6M 3.280w 6M 3.5w
> 40853089.565 1.29y 1 year 1Y 1.3Y 1.5Y 1 year 4 months 1Y 4M 1Y 3.5M 1Y 3.535M 1Y 3.5M
> 93962105.999 2.98y 3 years 3Y 3Y 3Y 2 years 12 months 2Y 12M 2Y 11.7M 2Y 11.730M 2Y 11.5M
> 216112843.798 6.85y 7 years 7Y 6.8Y 7Y 6 years 10 months 6Y 10M 6Y 10.2M 6Y 10.180M 6Y 10M
> 497059540.736 15.75y 16 years 16Y 15.8Y 16Y 15 years 9 months 15Y 9M 15Y 9M 15Y 9.014M 15Y 9M
> 1143236943.694 36.23y 36 years 36Y 36.2Y 36Y 36 years 3 months 36Y 3M 36Y 2.7M 36Y 2.733M 36Y 2.5M
>
> <s2s_test.el>
> <0001-seconds-to-string-new-optional-arguments-for-readabl.patch>
>
>
[-- Attachment #2: Type: text/html, Size: 12360 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-11-30 18:58 ` JD Smith
@ 2024-12-07 13:02 ` Eli Zaretskii
2024-12-07 17:52 ` JD Smith
0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2024-12-07 13:02 UTC (permalink / raw)
To: JD Smith; +Cc: adam, 71572, jonas, eggert
> From: JD Smith <jdtsmith@gmail.com>
> Date: Sat, 30 Nov 2024 13:58:52 -0500
> Cc: Eli Zaretskii <eliz@gnu.org>,
> Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li,
> Paul Eggert <eggert@cs.ucla.edu>
>
> I was recently reminded of the need for a more capable seconds-to-string.
>
> Anyone have any additional comments on this proposed patch? If not, I'd suggest someone with access
> merges.
A few minor nits below, and then we can install:
> --- 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 "2 hours 44 minutes".
The strings should be in @samp (and then you can drop the quotes).
> +** Time & Date
> +
> ++++
> +*** 'seconds-to-string' includes new arguments to produce human-readable
> +delay strings in a variety of formats, for example "6 months 3 weeks" or
> +"5m 52.5s".
This should have a heading line, which is a single complete sentence.
> +(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.")
Please exp.lain in more detail the format of this list's elements, so
that people could modify it more easily in the future.
> +(defun seconds-to-string (delay &optional readable abbrev precision)
> + "Convert time interval DELAY (in seconds) to a short string.
^^^^^^^^^^^^
Why "short"?
> +By default, the returned string has two decimal precision in the
> +smallest unit that is larger than DELAY from the variable
> +`seconds-to-string'.
I couldn't parse this sentence. "Two decimal precision"? is that a
typo?
Thanks.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-12-07 13:02 ` Eli Zaretskii
@ 2024-12-07 17:52 ` JD Smith
2024-12-07 19:17 ` john muhl
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-12-07 17:52 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Adam Porter, 71572, Jonas Bernoulli, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 592 bytes --]
> On Dec 7, 2024, at 8:02 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>
>> From: JD Smith <jdtsmith@gmail.com>
>> Date: Sat, 30 Nov 2024 13:58:52 -0500
>> Cc: Eli Zaretskii <eliz@gnu.org>,
>> Adam Porter <adam@alphapapa.net>,
>> jonas@bernoul.li,
>> Paul Eggert <eggert@cs.ucla.edu>
>>
>> I was recently reminded of the need for a more capable seconds-to-string.
>>
>> Anyone have any additional comments on this proposed patch? If not, I'd suggest someone with access
>> merges.
>
> A few minor nits below, and then we can install:
Thanks. Updated patch below.
[-- Attachment #2: 0001-seconds-to-string-new-optional-arguments-for-readabl.patch --]
[-- Type: application/octet-stream, Size: 5688 bytes --]
From 99f52d767e67d5ce929a2696749b3db635bd302d 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 | 81 ++++++++++++++++++++++++++++++++++++--
3 files changed, 90 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..60573f814ac 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -409,11 +409,84 @@ 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 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)
+ (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))
+ (cl-labels
+ ((unit (val here)
+ (cond (abbrev (car here))
+ ((<= (floor val) 1) (nth 1 here))
+ (t (nth 2 here)))))
+ (concat
+ (when here-pre
+ (concat (number-to-string cnt-pre) padding
+ (unit (* cnt-pre round-to) here-pre) " "))
+ (if (and (> digits 0)
+ (> (- cnt-val (floor cnt-val)) 0.))
+ (format dformat cnt-val)
+ (number-to-string (floor cnt-val)))
+ padding (unit cnt-val here))))))
((= 0 delay) "0s")
(t (let ((sts seconds-to-string) here)
(while (and (car (setq here (pop sts)))
--
2.45.2
^ permalink raw reply related [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-12-07 17:52 ` JD Smith
@ 2024-12-07 19:17 ` john muhl
2024-12-08 19:52 ` JD Smith
0 siblings, 1 reply; 31+ messages in thread
From: john muhl @ 2024-12-07 19:17 UTC (permalink / raw)
To: JD Smith; +Cc: Adam Porter, 71572, Eli Zaretskii, Jonas Bernoulli, Paul Eggert
JD Smith <jdtsmith@gmail.com> writes:
>> On Dec 7, 2024, at 8:02 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>
>>> From: JD Smith <jdtsmith@gmail.com>
>>> Date: Sat, 30 Nov 2024 13:58:52 -0500
>>> Cc: Eli Zaretskii <eliz@gnu.org>,
>>> Adam Porter <adam@alphapapa.net>,
>>> jonas@bernoul.li,
>>> Paul Eggert <eggert@cs.ucla.edu>
>>>
>>> I was recently reminded of the need for a more capable seconds-to-string.
>>>
>>> Anyone have any additional comments on this proposed patch? If not,
>>> I'd suggest someone with access
>>> merges.
>>
>> A few minor nits below, and then we can install:
>
> Thanks. Updated patch below.
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"
(seconds-to-string 73082924 'expanded nil 2)
;; "2 year 3.79 months"
(seconds-to-string 2511822 'expanded nil 3)
;; "4 week 1.072 day"
I would expect those to output:
1 year 1.9 months
2 years 3.79 months
4 weeks 1.072 days
According to the Chicago Manual of Style all fractional values are
plural, even 1.0, 2.0 &c.
The incorrect “2 year”, “4 week” only happen when PRECISION is
non-nil; e.g.
(seconds-to-string 2511822 'expanded nil)
"4 weeks 1 day"
p.s. There is a missing “is” in the last sentence of the docstring
for seconds-to-string.
^ permalink raw reply [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-12-07 19:17 ` john muhl
@ 2024-12-08 19:52 ` JD Smith
2024-12-08 22:51 ` john muhl
0 siblings, 1 reply; 31+ messages in thread
From: JD Smith @ 2024-12-08 19:52 UTC (permalink / raw)
To: john muhl; +Cc: Adam Porter, 71572, Eli Zaretskii, Jonas Bernoulli, Paul Eggert
[-- Attachment #1: Type: text/plain, Size: 1153 bytes --]
> On Dec 8, 2024, at 4:17 AM, john muhl <jm@pub.pink> wrote:
>
> JD Smith <jdtsmith@gmail.com <mailto:jdtsmith@gmail.com>> writes:
>
>>> On Dec 7, 2024, at 8:02 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>>>
>>>> From: JD Smith <jdtsmith@gmail.com>
>>>> Date: Sat, 30 Nov 2024 13:58:52 -0500
>>>> Cc: Eli Zaretskii <eliz@gnu.org>,
>>>> Adam Porter <adam@alphapapa.net>,
>>>> jonas@bernoul.li,
>>>> Paul Eggert <eggert@cs.ucla.edu>
>>>>
>>>> I was recently reminded of the need for a more capable seconds-to-string.
>>>>
>>>> Anyone have any additional comments on this proposed patch? If not,
>>>> I'd suggest someone with access
>>>> merges.
>>>
>>> A few minor nits below, and then we can install:
>>
>> Thanks. Updated patch below.
>
> 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"
...
> According to the Chicago Manual of Style all fractional values are
> plural, even 1.0, 2.0 &c.
Thanks for this, a case I hadn't tested. Corrected in the attached.

[-- Attachment #2.1: Type: text/html, Size: 6807 bytes --]
[-- Attachment #2.2: 0001-seconds-to-string-new-optional-arguments-for-readabl.patch --]
[-- Type: application/octet-stream, Size: 5860 bytes --]
From c1937c9969955cd040a6587cba9e4d91e79ec78c 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..5d53052a65c 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 round-to) here-pre) " "))
+ (if isfloatp (format dformat cnt-val)
+ (number-to-string (floor cnt-val)))
+ padding
+ (unit cnt-val here isfloatp)))))) ; floats 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: 216 bytes --]
^ permalink raw reply related [flat|nested] 31+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate
2024-12-08 19:52 ` JD Smith
@ 2024-12-08 22:51 ` john muhl
0 siblings, 0 replies; 31+ messages in thread
From: john muhl @ 2024-12-08 22:51 UTC (permalink / raw)
To: JD Smith; +Cc: Adam Porter, 71572, Eli Zaretskii, Jonas Bernoulli, Paul Eggert
JD Smith <jdtsmith@gmail.com> writes:
> On Dec 8, 2024, at 4:17 AM, john muhl <jm@pub.pink> wrote:
>
> JD Smith <jdtsmith@gmail.com> writes:
>
> On Dec 7, 2024, at 8:02 AM, Eli Zaretskii <eliz@gnu.org> wrote:
>
> From: JD Smith <jdtsmith@gmail.com>
> Date: Sat, 30 Nov 2024 13:58:52 -0500
> Cc: Eli Zaretskii <eliz@gnu.org>,
> Adam Porter <adam@alphapapa.net>,
> jonas@bernoul.li,
> Paul Eggert <eggert@cs.ucla.edu>
>
> I was recently reminded of the need for a more capable seconds-to-string.
>
> Anyone have any additional comments on this proposed patch? If not,
> I'd suggest someone with access
> merges.
>
> A few minor nits below, and then we can install:
>
> Thanks. Updated patch below.
>
> 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"
>
> ...
>
> According to the Chicago Manual of Style all fractional values are
> plural, even 1.0, 2.0 &c.
>
> Thanks for this, a case I hadn't tested. Corrected in the attached.
Looks good but the bigger interval is still always singular in the
expanded w/ precision case:
(seconds-to-string 73082924 'expanded nil 2)
;; "2 year 3.79 months"
;; ^ should be years
I got it working with:
@@ -477,7 +477,7 @@ seconds-to-string
(concat
(when here-pre
(concat (number-to-string cnt-pre) padding
- (unit (* cnt-pre round-to) here-pre) " "))
+ (unit (* cnt-pre round-to) here-pre (> cnt-pre 1)) " "))
(if isfloatp (format dformat cnt-val)
(number-to-string (floor cnt-val)))
padding
Thanks again.
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2024-12-08 22:51 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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-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
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).