Here is a patch for timestamp sortation issues in todo-list, as documented in the following recent threads:
[BUG] Agenda sortation generally ignores time-of-day https://list.orgmode.org/caku+9yxmuxgzg_lxosbgudkb6rtsenmtnctpnxw5z8kbbwf...@mail.gmail.com/ Sorting a TODO agenda across multiple files https://orgmode.org/list/16ea261a-b94c-43b2-91e0-a1ff01d0b...@kuba-orlik.name For ease of discussion, here is the exposition in the patch msg: Headline sortation in agenda views was originally done by generating the agenda text, then inspecting text properties like 'ts-date and 'time-of-day therein. The agenda text generation fully populates these values in agenda view, but not in todo-list view. As a result, todo-list only partially respects `org-agenda-sorting-strategy'; in particular, when considering things like SCHEDULED timestamps for the scheduled-up/down strategies, it discards time-of-day information and instead ends up using other parameters like filename or order of appearance in file. See the linked messages below for simple examples. This change uses the full timestamp, not just the date, and retrieves it by inspecting the headline in its original file, instead of relying on the population of text properties. This is probably a little more computationally expensive -- we do this inspection step during *every* call to the comparison function, instead of caching its value in a text property. In my personal experience using this across about 50 agenda files containing a total of about 500 headlines, this has not resulted in any noticeable lag. In addition, I felt it was prudent to make a change to this function, which is only ever called in the context of agenda sorting, rather than to redesign and repair the assignment of text properties that happens in org.el:org-scan-tags. A more proper solution would be to make a fix there. As mentioned, I believe the root cause to be that some properties (like time-of-day) are assigned in the weekly/daily agenda view and not in any of the other views, and surely `org-agenda-sorting-strategy' is not the only functionality affected by this discrepancy. PTAL, Charles
From 9d2656dcee19af9f8231aeda3da7c755ad0a6a4d Mon Sep 17 00:00:00 2001 From: Charles Tam <m...@charlest.net> Date: Thu, 17 Apr 2025 15:19:48 -0400 Subject: [PATCH] lisp/org-agenda.el: Account for timestamps in agenda sort methods * org-agenda.el (org-cmp-ts, org-agenda-sorting-strategy): Account for full datetime when comparing timestamps. (org-cmp-ts): When figuring out what timestamp to use for headline comparison, visit the headline directly. The original behavior is to rely on text properties that do not fully populate, or populate with truncated values, in todo-list mode. (This is why sorting-strategy may already behave correctly in the main agenda view.) (org-agenda-sorting-strategy): Update docstring to reflect new behavior. * testing/lisp/test-org-agenda.el (todo-list-timestamp-sort): New unit test covering this behavior. (test-org-agenda/todo-list-timestamp-sort): With some added test data, checks that scheduled-up respects timestamps by interweaving the scheduled headlines from two agenda files, one of which is in unsorted order. * testing/examples/agenda-file3.org: New unit test data. Headline sortation in agenda views was originally done by generating the agenda text, then inspecting text properties like 'ts-date and 'time-of-day therein. The agenda text generation fully populates these values in agenda view, but not in todo-list view. As a result, todo-list only partially respects `org-agenda-sorting-strategy'; in particular, when considering things like SCHEDULED timestamps for the scheduled-up/down strategies, it discards time-of-day information and instead ends up using other parameters like filename or order of appearance in file. See the linked messages below for simple examples. This change uses the full timestamp, not just the date, and retrieves it by inspecting the headline in its original file, instead of relying on the population of text properties. This is probably a little more computationally expensive -- we do this inspection step during *every* call to the comparison function, instead of caching its value in a text property. In my personal experience using this across about 50 agenda files containing a total of about 500 headlines, this has not resulted in any noticeable lag. In addition, I felt it was prudent to make a change to this function, which is only ever called in the context of agenda sorting, rather than to redesign and repair the assignment of text properties that happens in org.el:org-scan-tags. A more proper solution would be to make a fix there. As mentioned, I believe the root cause to be that some properties (like time-of-day) are assigned in the weekly/daily agenda view and not in any of the other views, and surely `org-agenda-sorting-strategy' is not the only functionality affected by this discrepancy. Reported-by: Kuba Orlik <kont...@kuba-orlik.name> Link: https://orgmode.org/list/16ea261a-b94c-43b2-91e0-a1ff01d0b...@kuba-orlik.name Reported-by: Charles Tam <m...@charlest.net> Link: https://orgmode.org/list/caku+9yxmuxgzg_lxosbgudkb6rtsenmtnctpnxw5z8kbbwf...@mail.gmail.com/ --- lisp/org-agenda.el | 34 +++++++++++++++---------------- testing/examples/agenda-file3.org | 11 ++++++++++ testing/lisp/test-org-agenda.el | 34 +++++++++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 testing/examples/agenda-file3.org diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index 87a9a462a..45030ffa5 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -1637,16 +1637,16 @@ symbols are recognized: time-up Put entries with time-of-day indications first, early first. time-down Put entries with time-of-day indications first, late first. -timestamp-up Sort by any timestamp date, early first. -timestamp-down Sort by any timestamp date, late first. -scheduled-up Sort by scheduled timestamp date, early first. -scheduled-down Sort by scheduled timestamp date, late first. -deadline-up Sort by deadline timestamp date, early first. -deadline-down Sort by deadline timestamp date, late first. -ts-up Sort by active timestamp date, early first. -ts-down Sort by active timestamp date, late first. -tsia-up Sort by inactive timestamp date, early first. -tsia-down Sort by inactive timestamp date, late first. +timestamp-up Sort by any timestamp datetime, early first. +timestamp-down Sort by any timestamp datetime, late first. +scheduled-up Sort by scheduled timestamp datetime, early first. +scheduled-down Sort by scheduled timestamp datetime, late first. +deadline-up Sort by deadline timestamp datetime, early first. +deadline-down Sort by deadline timestamp datetime, late first. +ts-up Sort by active timestamp datetime, early first. +ts-down Sort by active timestamp datetime, late first. +tsia-up Sort by inactive timestamp datetime, early first. +tsia-down Sort by inactive timestamp datetime, late first. category-keep Keep the default order of categories, corresponding to the sequence in `org-agenda-files'. category-up Sort alphabetically by category, A-Z. @@ -7587,13 +7587,13 @@ When TYPE is \"scheduled\", \"deadline\", \"timestamp\" or \"timestamp_ia\", compare within each of these type. When TYPE is the empty string, compare all timestamps without respect of their type." - (let* ((def (if org-agenda-sort-notime-is-late 99999999 -1)) - (ta (or (and (string-match type (or (get-text-property 1 'type a) "")) - (get-text-property 1 'ts-date a)) - def)) - (tb (or (and (string-match type (or (get-text-property 1 'type b) "")) - (get-text-property 1 'ts-date b)) - def))) + (let* ((def (if org-agenda-sort-notime-is-late 99999999999 -1)) + (time-string-a (and (string-match type (or (get-text-property 1 'type a) "")) + (org-entry-get (get-text-property 1 'org-hd-marker a) (upcase type)))) + (ta (if time-string-a (org-time-string-to-seconds time-string-a) def)) + (time-string-b (and (string-match type (or (get-text-property 1 'type b) "")) + (org-entry-get (get-text-property 1 'org-hd-marker b) (upcase type)))) + (tb (if time-string-b (org-time-string-to-seconds time-string-b) def))) (cond ((if ta (and tb (< ta tb)) tb) -1) ((if tb (and ta (< tb ta)) ta) +1)))) diff --git a/testing/examples/agenda-file3.org b/testing/examples/agenda-file3.org new file mode 100644 index 000000000..4e1adfd41 --- /dev/null +++ b/testing/examples/agenda-file3.org @@ -0,0 +1,11 @@ +* TODO three point five +SCHEDULED: <2024-01-17 Wed 17:00> + +* TODO zero +SCHEDULED: <2024-01-17 Wed 08:00> + +* TODO four point five +SCHEDULED: <2024-03-14 Thu 01:00> + +* TODO two point five +SCHEDULED: <2024-01-17 Wed 12:00> diff --git a/testing/lisp/test-org-agenda.el b/testing/lisp/test-org-agenda.el index 06d5abc43..58dde3c5f 100644 --- a/testing/lisp/test-org-agenda.el +++ b/testing/lisp/test-org-agenda.el @@ -216,7 +216,7 @@ "all todo" (goto-char (point-min)) (search-forward "TODO Todo and will appear in agenda" nil t))) - + ;; All todo keywords, including not done. (org-todo-list "*") (should @@ -277,7 +277,37 @@ (progn "NEXT|TODO" (goto-char (point-min)) - (search-forward "TODO Todo and will appear in agenda" nil t)))))) + (search-forward "TODO Todo and will appear in agenda" nil t)))) + (org-test-agenda--kill-all-agendas))) + +(ert-deftest test-org-agenda/todo-list-timestamp-sort () + "Test agenda-sortation of scheduled headlines." + (cl-assert (not org-agenda-sticky) nil "precondition violation") + (cl-assert (not (org-test-agenda--agenda-buffers)) + nil "precondition violation") + (let ((org-agenda-files `(,(expand-file-name "examples/agenda-file2.org" + org-test-dir) + ,(expand-file-name "examples/agenda-file3.org" + org-test-dir))) + (org-agenda-sorting-strategy '(scheduled-up)) + (org-agenda-sort-notime-is-late t)) + (org-todo-list) + (set-buffer org-agenda-buffer-name) + (print (buffer-substring-no-properties (point-min) (point-max))) + (goto-char (point-min)) + (should (search-forward "TODO zero" nil t)) + (should (search-forward "TODO one" nil t)) + (should (search-forward "TODO two" nil t)) + (should (search-forward "TODO two point five" nil t)) + (should (search-forward "TODO three" nil t)) + (should (search-forward "TODO three point five" nil t)) + (should (search-forward "TODO four" nil t)) + (should (search-forward "TODO four point five" nil t)) + ;; The timestamp in TODO five is not SCHEDULED; with + ;; notime-is-late, it should appear last in scheduled-up even + ;; though TODO four point five is scheduled two months later. + (should (search-forward "TODO five" nil t)) + (org-test-agenda--kill-all-agendas))) (ert-deftest test-org-agenda/scheduled-non-todo () "One informative line in the agenda from scheduled non-todo-keyword-item." -- 2.34.1