world-clock: Add user option to control order of entries

* lisp/time.el (world-clock-sort-order): Add user option.
(world-clock--sort-entries): Add auxiliary sorting function.
(world-clock-display): Sort entries based on the new user option.
(Bug#78678)
This commit is contained in:
Jacob S. Gordon 2025-06-02 21:06:37 -04:00 committed by Eli Zaretskii
parent 5bd9fa084d
commit 9c6c7c4bb7
2 changed files with 65 additions and 7 deletions

View file

@ -2193,6 +2193,17 @@ Latin-1 range 0-255. This hard-coded maximum is replaced by
the display of matching vectors as Unicode strings. The default value
is '0xFF' or '255' to preserve the existing behavior.
** Time
*** New user option 'world-clock-sort-order'.
This option controls the order of timezone entries in the 'world-clock'.
By default, no sorting is done, and entries appear in the same order as
in 'world-clock-list'. Any format understood by 'format-time-string'
can be used to specify a key for the sort order, which is updated upon
each refresh. The sort direction can be controlled by using a cons cell
of a format string and a boolean. Alternatively, a sorting function can
be provided directly.
* New Modes and Packages in Emacs 31.1

View file

@ -536,6 +536,36 @@ If the value is t instead of an alist, use the value of
:type 'natnum
:version "28.1")
(defcustom world-clock-sort-order nil
"Sort order of entries in the `world-clock'.
This variable can take a few different forms:
- A nil value indicates no sorting, and the displayed order is the same
as in `world-clock-list'. This is the default.
- A string is taken as a format for `format-time-string', whose output
is used as the sorting key. For example, an ISO 8601 format `\"%FT%T\"'
means entries are sorted in chronological order.
- A cons cell of the form (STRING . BOOL) again provides a format for
`format-time-string'. The boolean part controls the sort direction.
For example, `(\"%FT%T\" . t)' means entries are sorted in reverse
chronological order.
- A function that takes a list of (TIMEZONE LABEL) entries and a TIME as
arguments, and returns another list of entries."
:version "31.1"
:type '(choice (const :tag "No sorting" nil)
(const :tag "Chronological order" "%FT%T")
(const :tag "Reverse chronological order" ("%FT%T" . t))
(const :tag "Time of day order" "%T")
(const :tag "Reverse time of day order" ("%T" . t))
(string :tag "Format string")
(cons :tag "Format string and reverse flag"
(string :tag "Format string")
(boolean :tag "Reverse"))
(function :tag "Sorting function")))
(defface world-clock-label
'((t :inherit font-lock-variable-name-face))
"Face for time zone label in `world-clock' buffer.")
@ -562,15 +592,32 @@ See `world-clock'."
(defvar world-clock--timer nil
"The current world clock timer.")
(defun world-clock--sort-entries (tzlist time)
"Sort TZLIST according to `world-clock-sort-order' at a given TIME."
(pcase world-clock-sort-order
((pred null) tzlist)
((or (and (pred stringp) format)
`(,(and (pred stringp) format) .
,(and (pred booleanp) reverse)))
(sort tzlist
:key (lambda (entry)
(format-time-string format time (car entry)))
:reverse reverse))
((pred functionp) (funcall world-clock-sort-order tzlist time))
(_ (error "Invalid `world-clock-sort-order': `%s'"
world-clock-sort-order))))
(defun world-clock-display (alist)
"Replace current buffer text with times in various zones, based on ALIST."
(let ((inhibit-read-only t)
(buffer-undo-list t)
(now (current-time))
(max-width 0)
result fmt)
"Replace current buffer text with times in various zones, based on ALIST.
The entries are ordered according to `world-clock-sort-order'."
(let* ((inhibit-read-only t)
(buffer-undo-list t)
(now (current-time))
(zones (world-clock--sort-entries alist now))
(max-width 0)
result fmt)
(erase-buffer)
(dolist (zone alist)
(dolist (zone zones)
(let* ((label (cadr zone))
(width (string-width label)))
(push (cons label