Subject: [PATCH v1] world-clock: Sort entries based on a user-provided function
From: "Jacob S. Gordon" <[email-masked]>
To: [email-masked]
Date: Tue, 03 Jun 2025 02:30:59 -0400
List-Id: <debbugs-submit.debbugs.gnu.org>

Tags: patch

Hello all,

Please find below a feature proposal for sorting entries of
world-clock, and a first draft of a patch attached to this message.

# Motivation

The `world-clock' displays time zone entries in the order they appear
in `world-clock-list'. Usually this is enough, since orders like time
can often be done statically. However, there are scenarios where you
might want a dynamic ordering. For example:

1. You work with people in multiple time zones, and you want entries
to appear in order of proximity to working hours, e.g. in what time
zones are people likely to be responsive right now?

2. Daylight savings can go into effect at [different times]. For
example, eastern Europe and Egypt both shift from UTC+2 in the winter
to UTC+3 in the summer.

| Zone           | DST start            | DST end                  |
|----------------+----------------------+--------------------------|
| eastern Europe | last Sunday of March | last Sunday of October   |
| Egypt          | last Friday of April | last Thursday of October |

For a period in the spring, local time in Egypt (UTC+2) is earlier
than eastern Europe (UTC+3). Depending on the year, DST in Egypt may
end earlier or later than in eastern Europe.

In years like 2025 & 2026 where the last Thursday of October is after
the last Sunday of October, there’s no static ordering of the zones
that works all year (bracket contents are UTC offsets at a vertical
time slice):

#+begin_src text
time             |< ~April             ~November >|
Egypt  ---(+2)-----(+2)-|-(+3)--------(+3)---(+3)-|-(+2)-----
Europe ---(+2)---|-(+3)---(+3)--------(+3)-|-(+2)---(+2)-----
#+end_src

In other years like 2027, a consistent ordering is possible:

#+begin_src text
time             |< ~April             ~November >|
Egypt  ---(+2)-----(+2)-|-(+3)--------(+3)-|-(+2)---(+2)-----
Europe ---(+2)---|-(+3)---(+3)--------(+3)---(+3)-|-(+2)-----
#+end_src

[different times]
https://en.wikipedia.org/wiki/Daylight_saving_time_by_country

# Possible Implementation

A simple way to do this is with a `world-clock-sort-function' custom
variable that sorts the entries in `world-clock-display'.

