From mboxrd@z Thu Jan  1 00:00:00 1970
Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail
From: Adam Porter <adam@alphapapa.net>
Newsgroups: gmane.emacs.bugs
Subject: bug#71573: Related functions from ts.el
Date: Mon, 17 Jun 2024 01:20:04 -0500
Message-ID: <0f564d69-2d04-49b9-935d-15ed8741c7ba@alphapapa.net>
References: <D91AFAEE-C909-4238-AFF2-226727D4B0F6@gmail.com>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214";
	logging-data="36657"; mail-complaints-to="usenet@ciao.gmane.io"
User-Agent: Mozilla Thunderbird
To: 71573@debbugs.gnu.org
Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Jun 17 08:21:13 2024
Return-path: <bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org>
Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org
Original-Received: from lists.gnu.org ([209.51.188.17])
	by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
	(Exim 4.92)
	(envelope-from <bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org>)
	id 1sJ5jr-0009Jy-D7
	for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 17 Jun 2024 08:21:13 +0200
Original-Received: from localhost ([::1] helo=lists1p.gnu.org)
	by lists.gnu.org with esmtp (Exim 4.90_1)
	(envelope-from <bug-gnu-emacs-bounces@gnu.org>)
	id 1sJ5ji-00011d-L6; Mon, 17 Jun 2024 02:21:02 -0400
Original-Received: from eggs.gnu.org ([2001:470:142:3::10])
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <Debian-debbugs@debbugs.gnu.org>)
 id 1sJ5jg-00011L-Kq
 for bug-gnu-emacs@gnu.org; Mon, 17 Jun 2024 02:21:00 -0400
Original-Received: from debbugs.gnu.org ([2001:470:142:5::43])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)
 (Exim 4.90_1) (envelope-from <Debian-debbugs@debbugs.gnu.org>)
 id 1sJ5jg-0002IT-D1
 for bug-gnu-emacs@gnu.org; Mon, 17 Jun 2024 02:21:00 -0400
Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2)
 (envelope-from <Debian-debbugs@debbugs.gnu.org>) id 1sJ5ji-0007lU-4Q
 for bug-gnu-emacs@gnu.org; Mon, 17 Jun 2024 02:21:02 -0400
X-Loop: help-debbugs@gnu.org
In-Reply-To: <D91AFAEE-C909-4238-AFF2-226727D4B0F6@gmail.com>
Resent-From: Adam Porter <adam@alphapapa.net>
Original-Sender: "Debbugs-submit" <debbugs-submit-bounces@debbugs.gnu.org>
Resent-CC: bug-gnu-emacs@gnu.org
Resent-Date: Mon, 17 Jun 2024 06:21:02 +0000
Resent-Message-ID: <handler.71573.B71573.171860521529789@debbugs.gnu.org>
Resent-Sender: help-debbugs@gnu.org
X-GNU-PR-Message: followup 71573
X-GNU-PR-Package: emacs
X-GNU-PR-Keywords: patch
Original-Received: via spool by 71573-submit@debbugs.gnu.org id=B71573.171860521529789
 (code B ref 71573); Mon, 17 Jun 2024 06:21:02 +0000
Original-Received: (at 71573) by debbugs.gnu.org; 17 Jun 2024 06:20:15 +0000
Original-Received: from localhost ([127.0.0.1]:59330 helo=debbugs.gnu.org)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <debbugs-submit-bounces@debbugs.gnu.org>)
 id 1sJ5iw-0007kO-U6
 for submit@debbugs.gnu.org; Mon, 17 Jun 2024 02:20:15 -0400
Original-Received: from bonobo.banana.relay.mailchannels.net ([23.83.217.22]:58647)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <adam@alphapapa.net>) id 1sJ5is-0007kC-4Z
 for 71573@debbugs.gnu.org; Mon, 17 Jun 2024 02:20:13 -0400
X-Sender-Id: dreamhost|x-authsender|adam@alphapapa.net
Original-Received: from relay.mailchannels.net (localhost [127.0.0.1])
 by relay.mailchannels.net (Postfix) with ESMTP id EC6314284D
 for <71573@debbugs.gnu.org>; Mon, 17 Jun 2024 06:20:05 +0000 (UTC)
Original-Received: from pdx1-sub0-mail-a313.dreamhost.com (unknown [127.0.0.6])
 (Authenticated sender: dreamhost)
 by relay.mailchannels.net (Postfix) with ESMTPA id 842CA427C0
 for <71573@debbugs.gnu.org>; Mon, 17 Jun 2024 06:20:05 +0000 (UTC)
