* 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate 2024-12-08 19:52 ` JD Smith @ 2024-12-08 22:51 ` john muhl 2024-12-15 18:07 ` JD Smith 0 siblings, 1 reply; 32+ 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] 32+ messages in thread
* bug#71572: [PATCH] seconds-to-string-approximate 2024-12-08 22:51 ` john muhl @ 2024-12-15 18:07 ` JD Smith 0 siblings, 0 replies; 32+ messages in thread From: JD Smith @ 2024-12-15 18:07 UTC (permalink / raw) To: john muhl; +Cc: Adam Porter, 71572, Eli Zaretskii, Jonas Bernoulli, Paul Eggert [-- 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 --] ^ permalink raw reply related [flat|nested] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ 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; 32+ 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] 32+ messages in thread
end of thread, other threads:[~2024-12-15 18:07 UTC | newest] Thread overview: 32+ 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-12-15 18:07 ` JD Smith 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).