#+begin_src emacs-lisp
(setopt world-clock-sort-function 'my/world-clock-sort-function)
#+end_src

You could then accomplish each of the orderings mentioned earlier:

1. Time zones appear in order of how far they are from the middle of
the workday:

#+begin_src emacs-lisp
(defun my/world-clock-sort-function (tzlist)
  "Sort on proximity of local time to work hours."
  (let* ((now (current-time))
         (start 9)
         (end  17)
         (mid (/ (+ start end) 2.0)))
    (sort tzlist
          :key (lambda (entry)
                 (pcase-let* ((zone (car entry))
                              (`(,s ,m ,h _) (decode-time now zone))
                              (hour (+ h (/ m 60.0) (/ s 3600.0)))
                              (diff (min (abs (- hour (- mid 24)))
                                         (abs (- hour mid))
                                         (abs (- hour (+ mid 24))))))
                   diff)))))
#+end_src

2. Sort on an ISO-8601 timestamp so zones are always in ascending
order:

#+begin_src emacs-lisp
(defun my/world-clock-sort-function (tzlist)
  "Sort on local ISO-8601 timestamps."
  (let ((now (current-time)))
    (sort tzlist
          :key (lambda (entry)
                 (let ((zone (car entry)))
                   (format-time-string "%FT%T" now zone))))))
#+end_src

Note that in the current patch there’s a small race condition between
recording the current time in `world-clock-display' and sorting the
list.

Any thoughts on this idea and the interface?

Thanks,

-- 
Jacob S. Gordon
[email-masked]

=========================

Please avoid sending me HTML emails and MS Office documents.
https://useplaintext.email/#etiquette
https://www.gnu.org/philosophy/no-word-attachments.html

In GNU Emacs 30.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.49,
cairo version 1.18.4)
System Description: Arch Linux

Configured using:
 'configure --with-pgtk --sysconfdir=/etc --prefix=/usr
 --libexecdir=/usr/lib --localstatedir=/var --disable-build-details
 --with-cairo --with-harfbuzz --with-libsystemd --with-modules
 --with-native-compilation=aot --with-tree-sitter 'CFLAGS=-march=x86-64
 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=3
 -Wformat -Werror=format-security -fstack-clash-protection
 -fcf-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -g
 -ffile-prefix-map=/build/emacs/src=/usr/src/debug/emacs -flto=auto'
 'LDFLAGS=-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro
 -Wl,-z,now -Wl,-z,pack-relative-relocs -flto=auto'
 'CXXFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions
 -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -fno-omit-frame-pointer
 -mno-omit-leaf-frame-pointer -Wp,-D_GLIBCXX_ASSERTIONS -g
 -ffile-prefix-map=/build/emacs/src=/usr/src/debug/emacs -flto=auto''

From 0d293d92d81d1086a63942734fa067c4f0ca0bfe Mon Sep 17 00:00:00 2001
From: "Jacob S. Gordon" <[email-masked]>
Date: Mon, 2 Jun 2025 21:06:37 -0400
Subject: [PATCH v1] world-clock: Sort entries based on a user-provided
 function

The 'world-clock' displays time zone entries in the order they appear in
'world-clock-list' (or a default if unset).  We introduce a custom
variable 'world-clock-sort-function' to generate an order on each update
of 'world-clock-display'.  The default value of 'identity' preserves the
existing behavior.

* lisp/time.el (world-clock-sort-function, world-clock-display):

Add custom variable 'world-clock-sort-function', and sort time zone
entries in 'world-clock-display' according to it.
---
 etc/NEWS     | 10 ++++++++++
 lisp/time.el | 15 +++++++++++++--
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 01ed372a2da..001609c5b21 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2120,6 +2120,16 @@ You can now use the mouse wheel to scroll the calendar by 3 months.
 With the shift modifier, it scrolls by one month.  With the meta
 modifier, it scrolls by year.
 
+
+** Time
+
+*** New user option 'world-clock-sort-function'
+
+The order of time zone entries in the 'world-clock' is updated according
+to the 'world-clock-sort-function'.  By default, no sorting is done and
+entries appear according to their order in 'world-clock-list' (or a
+default if unset).
+
 * New Modes and Packages in Emacs 31.1
 
 ** New minor mode 'delete-trailing-whitespace-mode'.
diff --git a/lisp/time.el b/lisp/time.el
index fd66775c879..2456ff279e4 100644
--- a/lisp/time.el
+++ b/lisp/time.el
@@ -536,6 +536,15 @@ world-clock-timer-second
   :type 'natnum
   :version "28.1")
 
+(defcustom world-clock-sort-function 'identity
+  "Function to sort time zone entries upon each `world-clock-display'.
+It should take a list of `(TIMEZONE LABEL)' entries and return the same.
+
+This function will be called with `world-clock-list' if set, otherwise
+with a default."
+  :version "31.1"
+  :type '(function :tag "Sorting function"))
+
 (defface world-clock-label
   '((t :inherit font-lock-variable-name-face))
   "Face for time zone label in `world-clock' buffer.")
@@ -563,14 +572,16 @@ world-clock--timer
   "The current world clock timer.")
 
 (defun world-clock-display (alist)
-  "Replace current buffer text with times in various zones, based on ALIST."
+  "Replace current buffer text with times in various zones, based on ALIST.
+The displayed entries are sorted according to `world-clock-sort-function'."
   (let ((inhibit-read-only t)
 	(buffer-undo-list t)
 	(now (current-time))
+	(sorted-zones (funcall world-clock-sort-function alist))
 	(max-width 0)
 	result fmt)
     (erase-buffer)
-    (dolist (zone alist)
+    (dolist (zone sorted-zones)
       (let* ((label (cadr zone))
 	     (width (string-width label)))
 	(push (cons label

base-commit: 50e3bce315eb499917c1d208f87f84315f1a8b4e
-- 
Jacob S. Gordon
[email-masked]

=========================

Please avoid sending me HTML emails and MS Office documents.
https://useplaintext.email/#etiquette
https://www.gnu.org/philosophy/no-word-attachments.html