ARC-Seal: i=1; s=arc-2022; d=mailchannels.net; t=1718605205; a=rsa-sha256;
 cv=none;
 b=x8ReSYWmlg8+V0IXhNqRGikRIzXdRKe9AIYd8TziYjbSJtgM1lCBIaydBilH5JyJrphUS0
 49utZOVHOcDcdSOfiO7qR4DIZ86kZre1x0ffPSOP/GyUclutDhBEoxI/uBw1CPI9pntWYl
 NnJh1ETj1VXTz8Hbee7Eops+R5+b7r267STTAsChLLZxrtAkNSxQF770Dhs3m94elGuEnD
 mHT1rtLETD542g0Khc/tNQ/68QmXYSrkaTQa5QY7YbmdYO3gBqWsGyE+sIbcsKCvH9MOrK
 RQjomsEmINfXWm08/2Z5vCMWhfGjIZ4AdfPQuyAqjdVkmH0JhK/zWzQKpCgGPA==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed;
 d=mailchannels.net; s=arc-2022; t=1718605205;
 h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
 to:to:cc:mime-version:mime-version:content-type:content-type:
 content-transfer-encoding:content-transfer-encoding:dkim-signature;
 bh=ZxiXVsF7QMEKEtzTG4MzsMiWpaT84kSc+T8AROwXUlk=;
 b=yPLLd0SfmFmcu2mFtrFaFNvHDwFBQOS7PRQAbTjmGG5xEIGqE4Hx4jEgi2iZO17Lg3r7yl
 VMC0UJ295F+zAM96xLFF6Z1h0K7YumuNR4un8uOedAXfUAsMhuYbOlTRTDpEaTNbH6dRuQ
 JaCtilIUzxSbUSy5EufzW86mL88Fui+6V1mpMS8sixvz2LiCekNuhbNe6W3B+1s72qUlin
 NII+w9IYvawAY5lqnV8WN0LZ4Fxc3TV7dNrzPhEhmkW9MY/PKL1UE/iruba0I54xnuHun9
 EoQc6t6i6Gfd94PsCw66woX+TmIotewQl/n47YvQtR9ti9lY+aJYeVb5OERtYA==
ARC-Authentication-Results: i=1; rspamd-79677bdb95-pq7kx;
 auth=pass smtp.auth=dreamhost smtp.mailfrom=adam@alphapapa.net
X-Sender-Id: dreamhost|x-authsender|adam@alphapapa.net
X-MC-Relay: Junk
X-MailChannels-SenderId: dreamhost|x-authsender|adam@alphapapa.net
X-MailChannels-Auth-Id: dreamhost
X-Little-Invention: 674a568e2bcb89e5_1718605205749_541955930
X-MC-Loop-Signature: 1718605205749:385998355
X-MC-Ingress-Time: 1718605205749
Original-Received: from pdx1-sub0-mail-a313.dreamhost.com (pop.dreamhost.com
 [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384)
 by 100.121.33.136 (trex/6.9.2); Mon, 17 Jun 2024 06:20:05 +0000
Original-Received: from [10.144.73.185] (unknown [172.98.33.227])
 (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)
 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256)
 (No client certificate requested)
 (Authenticated sender: adam@alphapapa.net)
 by pdx1-sub0-mail-a313.dreamhost.com (Postfix) with ESMTPSA id 4W2fr51frcz6F
 for <71573@debbugs.gnu.org>; Sun, 16 Jun 2024 23:20:05 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alphapapa.net;
 s=dreamhost; t=1718605205;
 bh=ZxiXVsF7QMEKEtzTG4MzsMiWpaT84kSc+T8AROwXUlk=;
 h=Date:To:From:Subject:Content-Type:Content-Transfer-Encoding;
 b=Z2iFYTy/5EggbbMrxRctKmfuVQMLPBvtHj6YsAa3VZkcwiPr2isMnOgKzATHfbMmp
 XVhbCqImLBLSjqOUTrhbXXC06ZuJyvdHPEOrEhuCzno9uDepdR4Po/LqBG0CBnMrO1
 rcjIijL10/BmM8FegxyxUpLc/LTxm1yImxarUQ5FNETlJUChVSAF9s0MLmlA82VBI1
 QhVV6h5KvziXnbciSXndB5vaANF7CW9ZMAeiYVimKS19finne6hJjFaoRd8HNBqlXX
 wwllxgw1HeHvoNMWzTNhcFsxbdUxVpmPx0nx9UbpAFIHGA00SJXN9S+8J8TD2S5BnL
 jCDysX7KBvaqw==
Content-Language: en-US
X-BeenThere: debbugs-submit@debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
X-BeenThere: bug-gnu-emacs@gnu.org
List-Id: "Bug reports for GNU Emacs,
 the Swiss army knife of text editors" <bug-gnu-emacs.gnu.org>
List-Unsubscribe: <https://lists.gnu.org/mailman/options/bug-gnu-emacs>,
 <mailto:bug-gnu-emacs-request@gnu.org?subject=unsubscribe>
List-Archive: <https://lists.gnu.org/archive/html/bug-gnu-emacs>
List-Post: <mailto:bug-gnu-emacs@gnu.org>
List-Help: <mailto:bug-gnu-emacs-request@gnu.org?subject=help>
List-Subscribe: <https://lists.gnu.org/mailman/listinfo/bug-gnu-emacs>,
 <mailto:bug-gnu-emacs-request@gnu.org?subject=subscribe>
Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org
Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org
Xref: news.gmane.io gmane.emacs.bugs:287359
Archived-At: <http://permalink.gmane.org/gmane.emacs.bugs/287359>

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)))))