unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: JD Smith <jdtsmith@gmail.com>
To: john muhl <jm@pub.pink>
Cc: Adam Porter <adam@alphapapa.net>,
	71572@debbugs.gnu.org, Eli Zaretskii <eliz@gnu.org>,
	Jonas Bernoulli <jonas@bernoul.li>,
	Paul Eggert <eggert@cs.ucla.edu>
Subject: bug#71572: [PATCH] seconds-to-string-approximate
Date: Sun, 15 Dec 2024 13:07:52 -0500	[thread overview]
Message-ID: <1CCC10A3-F76D-477C-9E04-AA0903853EAC@gmail.com> (raw)
In-Reply-To: <871pyhu6t4.fsf@pub.pink>

[-- Attachment #1: Type: text/plain, Size: 6221 bytes --]


>> On Dec 8, 2024, at 4:17 AM, john muhl <jm@pub.pink> wrote:
>> 
>> Thanks for working on this. I gave a quick try and noticed some
>> amounts aren’t pluralized how I expect; e.g.
>> 
>>  (seconds-to-string 36541462 'expanded nil 1)
>>  ;; "1 year 1.9 month"

Thanks again.  This was another small bug in the units for the first expanded term.  Corrected (final?) patch attached.  Could you please give another quick test?

For posterity I also include below a more complete test script and its output (see new column s2s-e1).

++
     Delay (s)        s2s       s2s-r  s2s-ra  s2s-ra1  s2s-rah                  s2s-e                   s2s-e1   s2s-ea    s2s-ea1      s2s-ea3    s2s-eah
         0.000         0s   0 seconds      0s       0s       0s              0 seconds                0 seconds       0s         0s           0s         0s
         0.450   450.00ms   0 seconds      0s     0.4s     0.5s              0 seconds              0.4 seconds       0s       0.4s       0.450s       0.5s
         0.855   855.00ms    1 second      1s     0.9s       1s               1 second              0.9 seconds       1s       0.9s       0.855s         1s
         1.624      1.62s   2 seconds      2s     1.6s     1.5s              2 seconds              1.6 seconds       2s       1.6s       1.624s       1.5s
         3.087      3.09s   3 seconds      3s     3.1s       3s              3 seconds              3.1 seconds       3s       3.1s       3.087s         3s
         5.864      5.86s   6 seconds      6s     5.9s       6s              6 seconds              5.9 seconds       6s       5.9s       5.864s         6s
        11.142     11.14s  11 seconds     11s    11.1s      11s             11 seconds             11.1 seconds      11s      11.1s      11.142s        11s
        21.171     21.17s  21 seconds     21s    21.2s      21s             21 seconds             21.2 seconds      21s      21.2s      21.171s        21s
        40.224     40.22s  40 seconds     40s    40.2s      40s             40 seconds             40.2 seconds      40s      40.2s      40.224s        40s
        76.426     76.43s    1 minute      1m     1.3m     1.5m    1 minute 16 seconds    1 minute 16.4 seconds   1m 16s   1m 16.4s   1m 16.426s   1m 16.5s
       145.209      2.42m   2 minutes      2m     2.4m     2.5m   2 minutes 25 seconds   2 minutes 25.2 seconds   2m 25s   2m 25.2s   2m 25.209s     2m 25s
       275.898      4.60m   5 minutes      5m     4.6m     4.5m   4 minutes 36 seconds   4 minutes 35.9 seconds   4m 36s   4m 35.9s   4m 35.898s     4m 36s
       524.206      8.74m   9 minutes      9m     8.7m     8.5m   8 minutes 44 seconds   8 minutes 44.2 seconds   8m 44s   8m 44.2s   8m 44.206s     8m 44s
       995.992     16.60m  17 minutes     17m    16.6m    16.5m  16 minutes 36 seconds    16 minutes 36 seconds  16m 36s    16m 36s  16m 35.992s    16m 36s
      1892.384     31.54m  32 minutes     32m    31.5m    31.5m  31 minutes 32 seconds  31 minutes 32.4 seconds  31m 32s  31m 32.4s  31m 32.384s  31m 32.5s
      3595.530     59.93m  60 minutes     60m    59.9m      60m  59 minutes 56 seconds  59 minutes 55.5 seconds  59m 56s  59m 55.5s  59m 55.530s  59m 55.5s
      6831.507      1.90h     2 hours      2h     1.9h       2h      1 hour 54 minutes      1 hour 53.9 minutes   1h 54m   1h 53.9m   1h 53.858m     1h 54m
     12979.864      3.61h     4 hours      4h     3.6h     3.5h     3 hours 36 minutes     3 hours 36.3 minutes   3h 36m   3h 36.3m   3h 36.331m   3h 36.5m
     24661.741      6.85h     7 hours      7h     6.9h       7h     6 hours 51 minutes       6 hours 51 minutes   6h 51m     6h 51m   6h 51.029m     6h 51m
     46857.308     13.02h    13 hours     13h      13h      13h      13 hours 1 minute        13 hours 1 minute   13h 1m     13h 1m   13h 0.955m     13h 1m
     89028.885     24.73h       1 day      1d       1d       1d           1 day 1 hour          1 day 0.7 hours    1d 1h    1d 0.7h    1d 0.730h    1d 0.5h
    169154.881      1.96d      2 days      2d       2d       2d         1 day 23 hours           1 day 23 hours   1d 23h     1d 23h   1d 22.987h     1d 23h
    321394.273      3.72d      4 days      4d     3.7d     3.5d        3 days 17 hours        3 days 17.3 hours   3d 17h   3d 17.3h   3d 17.276h   3d 17.5h
    610649.119      7.07d      1 week      1w       1w       1w                 1 week          1 week 0.1 days       1w    1w 0.1d    1w 0.068d         1w
   1160233.326     13.43d     2 weeks      2w     1.9w       2w          1 week 6 days          1 week 6.4 days    1w 6d    1w 6.4d    1w 6.429d    1w 6.5d
   2204443.319     25.51d     4 weeks      4w     3.6w     3.5w         3 weeks 5 days         3 weeks 4.5 days    3w 5d    3w 4.5d    3w 4.514d    3w 4.5d
   4188442.306     48.48d    2 months      2M     1.6M     1.5M        1 month 3 weeks        1 month 2.6 weeks    1M 3w    1M 2.6w    1M 2.577w    1M 2.5w
   7958040.381     92.11d    3 months      3M       3M       3M               3 months       3 months 0.1 weeks       3M    3M 0.1w    3M 0.114w         3M
  15120276.725    175.00d    6 months      6M     5.7M     5.5M       5 months 3 weeks       5 months 3.3 weeks    5M 3w    5M 3.3w    5M 3.260w    5M 3.5w
  28728525.777    332.51d   11 months     11M    10.9M      11M      10 months 4 weeks        10 months 4 weeks   10M 4w     10M 4w   10M 4.020w     10M 4w
  54584198.976      1.73y     2 years      2Y     1.7Y     1.5Y        1 year 9 months        1 year 8.8 months    1Y 9M    1Y 8.8M    1Y 8.756M      1Y 9M
 103709978.054      3.29y     3 years      3Y     3.3Y     3.5Y       3 years 3 months       3 years 3.4 months    3Y 3M    3Y 3.4M    3Y 3.437M    3Y 3.5M
 197048958.302      6.24y     6 years      6Y     6.2Y       6Y       6 years 3 months       6 years 2.9 months    6Y 3M    6Y 2.9M    6Y 2.931M      6Y 3M
 374393020.774     11.86y    12 years     12Y    11.9Y      12Y     11 years 10 months     11 years 10.4 months  11Y 10M  11Y 10.4M  11Y 10.369M  11Y 10.5M
 711346739.471     22.54y    23 years     23Y    22.5Y    22.5Y      22 years 7 months      22 years 6.5 months   22Y 7M   22Y 6.5M   22Y 6.500M   22Y 6.5M







[-- Attachment #2.1: Type: text/html, Size: 14024 bytes --]

[-- Attachment #2.2: 0001-seconds-to-string-new-optional-arguments-for-readabl.patch --]
[-- Type: application/octet-stream, Size: 5854 bytes --]

From e72354cdf2bc0394890c42a28fe8e892a0e6f9a8 Mon Sep 17 00:00:00 2001
From: JD Smith <93749+jdtsmith@users.noreply.github.com>
Date: Thu, 11 Jul 2024 16:24:17 -0400
Subject: [PATCH] seconds-to-string: new optional arguments for readable
 strings

---
 doc/lispref/os.texi        |  6 +++
 etc/NEWS                   |  7 ++++
 lisp/calendar/time-date.el | 82 ++++++++++++++++++++++++++++++++++++--
 3 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3ba3da459bf..ac9d5acdf3d 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -2155,6 +2155,12 @@ Time Calculations
 structure.  For instance, the 120th day in 2004 is April 29th.
 @end defun
 
+@defun seconds-to-string delay &optional readable abbrev precision
+Return a string describing a given @var{delay} (in seconds).  Optional
+arguments can be used to configure a human readable delay using various
+formats.  For example, a delay of 9861.5 seconds with @var{readable} set
+to the symbol @code{expanded} returns @samp{2 hours 44 minutes}.
+
 @node Timers
 @section Timers for Delayed Execution
 @cindex timers
diff --git a/etc/NEWS b/etc/NEWS
index f10f9ae4d65..1fd2a9404bb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -30,6 +30,13 @@ applies, and please also update docstrings as needed.
 \f
 * Changes in Emacs 31.1
 
+** Time & Date
+
++++
+*** 'seconds-to-string' includes new formatting options.
+Options are provided to produce human-readable delay strings in a
+variety of formats, for example "6 months 3 weeks" or "5m 52.5s".
+
 \f
 * Editing Changes in Emacs 31.1
 
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el
index eca80f1e8b6..c3206d1aa54 100644
--- a/lisp/calendar/time-date.el
+++ b/lisp/calendar/time-date.el
@@ -409,11 +409,85 @@ seconds-to-string
         (list (* 3600 24 400) "d" (* 3600.0 24.0))
         (list nil "y" (* 365.25 24 3600)))
   "Formatting used by the function `seconds-to-string'.")
+
+(defvar seconds-to-string-readable
+  `(("Y" "year"   "years"   ,(round (* 60 60 24 365.2425)))
+    ("M" "month"  "months"  ,(round (* 60 60 24 30.436875)))
+    ("w" "week"   "weeks"   ,(* 60 60 24 7))
+    ("d" "day"    "days"    ,(* 60 60 24))
+    ("h" "hour"   "hours"   ,(* 60 60))
+    ("m" "minute" "minutes" 60)
+    ("s" "second" "seconds" 1))
+  "Formatting used by the function `seconds-to-string' with READABLE set.
+The format is an alist, with string keys ABBREV-UNIT, and elements like:
+
+  (ABBREV-UNIT UNIT UNIT-PLURAL SECS)
+
+where UNIT is a unit of time, ABBREV-UNIT is the abreviated form of
+UNIT, UNIT-PLURAL is the plural form of UNIT, and SECS is the number of
+seconds per UNIT.")
+
 ;;;###autoload
-(defun seconds-to-string (delay)
-  ;; FIXME: There's a similar (tho fancier) function in mastodon.el!
-  "Convert the time interval in seconds to a short string."
-  (cond ((> 0 delay) (concat "-" (seconds-to-string (- delay))))
+(defun seconds-to-string (delay &optional readable abbrev precision)
+  "Convert time interval DELAY (in seconds) to a string.
+By default, the returned string is formatted as a float in the smallest
+unit from the variable `seconds-to-string' that is longer than DELAY,
+and a precision of two.  If READABLE is non-nil, convert DELAY into a
+readable string, using the information provided in the variable
+`seconds-to-string-readable'.  If it is the symbol `expanded', use two
+units to describe DELAY, if appropriate.  E.g. \"1 hour 32 minutes\".
+If ABBREV is non-nil, abbreviate the readable units.  If PRECISION is a
+whole number, round the value associated with the smallest displayed
+unit to that many digits after the decimal.  If it is a non-negative
+float less than 1.0, round to that value."
+  (cond ((< delay 0)
+	 (concat "-" (seconds-to-string (- delay) readable precision)))
+        (readable
+         (let* ((stsa seconds-to-string-readable)
+		(expanded (eq readable 'expanded))
+		digits
+		(round-to (cond ((wholenump precision)
+				 (setq digits precision)
+				 (expt 10 (- precision)))
+				((and (floatp precision) (< precision 1.))
+				 (setq digits (- (floor (log precision 10))))
+				 precision)
+				(t (setq digits 0) 1)))
+		(dformat (if (> digits 0) (format "%%0.%df" digits)))
+		(padding (if abbrev "" " "))
+		here cnt cnt-pre here-pre cnt-val isfloatp)
+	   (if (= (round delay round-to) 0)
+	       (format "0%s" (if abbrev "s" " seconds"))
+	     (while (and (setq here (pop stsa)) stsa
+			 (< (/ delay (nth 3 here)) 1)))
+	     (or (and
+		  expanded stsa 	; smaller unit remains
+		  (progn
+		    (setq
+		     here-pre here here (car stsa)
+		     cnt-pre (floor (/ (float delay) (nth 3 here-pre)))
+		     cnt (round
+			  (/ (- (float delay) (* cnt-pre (nth 3 here-pre)))
+			     (nth 3 here))
+			  round-to))
+		    (if (> cnt 0) t (setq cnt cnt-pre here here-pre here-pre nil))))
+		 (setq cnt (round (/ (float delay) (nth 3 here)) round-to)))
+	     (setq cnt-val (* cnt round-to)
+                   isfloatp (and (> digits 0)
+			            (> (- cnt-val (floor cnt-val)) 0.)))
+	     (cl-labels
+		 ((unit (val here &optional plural)
+		    (cond (abbrev (car here))
+			  ((and (not plural) (<= (floor val) 1)) (nth 1 here))
+			  (t (nth 2 here)))))
+	       (concat
+		(when here-pre
+		  (concat (number-to-string cnt-pre) padding
+			  (unit cnt-pre here-pre) " "))
+		(if isfloatp (format dformat cnt-val)
+                  (number-to-string (floor cnt-val)))
+                padding
+                (unit cnt-val here isfloatp)))))) ; float formats are always plural
         ((= 0 delay) "0s")
         (t (let ((sts seconds-to-string) here)
              (while (and (car (setq here (pop sts)))
-- 
2.45.2


[-- Attachment #2.3: Type: text/html, Size: 242 bytes --]

[-- Attachment #2.4: s2s_test.el --]
[-- Type: application/octet-stream, Size: 1863 bytes --]

(require 'cl-lib)

(defun s2s/example ()
  (interactive)
  (with-temp-buffer-window "s2s/example" nil nil
    (princ
     (concat
      (format "%14s %10s  %10s  %6s  %7s  %7s  %21s  %23s  %7s  %9s  %11s  %9s\n"
	      "Delay (s)" "s2s" "s2s-r" "s2s-ra" "s2s-ra1" "s2s-rah" "s2s-e" "s2s-e1" "s2s-ea" "s2s-ea1"
	      "s2s-ea3" "s2s-eah")
      (cl-loop for s = 0.0 then (if (zerop s) 0.45 (* s 1.9))
	       while (< s (* 365.25 24 3600 40))
	       concat (format "%14.3f %10s  %10s  %6s  %7s  %7s  %21s  %23s  %7s  %9s  %11s  %9s\n" s
			      (seconds-to-string s)
			      (seconds-to-string s 'readable)
			      (seconds-to-string s 'readable 'abbrev)
			      (seconds-to-string s 'readable 'abbrev 1)
			      (seconds-to-string s 'readable 'abbrev 0.5)
			      (seconds-to-string s 'expanded)
			      (seconds-to-string s 'expanded nil 1)
			      (seconds-to-string s 'expanded 'abbrev)
			      (seconds-to-string s 'expanded 'abbrev 1)
			      (seconds-to-string s 'expanded 'abbrev 3)
			      (seconds-to-string s 'expanded 'abbrev 0.5)))))))

(defun s2s/benchmark ()
  (interactive)
  (let* ((ndelays 100000)
	 (delays (cl-loop for i from 1 to ndelays
			  with max = (* 365.25 24 3600 40)
			  collect (cl-random max)))
	 (bsmpl (benchmark-run nil
		  (cl-loop for d in delays
			   do (seconds-to-string d))))
	 (brdbl (benchmark-run nil
		  (cl-loop for d in delays
			   do (seconds-to-string d t t 0.1)))))
    (with-temp-buffer-window "s2s/benchmarks" nil nil
      (princ "seconds-to-string benchmarks\n")
      (princ (format "  default timing: %0.2fµs\n\t%S\n"
		     (/ (car bsmpl) ndelays 1e-6) bsmpl))
      (princ (format " readable timing: %0.2fµs\n\t%S\n\n"
		     (/ (car brdbl) ndelays 1e-6) brdbl))
      (princ (format "readable/default: %0.2f\n" (/ (car brdbl) (car bsmpl)))))))

[-- Attachment #2.5: Type: text/html, Size: 246 bytes --]

  reply	other threads:[~2024-12-15 18:07 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-06-15 17:24 bug#71573: [PATCH] seconds-to-string-approximate JD Smith
2024-06-15 17:36 ` Eli Zaretskii
2024-06-17  6:20 ` bug#71573: Related functions from ts.el Adam Porter
2024-06-22 10:55   ` Stefan Kangas
2024-06-22 21:54     ` Adam Porter
2024-06-22  8:45 ` bug#71572: [PATCH] seconds-to-string-approximate Eli Zaretskii
2024-06-22 21:56   ` Adam Porter
2024-06-22 23:42   ` Paul Eggert
2024-06-23  2:16     ` JD Smith
2024-07-04  5:29       ` Eli Zaretskii
2024-07-04  6:04         ` Eli Zaretskii
2024-07-04  7:09         ` Paul Eggert
2024-07-06 19:29           ` JD Smith
2024-07-06 21:09             ` Paul Eggert
2024-07-11 21:01               ` JD Smith
2024-11-30 18:58                 ` JD Smith
2024-12-07 13:02                   ` Eli Zaretskii
2024-12-07 17:52                     ` JD Smith
2024-12-07 19:17                       ` john muhl
2024-12-08 19:52                         ` JD Smith
2024-12-08 22:51                           ` john muhl
2024-12-15 18:07                             ` JD Smith [this message]
2024-12-15 23:26                               ` john muhl
2024-07-04 15:27         ` JD Smith
2024-07-04 15:59           ` Eli Zaretskii
2024-07-04 17:16             ` JD Smith
2024-07-04 18:06               ` Eli Zaretskii
2024-07-04 16:36           ` Ihor Radchenko
2024-07-04 17:23             ` JD Smith
2024-07-04 17:57               ` Ihor Radchenko
2024-06-23  5:13     ` Eli Zaretskii
2024-07-03 20:32       ` JD Smith
2024-07-04  5:29         ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1CCC10A3-F76D-477C-9E04-AA0903853EAC@gmail.com \
    --to=jdtsmith@gmail.com \
    --cc=71572@debbugs.gnu.org \
    --cc=adam@alphapapa.net \
    --cc=eggert@cs.ucla.edu \
    --cc=eliz@gnu.org \
    --cc=jm@pub.pink \
    --cc=jonas@bernoul.li \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this 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).