emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [DRAFT][PATCH] org-encode-time compatibility and convenience helper
@ 2022-04-11 15:22 Max Nikulin
  2022-04-11 17:43 ` Paul Eggert
  2022-04-23  8:25 ` Ihor Radchenko
  0 siblings, 2 replies; 17+ messages in thread
From: Max Nikulin @ 2022-04-11 15:22 UTC (permalink / raw)
  To: org-mode-email; +Cc: Paul Eggert

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

Hi,

After a recent report of incorrect daylight saving time handling in agenda:

Ignacio Casso [BUG] org-agenda thinks timestamps after 23:00 correspond 
to the next day Tue, 29 Mar 2022 15:09:10 +0200
https://list.orgmode.org/PAXPR06MB7760238F410CBE3203F78EE0C61E9@PAXPR06MB7760.eurprd06.prod.outlook.com

I tried to create a compatibility helper that will use currently 
recommended way to call `encode-time' with single list argument for 
Emacs-27 and newer, but use the only available call style as separated 
arguments for older Emacs versions.

 From my point of view
- it should work at the compile or load time to minimize runtime 
performance impact,
- since both ways to call `encode-time' are necessary (in a half of 
cases a list returned by `decode-time' is available, in other cases 
timestamps are assembled from scratch, none is preferred), it should be 
convenient in both cases,
- it should allow Org to work even if support of multiple `encode-time' 
arguments will be removed from Emacs.

Paul Eggert proposed org-encode-time-1 defsubst/defun
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

My patch requires more changes since the macro is just defined but not 
actually used. It does not fix the problem with "no DST" flag returned 
by some function in Org. I can prepare next patches, but I think it 
should be decided at first which approach should be accepted by Org Mode:
- org-encode-time accepting both list or separate arguments
- mix of `encode-time' with multiple arguments and org-encode-time-1 for 
lists.

[-- Attachment #2: 0001-org-macs.el-Introduce-a-helper-for-encode-time.patch --]
[-- Type: text/x-patch, Size: 4035 bytes --]

From e330999cefe40d6d9a2f25abfd48b1f332b3688d Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Fri, 8 Apr 2022 23:10:50 +0700
Subject: [PATCH] org-macs.el: Introduce a helper for `encode-time'

* lisp/org-macs.el (org-encode-time): New compatibility and convenience
helper macro to allow a list for time components or separate arguments
independently of Emacs version.
* testing/lisp/test-org.el (test-org/org-encode-time): Tests for various
ways to call `org-encode-time'.

Ensure recommended way to call `encode-time' for Emacs-27 and newer with
hope to avoid bugs due to attempts to modernize the code similar to
bug#54731.
---
 lisp/org-macs.el         | 20 ++++++++++++++++++
 testing/lisp/test-org.el | 45 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index a09115e7c..7e8c23d09 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1225,6 +1225,26 @@ nil, just return 0."
 	(b (org-2ft b)))
     (and (> a 0) (> b 0) (\= a b))))
 
+(if (version< emacs-version "27.1")
+    (defmacro org-encode-time (&rest time)
+      (if (cdr time)
+          `(encode-time ,@time)
+        `(apply #'encode-time ,(car time))))
+  (defmacro org-encode-time (&rest time)
+    (pcase (length time)
+      (1 `(encode-time ,(car time)))
+      (6 `(encode-time (list ,@time nil -1 nil)))
+      (9 `(encode-time (list ,@time)))
+      (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                (length time))))))
+(put 'org-encode-time 'function-documentation
+     "Compatibility and convenience helper for `encode-time'.
+May be called with 9 components list (SECONDS ... YEAR IGNORED DST ZONE)
+as the recommended way since Emacs-27 or with 6 or 9 separate arguments
+similar to the only possible variant for Emacs-26 and earlier.
+Warning: use -1 for DST that means guess actual value, nil means no
+daylight saving time and may be wrong at particular time.")
+
 (defun org-parse-time-string (s &optional nodefault)
   "Parse Org time string S.
 
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 6aecc3af8..a0ed36362 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -179,6 +179,51 @@
 \f
 ;;; Date and time analysis
 
+(ert-deftest test-org/org-encode-time ()
+  "Test various ways to call `org-encode-time'"
+  ;; list as the sole argument
+  (should (string-equal
+           "2022-03-24 23:30:01"
+           (format-time-string
+            "%F %T"
+            (org-encode-time '(01 30 23 24 03 2022 nil -1 nil)))))
+  ;; SECOND...YEAR
+  (should (string-equal
+           "2022-03-24 23:30:02"
+           (format-time-string
+            "%F %T"
+            (org-encode-time 02 30 23 24 03 2022))))
+  ;; SECOND...YEAR IGNORED DST ZONE
+  (should (string-equal
+           "2022-03-24 23:30:03"
+           (format-time-string
+            "%F %T"
+            (org-encode-time 03 30 23 24 03 2022 nil -1 nil))))
+  ;; function call
+  (should (string-equal
+           "2022-03-24 23:30:04"
+           (format-time-string
+            "%F %T"
+            (org-encode-time (apply #'list 04 30 23 '(24 03 2022 nil -1 nil))))))
+  ;; wrong number of arguments
+  (if (not (version< emacs-version "27.1"))
+      (should-error (string-equal
+                     "2022-03-24 23:30:05"
+                     (format-time-string
+                      "%F %T"
+                      (org-encode-time 05 30 23 24 03 2022 nil)))))
+  ;; daylight saving time
+  (let ((tz (getenv "TZ")))
+    (unwind-protect
+        (progn
+          (setenv "TZ" "Europe/Madrid")
+          (should (string-equal
+                   "2022-03-31 23:30:06"
+                   (format-time-string
+                    "%F %T"
+                    (org-encode-time 06 30 23 31 03 2022)))))
+      (setenv "TZ" tz))))
+
 (ert-deftest test-org/org-read-date ()
   "Test `org-read-date' specifications."
   ;; Parse ISO date with abbreviated year and month.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH] org-encode-time compatibility and convenience helper
  2022-04-11 15:22 [DRAFT][PATCH] org-encode-time compatibility and convenience helper Max Nikulin
@ 2022-04-11 17:43 ` Paul Eggert
  2022-04-23  8:25 ` Ihor Radchenko
  1 sibling, 0 replies; 17+ messages in thread
From: Paul Eggert @ 2022-04-11 17:43 UTC (permalink / raw)
  To: Max Nikulin, org-mode-email

On 4/11/22 08:22, Max Nikulin wrote:

> +  (defmacro org-encode-time (&rest time)
> +    (pcase (length time)
> +      (1 `(encode-time ,(car time)))
> +      (6 `(encode-time (list ,@time nil -1 nil)))
> +      (9 `(encode-time (list ,@time)))

After seeing this code and thinking about it a bit more I now understand 
better why you'd prefer Emacs encode-time to accept a 6-argument list. 
I'll work on adding that to Emacs master (to become Emacs 29). Of course 
something like the above will still needed for Emacs 27 and 28.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH] org-encode-time compatibility and convenience helper
  2022-04-11 15:22 [DRAFT][PATCH] org-encode-time compatibility and convenience helper Max Nikulin
  2022-04-11 17:43 ` Paul Eggert
@ 2022-04-23  8:25 ` Ihor Radchenko
  2022-04-23 19:37   ` Paul Eggert
  2022-04-24 11:34   ` [DRAFT][PATCH v2] " Max Nikulin
  1 sibling, 2 replies; 17+ messages in thread
From: Ihor Radchenko @ 2022-04-23  8:25 UTC (permalink / raw)
  To: Max Nikulin; +Cc: Paul Eggert, org-mode-email

Max Nikulin <manikulin@gmail.com> writes:

> I tried to create a compatibility helper that will use currently 
> recommended way to call `encode-time' with single list argument for 
> Emacs-27 and newer, but use the only available call style as separated 
> arguments for older Emacs versions.

Thanks for this work!

>  From my point of view
> - it should work at the compile or load time to minimize runtime 
> performance impact,
> - since both ways to call `encode-time' are necessary (in a half of 
> cases a list returned by `decode-time' is available, in other cases 
> timestamps are assembled from scratch, none is preferred), it should be 
> convenient in both cases,
> - it should allow Org to work even if support of multiple `encode-time' 
> arguments will be removed from Emacs.

Agree.

> My patch requires more changes since the macro is just defined but not 
> actually used. It does not fix the problem with "no DST" flag returned 
> by some function in Org. I can prepare next patches, but I think it 
> should be decided at first which approach should be accepted by Org Mode:
> - org-encode-time accepting both list or separate arguments
> - mix of `encode-time' with multiple arguments and org-encode-time-1 for 
> lists.

This whole timezone staff is complex. I got lost in the emacs devel
discussion half-way through. From point of view of API, I would prefer a
single function with docstring explaining the necessary caveats.

> +      (if (cdr time)
> +          `(encode-time ,@time)
> +        `(apply #'encode-time ,(car time))))

Do I miss something or can you instead just do `(encode-time ,@time)
without if?

> +  (should (string-equal
> +           "2022-03-24 23:30:01"
> +           (format-time-string
> +            "%F %T"
> +            (org-encode-time '(01 30 23 24 03 2022 nil -1 nil)))))
> ...

These tests will be executed using system value of TZ. I am not sure if
tests are not going to break, say, in southern hemisphere or at some
other bizzare values of TZ.

> +  ;; daylight saving time
> +  (let ((tz (getenv "TZ")))
> +    (unwind-protect
> +        (progn
> +          (setenv "TZ" "Europe/Madrid")
> +          (should (string-equal
> +                   "2022-03-31 23:30:06"
> +                   (format-time-string
> +                    "%F %T"
> +                    (org-encode-time 06 30 23 31 03 2022)))))
> +      (setenv "TZ" tz))))

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH] org-encode-time compatibility and convenience helper
  2022-04-23  8:25 ` Ihor Radchenko
@ 2022-04-23 19:37   ` Paul Eggert
  2022-04-24  3:35     ` Ihor Radchenko
  2022-04-24 11:34   ` [DRAFT][PATCH v2] " Max Nikulin
  1 sibling, 1 reply; 17+ messages in thread
From: Paul Eggert @ 2022-04-23 19:37 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: Max Nikulin, org-mode-email

On 4/23/22 01:25, Ihor Radchenko wrote:
>> +  (should (string-equal
>> +           "2022-03-24 23:30:01"
>> +           (format-time-string
>> +            "%F %T"
>> +            (org-encode-time '(01 30 23 24 03 2022 nil -1 nil)))))
>> ...
> These tests will be executed using system value of TZ. I am not sure if
> tests are not going to break, say, in southern hemisphere or at some
> other bizzare values of TZ.
> 

Good point: that test won't work in a time zone where the local time 
23:30:01 does not exist due to a daylight-saving spring-forward transition.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH] org-encode-time compatibility and convenience helper
  2022-04-23 19:37   ` Paul Eggert
@ 2022-04-24  3:35     ` Ihor Radchenko
  0 siblings, 0 replies; 17+ messages in thread
From: Ihor Radchenko @ 2022-04-24  3:35 UTC (permalink / raw)
  To: Paul Eggert; +Cc: Max Nikulin, org-mode-email

Paul Eggert <eggert@cs.ucla.edu> writes:

> On 4/23/22 01:25, Ihor Radchenko wrote:
>>> +  (should (string-equal
>>> +           "2022-03-24 23:30:01"
>>> +           (format-time-string
>>> +            "%F %T"
>>> +            (org-encode-time '(01 30 23 24 03 2022 nil -1 nil)))))
>>> ...
>> These tests will be executed using system value of TZ. I am not sure if
>> tests are not going to break, say, in southern hemisphere or at some
>> other bizzare values of TZ.
>> 
>
> Good point: that test won't work in a time zone where the local time 
> 23:30:01 does not exist due to a daylight-saving spring-forward transition.

Note that it is rather hypothetical case. It's not just about 23:30:01,
but also about March 24 date. I am now checking
https://en.wikipedia.org/wiki/Daylight_saving_time_by_country The day
saving changes in March are:
- Second Sunday
- Last Sunday
- Friday before last Sunday
- Last Friday
- 20 or 21 March (Iran)
- Fourth Sunday

March 24, 2023 will be the last Friday before last Sunday in March...

The conclusion is: it is all tricky and I would not bet on trying to
find a "safe" date for an arbitrary present time zone DST. It would be
better to set some test TZ for testing and be done with all this
ambiguity.

Best,
Ihor



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH v2] org-encode-time compatibility and convenience helper
  2022-04-23  8:25 ` Ihor Radchenko
  2022-04-23 19:37   ` Paul Eggert
@ 2022-04-24 11:34   ` Max Nikulin
  2022-04-26  9:07     ` Ihor Radchenko
  1 sibling, 1 reply; 17+ messages in thread
From: Max Nikulin @ 2022-04-24 11:34 UTC (permalink / raw)
  To: emacs-orgmode

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

On 23/04/2022 15:25, Ihor Radchenko wrote:
> Max Nikulin writes:
> 
>> My patch requires more changes since the macro is just defined but not
>> actually used. It does not fix the problem with "no DST" flag returned
>> by some function in Org. I can prepare next patches, but I think it
>> should be decided at first which approach should be accepted by Org Mode:
>> - org-encode-time accepting both list or separate arguments
>> - mix of `encode-time' with multiple arguments and org-encode-time-1 for
>> lists.
> 
> This whole timezone staff is complex. I got lost in the emacs devel
> discussion half-way through. From point of view of API, I would prefer a
> single function with docstring explaining the necessary caveats.

To have namely a single function that accepts both a list or multiple 
arguments, it is necessary to introduce a new name to Emacs. 
`encode-time' has a subtle difference in behavior depending on the 
calling style and fixing this issue may break some code. That is why I 
decided to offer a macro.

I have not figured out which kind of high-level API I would like to have 
instead of `encode-time', and I believe, it is acceptable to rely on 
default normalization and ambiguity resolution as the status quo. 
Problems may happen during 2 days of 365 and people usually expect some 
glitches for these days. There are too many caveats to explain them in 
docstring.

>> +      (if (cdr time)
>> +          `(encode-time ,@time)
>> +        `(apply #'encode-time ,(car time))))
> 
> Do I miss something or can you instead just do `(encode-time ,@time)
> without if?

I changed the code to use ,@time in both cases. Previous time likely I 
forgot to re-evaluate some definition, got some unexpected error, and 
decided to use `car' for a while.

`if' can not be dropped however. In the case of
     (org-encode-time '(0 0 23 30 04 2022 nil nil nil))
`(encode-time ,@time) will be expanded to
     (encode-time (list 0 0 23 30 04 2022 nil nil nil))
due to (&rest time) argument. Single list argument is unsupported in 
Emacs-26. So `apply' cat not be avoided.

>> +  (should (string-equal
>> +           "2022-03-24 23:30:01"
>> +           (format-time-string
>> +            "%F %T"
>> +            (org-encode-time '(01 30 23 24 03 2022 nil -1 nil)))))
>> ...
> 
> These tests will be executed using system value of TZ. I am not sure if
> tests are not going to break, say, in southern hemisphere or at some
> other bizzare values of TZ.

You are right, it is safer to run this test with explicitly chosen TZ 
value. I do not think there is a point in speculation whether the test 
fails in some currently existing time zone.

I am attaching an updated version of the draft. I have added a macro for 
testing of particular time zones.

[-- Attachment #2: 0001-org-macs.el-Introduce-a-helper-for-encode-time.patch --]
[-- Type: text/x-patch, Size: 4647 bytes --]

From 8fdcca3f3b0cf3e890149eb79ea6c7970b7d2cfa Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Fri, 8 Apr 2022 23:10:50 +0700
Subject: [PATCH] org-macs.el: Introduce a helper for `encode-time'

* lisp/org-macs.el (org-encode-time): New compatibility and convenience
helper macro to allow a list for time components or separate arguments
independently of Emacs version.
* testing/lisp/test-org.el (org-test-with-timezone): New macro to ensure
that some code is executed with or without daylight saving time or other
time shifts by setting certain TZ environment value.
* testing/lisp/test-org.el (test-org/org-encode-time): Tests for various
ways to call `org-encode-time'.

Ensure recommended way to call `encode-time' for Emacs-27 and newer with
hope to avoid bugs due to attempts to modernize the code similar to
bug#54731.
---
 lisp/org-macs.el         | 20 ++++++++++++++
 testing/lisp/test-org.el | 56 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index a09115e7c..85dd20028 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1225,6 +1225,26 @@ nil, just return 0."
 	(b (org-2ft b)))
     (and (> a 0) (> b 0) (\= a b))))
 
+(if (version< emacs-version "27.1")
+    (defmacro org-encode-time (&rest time)
+      (if (cdr time)
+          `(encode-time ,@time)
+        `(apply #'encode-time ,@time)))
+  (defmacro org-encode-time (&rest time)
+    (pcase (length time)
+      (1 `(encode-time ,(car time)))
+      (6 `(encode-time (list ,@time nil -1 nil)))
+      (9 `(encode-time (list ,@time)))
+      (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                (length time))))))
+(put 'org-encode-time 'function-documentation
+     "Compatibility and convenience helper for `encode-time'.
+May be called with 9 components list (SECONDS ... YEAR IGNORED DST ZONE)
+as the recommended way since Emacs-27 or with 6 or 9 separate arguments
+similar to the only possible variant for Emacs-26 and earlier.
+Warning: use -1 for DST that means guess actual value, nil means no
+daylight saving time and may be wrong at particular time.")
+
 (defun org-parse-time-string (s &optional nodefault)
   "Parse Org time string S.
 
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 6aecc3af8..aa3f9a3f6 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -24,6 +24,20 @@
 
 (eval-and-compile (require 'cl-lib))
 
+\f
+;;; Helpers
+
+(defmacro org-test-with-timezone (tz &rest body)
+  "Evaluate BODY with TZ environment temporary set to the passed value."
+  (declare (indent 1))
+  (org-with-gensyms (tz-saved)
+    `(let ((,tz-saved (getenv "TZ")))
+       (unwind-protect
+           (progn
+             (setenv "TZ" ,tz)
+             ,@body)
+             (setenv "TZ" ,tz-saved)))))
+
 \f
 ;;; Comments
 
@@ -179,6 +193,48 @@
 \f
 ;;; Date and time analysis
 
+(ert-deftest test-org/org-encode-time ()
+  "Test various ways to call `org-encode-time'"
+  (org-test-with-timezone "UTC"
+    ;; list as the sole argument
+    (should (string-equal
+             "2022-03-24 23:30:01"
+             (format-time-string
+              "%F %T"
+              (org-encode-time '(1 30 23 24 3 2022 nil -1 nil)))))
+    ;; SECOND...YEAR
+    (should (string-equal
+             "2022-03-24 23:30:02"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 2 30 23 24 3 2022))))
+    ;; SECOND...YEAR IGNORED DST ZONE
+    (should (string-equal
+             "2022-03-24 23:30:03"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 3 30 23 24 3 2022 nil -1 nil))))
+    ;; function call
+    (should (string-equal
+             "2022-03-24 23:30:04"
+             (format-time-string
+              "%F %T"
+              (org-encode-time (apply #'list 4 30 23 '(24 3 2022 nil -1 nil))))))
+    ;; wrong number of arguments
+    (if (not (version< emacs-version "27.1"))
+        (should-error (string-equal
+                       "2022-03-24 23:30:05"
+                       (format-time-string
+                        "%F %T"
+                        (org-encode-time 5 30 23 24 3 2022 nil))))))
+  ;; daylight saving time
+  (org-test-with-timezone "Europe/Madrid"
+    (should (string-equal
+             "2022-03-31 23:30:06"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 6 30 23 31 3 2022))))))
+
 (ert-deftest test-org/org-read-date ()
   "Test `org-read-date' specifications."
   ;; Parse ISO date with abbreviated year and month.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [DRAFT][PATCH v2] org-encode-time compatibility and convenience helper
  2022-04-24 11:34   ` [DRAFT][PATCH v2] " Max Nikulin
@ 2022-04-26  9:07     ` Ihor Radchenko
  2022-05-03 12:14       ` [PATCH v3] " Max Nikulin
  0 siblings, 1 reply; 17+ messages in thread
From: Ihor Radchenko @ 2022-04-26  9:07 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode

Max Nikulin <manikulin@gmail.com> writes:

>> This whole timezone staff is complex. I got lost in the emacs devel
>> discussion half-way through. From point of view of API, I would prefer a
>> single function with docstring explaining the necessary caveats.
>
> To have namely a single function that accepts both a list or multiple 
> arguments, it is necessary to introduce a new name to Emacs. 
> `encode-time' has a subtle difference in behavior depending on the 
> calling style and fixing this issue may break some code. That is why I 
> decided to offer a macro.

I did not mean "single function" to be a function specifically. Macro is
perfectly fine. Sorry for the confusion.

> I have not figured out which kind of high-level API I would like to have 
> instead of `encode-time', and I believe, it is acceptable to rely on 
> default normalization and ambiguity resolution as the status quo. 
> Problems may happen during 2 days of 365 and people usually expect some 
> glitches for these days. There are too many caveats to explain them in 
> docstring.

I understand. But, it may be helpful to mention the existence of such
caveats, ideally with a link where the user can learn more.

> I am attaching an updated version of the draft. I have added a macro for 
> testing of particular time zones.
> From 8fdcca3f3b0cf3e890149eb79ea6c7970b7d2cfa Mon Sep 17 00:00:00 2001
> From: Max Nikulin <manikulin@gmail.com>
> Date: Fri, 8 Apr 2022 23:10:50 +0700
> Subject: [PATCH] org-macs.el: Introduce a helper for `encode-time'

LGTM. I think you can go ahead and continue with the followup patch
making use of the new macro.

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3] org-encode-time compatibility and convenience helper
  2022-04-26  9:07     ` Ihor Radchenko
@ 2022-05-03 12:14       ` Max Nikulin
  2022-05-04  9:56         ` Ihor Radchenko
  0 siblings, 1 reply; 17+ messages in thread
From: Max Nikulin @ 2022-05-03 12:14 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Paul Eggert

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

On 26/04/2022 16:07, Ihor Radchenko wrote:
> 
> LGTM. I think you can go ahead and continue with the followup patch
> making use of the new macro.

The attached patch set is assumed to be complete.

I have no chance to thoroughly test it. Existing unit tests pass for 
Emacs-26 and Emacs-27. Nothing has changed for Emacs-25, as for the 
"main" branch one test fails. I have not tried Emacs-28 or the current 
git version.

In comparison to the previous patch version I have expanded the 
docstring and added a bit more tests. I have tried to support recently 
committed to Emacs 6-elements list for `encode-time', but I do not like 
the following compile-time warning:

> In toplevel form:
> org-macs.el:1397:23:Warning: encode-time called with 1 argument, but requires
>     6+

I had to carve the patch originally posted by Paul Eggert for the Emacs 
repository to adapt it for the Org repository and to drop 
org-encode-time-1 chunks.
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

The largest patch replaces all calls of `encode-time' to `org-encode-time'

Goals of `org-encode-time':
- it should work at the compile or load time to minimize runtime 
performance impact,
- since both ways to call `encode-time' are necessary (in a half of 
cases a list returned by `decode-time' is available, in other cases 
timestamps are assembled from scratch, none is preferred), it should be 
convenient in both cases,
- it should allow Org to work even if support of multiple `encode-time' 
arguments will be removed from Emacs,
- it should allow to avoid a pitfall with Emacs-27+ `encode-time': DST 
argument is taken into account when the function called with single list 
argument but it is ignored when multiple arguments is passed. 
`org-encode-time' respects DST value unless it is running in Emacs-26 or 
earlier. I have not added special code for old versions considering it 
as graceful degradation.

Finally, let me remind that these changes are result of the following 
discussions:

Ignacio Casso to emacs-orgmode. [BUG] org-agenda thinks timestamps after 
23:00 correspond to the next day.  Tue, 29 Mar 2022 15:09:10 +0200. 
https://list.orgmode.org/PAXPR06MB7760238F410CBE3203F78EE0C61E9@PAXPR06MB7760.eurprd06.prod.outlook.com

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54731
https://list.orgmode.org/8b85c879-4f9b-eac3-e700-f176cc588577@gmail.com
#54731 - Please, revert part of dd0727e1ec1 related to Org mode 
(`encode-time') - GNU bug report logs

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764
https://list.orgmode.org/5ed963b2-3fa8-48d8-627e-bc0571d15b43@gmail.com
#54764 - encode-time: make DST and TIMEZONE fields of the list argument 
optional ones - GNU bug report logs

[-- Attachment #2: 0001-Use-unknown-DST-instead-of-standard-time-in-timestam.patch --]
[-- Type: text/x-patch, Size: 2862 bytes --]

From fbddfa8cfeecc8465117eacf101a0880cfc13775 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 9 Apr 2022 00:17:09 -0700
Subject: [PATCH 1/6] Use unknown DST instead of standard time in timestamps

* lisp/ol.el (org-store-link): Prefer plain (encode-time ...)
to (apply 'encode-time ...), for speed.
* lisp/org-macs.el (org-parse-time-string): Return unknown DST,
not standard time.
* lisp/org.el (org-read-date-analyze): Return a timestamp with a DST
flag of -1 (unknown) rather than nil (standard time).

Max Nikulin:
A larger patch "Improve Org usage of timestamps" was suggested in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

Changes selected for this patch normalizes timestamp format:
if it is a list than it should contain 9 elements to be compatible
with Emacs-27 and Emacs-28 `encode-time' single argument, nil should not
be used for DST field since it means standard time while actual value
is unknown and should be guessed.

Ignacio Casso reported a problem with agenda
https://list.orgmode.org/PAXPR06MB7760238F410CBE3203F78EE0C61E9@PAXPR06MB7760.eurprd06.prod.outlook.com
due to Emacs commit dd0727e1ec1 changing Org code.  It was mostly reverted
by 8ef37913d3 (bug#54731).  Code in the Org repository did not have
the bug, but it safer to add protection against similar refactoring.
---
 lisp/ol.el       | 4 +---
 lisp/org-macs.el | 2 +-
 lisp/org.el      | 2 +-
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/lisp/ol.el b/lisp/ol.el
index d4bd0e40c..0de9c921b 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -1618,9 +1618,7 @@ non-nil."
 	  (setq link
 		(format-time-string
 		 (car org-time-stamp-formats)
-		 (apply 'encode-time
-			(list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd)
-			      nil nil nil))))
+		 (encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
 	  (org-link-store-props :type "calendar" :date cd)))
 
        ((eq major-mode 'w3-mode)
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 8535bf2cd..241155064 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1412,7 +1412,7 @@ This should be a lot faster than the `parse-time-string'."
 	(string-to-number (match-string 4 s))
 	(string-to-number (match-string 3 s))
 	(string-to-number (match-string 2 s))
-	nil nil nil))
+	nil -1 nil))
 
 (defun org-matcher-time (s)
   "Interpret a time comparison value S as a floating point time.
diff --git a/lisp/org.el b/lisp/org.el
index 1d5fc3903..165c83609 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -13663,7 +13663,7 @@ user."
 	 (setq year (nth 5 org-defdecode))
 	 (setq org-read-date-analyze-forced-year t))))
     (setq org-read-date-analyze-futurep futurep)
-    (list second minute hour day month year)))
+    (list second minute hour day month year nil -1 nil)))
 
 (defvar parse-time-weekdays)
 (defun org-read-date-get-relative (s today default)
-- 
2.25.1


[-- Attachment #3: 0002-test-org.el-Fix-tests-for-org-parse-time-string.patch --]
[-- Type: text/x-patch, Size: 1532 bytes --]

From 77dbfea6f491c45bb6fa6b67b99e55f883c1ed0c Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Tue, 3 May 2022 17:59:11 +0700
Subject: [PATCH 2/6] test-org.el: Fix tests for `org-parse-time-string'

* testing/lisp/test-org.el (ert-deftest test-org/org-parse-time-string):
Update test expectations to use DST of -1 (guess) after fix of
`org-parse-time-string'.
---
 testing/lisp/test-org.el | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index ca0dc676b..4b5105a57 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -268,15 +268,15 @@
 (ert-deftest test-org/org-parse-time-string ()
   "Test `org-parse-time-string'."
   (should (equal (org-parse-time-string "2012-03-29 16:40")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "[2012-03-29 16:40]")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29 16:40>")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29>")
-		 '(0 0 0 29 3 2012 nil nil nil)))
+		 '(0 0 0 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29>" t)
-		 '(0 nil nil 29 3 2012 nil nil nil))))
+		 '(0 nil nil 29 3 2012 nil -1 nil))))
 
 (ert-deftest test-org/closest-date ()
   "Test `org-closest-date' specifications."
-- 
2.25.1


[-- Attachment #4: 0003-Use-higher-level-helpers-instead-of-encode-time.patch --]
[-- Type: text/x-patch, Size: 3019 bytes --]

From 526e95ac4447f5deb4953860cb8c4e9d605dda8e Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 9 Apr 2022 00:17:09 -0700
Subject: [PATCH 3/6] Use higher level helpers instead of `encode-time'

* lisp/org-clock.el (org-clock-sum)
(org-clock-update-time-maybe):
Prefer org-time-string-to-seconds to doing it by hand.
* lisp/org-macs.el (org-2ft):
Prefer org-time-string-to-seconds to doing it by hand.
* lisp/org-table.el (org-table-eval-formula):
Prefer org-time-string-to-time to doing it by hand.

Max Nikulin:
A larger patch "Improve Org usage of timestamps" was suggested in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

Only cosmetic changes are selected for this patch.
---
 lisp/org-clock.el | 17 ++++++-----------
 lisp/org-macs.el  |  2 +-
 lisp/org-table.el |  3 +--
 3 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index ec87aaf8a..9f80dea04 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -1913,13 +1913,10 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes."
 	  (cond
 	   ((match-end 2)
 	    ;; Two time stamps.
-	    (let* ((ts (float-time
-			(apply #'encode-time
-			       (save-match-data
-				 (org-parse-time-string (match-string 2))))))
-		   (te (float-time
-			(apply #'encode-time
-			       (org-parse-time-string (match-string 3)))))
+	    (let* ((ss (match-string 2))
+		   (se (match-string 3))
+		   (ts (org-time-string-to-seconds ss))
+		   (te (org-time-string-to-seconds se))
 		   (dt (- (if tend (min te tend) te)
 			  (if tstart (max ts tstart) ts))))
 	      (when (> dt 0) (cl-incf t1 (floor dt 60)))))
@@ -3051,10 +3048,8 @@ Otherwise, return nil."
 	  (end-of-line 1)
 	  (setq ts (match-string 1)
 		te (match-string 3))
-	  (setq s (- (float-time
-		      (apply #'encode-time (org-parse-time-string te)))
-		     (float-time
-		      (apply #'encode-time (org-parse-time-string ts))))
+	  (setq s (- (org-time-string-to-seconds te)
+		     (org-time-string-to-seconds ts))
 		neg (< s 0)
 		s (abs s)
 		h (floor (/ s 3600))
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 241155064..435c9ea30 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1355,7 +1355,7 @@ nil, just return 0."
    ((numberp s) s)
    ((stringp s)
     (condition-case nil
-	(float-time (apply #'encode-time (org-parse-time-string s)))
+	(org-time-string-to-seconds s)
       (error 0)))
    (t 0)))
 
diff --git a/lisp/org-table.el b/lisp/org-table.el
index 2f31bef56..40a425197 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -2607,8 +2607,7 @@ location of point."
 		     (format-time-string
 		      (org-time-stamp-format
 		       (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts))
-		      (apply #'encode-time
-			     (save-match-data (org-parse-time-string ts))))))
+		      (save-match-data (org-time-string-to-time ts)))))
 		 form t t))
 
 	  (setq ev (if (and duration (string-match "^[0-9]+:[0-9]+\\(?::[0-9]+\\)?$" form))
-- 
2.25.1


[-- Attachment #5: 0004-testing-lisp-Use-org-time-string-to-time.patch --]
[-- Type: text/x-patch, Size: 7991 bytes --]

From c8e0bae4c96f72412962c0003d6b0bb5d5d51ccf Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Mon, 2 May 2022 17:41:34 +0700
Subject: [PATCH 4/6] testing/lisp: Use `org-time-string-to-time'

* testing/lisp/test-org.el (test-org/org-read-date, test-org/deadline)
(test-org/schedule, test-org/time-stamp, test-org/timestamp-from-time):
* testing/org-test.el (org-test-at-time): Use `org-time-string-to-time'
instead of composition of `org-parse-time-string' and `encode-time'.

The actual goal is to prepare to replace `encode-time' by
`org-encode-time' compatibility and convenience macro.
---
 testing/lisp/test-org.el | 44 ++++++++++++++++------------------------
 testing/org-test.el      |  2 +-
 2 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 4b5105a57..8748cfa75 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -201,14 +201,14 @@
     (org-test-at-time "2014-03-04"
       (org-read-date
        t nil "+1y" nil
-       (apply #'encode-time (org-parse-time-string "2012-03-29"))))))
+       (org-time-string-to-time "2012-03-29")))))
   (should
    (equal
     "2013-03-29"
     (org-test-at-time "2014-03-04"
       (org-read-date
        t nil "++1y" nil
-       (apply #'encode-time (org-parse-time-string "2012-03-29"))))))
+       (org-time-string-to-time "2012-03-29")))))
   ;; When `org-read-date-prefer-future' is non-nil, prefer future
   ;; dates (relatively to now) when incomplete.  Otherwise, use
   ;; default date.
@@ -255,7 +255,7 @@
       (let ((org-read-date-prefer-future t))
 	(org-read-date
 	 t nil "1" nil
-	 (apply #'encode-time (org-parse-time-string "2012-03-29")))))))
+	 (org-time-string-to-time "2012-03-29"))))))
   (should
    (equal
     "2014-03-25"
@@ -263,7 +263,7 @@
       (let ((org-read-date-prefer-future t))
 	(org-read-date
 	 t nil "25" nil
-	 (apply #'encode-time (org-parse-time-string "2012-03-29"))))))))
+	 (org-time-string-to-time "2012-03-29")))))))
 
 (ert-deftest test-org/org-parse-time-string ()
   "Test `org-parse-time-string'."
@@ -5443,8 +5443,7 @@ Paragraph<point>"
    (equal "* H\nDEADLINE: <2012-03-29 -705d>"
 	  (cl-letf (((symbol-function 'org-read-date)
 		     (lambda (&rest args)
-		       (apply #'encode-time
-			      (org-parse-time-string "2014-03-04")))))
+		       (org-time-string-to-time "2014-03-04"))))
 	    (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29>"
 	      (let ((org-adapt-indentation nil)
 		    (org-last-inserted-timestamp nil))
@@ -5453,8 +5452,7 @@ Paragraph<point>"
   (should-error
    (cl-letf (((symbol-function 'org-read-date)
 	      (lambda (&rest args)
-		(apply #'encode-time
-		       (org-parse-time-string "2014-03-04")))))
+		(org-time-string-to-time "2014-03-04"))))
      (org-test-with-temp-text "* H"
        (let ((org-adapt-indentation nil)
 	     (org-last-inserted-timestamp nil))
@@ -5557,8 +5555,7 @@ Paragraph<point>"
    (equal "* H\nSCHEDULED: <2012-03-29 -705d>"
 	  (cl-letf (((symbol-function 'org-read-date)
 		     (lambda (&rest args)
-		       (apply #'encode-time
-			      (org-parse-time-string "2014-03-04")))))
+		       (org-time-string-to-time "2014-03-04"))))
 	    (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29>"
 	      (let ((org-adapt-indentation nil)
 		    (org-last-inserted-timestamp nil))
@@ -5567,8 +5564,7 @@ Paragraph<point>"
   (should-error
    (cl-letf (((symbol-function 'org-read-date)
 	      (lambda (&rest args)
-		(apply #'encode-time
-		       (org-parse-time-string "2014-03-04")))))
+		(org-time-string-to-time "2014-03-04"))))
      (org-test-with-temp-text "* H"
        (let ((org-adapt-indentation nil)
 	     (org-last-inserted-timestamp nil))
@@ -7786,7 +7782,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; With a prefix argument, also insert time.
@@ -7796,8 +7792,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time
-			  (org-parse-time-string "2014-03-04 00:41")))))
+		   (org-time-string-to-time "2014-03-04 00:41"))))
 	(org-time-stamp '(4))
 	(buffer-string)))))
   ;; With two universal prefix arguments, insert an active timestamp
@@ -7816,7 +7811,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil t)
 	(buffer-string)))))
   ;; When called from a timestamp, replace existing one.
@@ -7826,7 +7821,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu.>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   (should
@@ -7835,7 +7830,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu.>--<2014-03-04 tue.>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; When replacing a timestamp, preserve repeater, if any.
@@ -7845,7 +7840,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu. +2y>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; When called twice in a raw, build a date range.
@@ -7855,7 +7850,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29 thu.><point>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(let ((last-command 'org-time-stamp)
 	      (this-command 'org-time-stamp))
 	  (org-time-stamp nil))
@@ -8030,8 +8025,7 @@ CLOSED: %s
     "<2012-03-29 .+>"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))))))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")))))
   ;; When optional argument WITH-TIME is non-nil, provide time
   ;; information.
   (should
@@ -8039,8 +8033,7 @@ CLOSED: %s
     "<2012-03-29 .+ 16:40>"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")
       t))))
   ;; When optional argument INACTIVE is non-nil, return an inactive
   ;; timestamp.
@@ -8049,8 +8042,7 @@ CLOSED: %s
     "[2012-03-29 .+]"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")
       nil t)))))
 
 (ert-deftest test-org/timestamp-to-time ()
diff --git a/testing/org-test.el b/testing/org-test.el
index 0f1e254aa..d19c7f044 100644
--- a/testing/org-test.el
+++ b/testing/org-test.el
@@ -455,7 +455,7 @@ TIME can be a non-nil Lisp time value, or a string specifying a date and time."
 	(at (cl-gensym)))
     `(let* ((,tm ,time)
 	    (,at (if (stringp ,tm)
-		     (apply #'encode-time (org-parse-time-string ,tm))
+		     (org-time-string-to-time ,tm)
 		   ,tm)))
        (cl-letf
 	   ;; Wrap builtins whose behavior can depend on the current time.
-- 
2.25.1


[-- Attachment #6: 0005-org-macs.el-Introduce-a-helper-for-encode-time.patch --]
[-- Type: text/x-patch, Size: 6202 bytes --]

From 53842d0db6265654a67aa7f4b30424064c2347f7 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Fri, 8 Apr 2022 23:10:50 +0700
Subject: [PATCH 5/6] org-macs.el: Introduce a helper for `encode-time'

* lisp/org-macs.el (org-encode-time): New compatibility and convenience
helper macro to allow a list for time components or separate arguments
independently of Emacs version.
* testing/lisp/test-org.el (org-test-with-timezone): New macro to ensure
that some code is executed with certain TZ environment value and thus
particular daylight saving time or other time shift rules are active.
* testing/lisp/test-org.el (test-org/org-encode-time): Tests for various
ways to call `org-encode-time'.

Ensure recommended way to call `encode-time' for Emacs-27 and newer with
hope to avoid bugs due to attempts to modernize the code similar to
bug#54731.  6-elements list may be allowed as `encode-time' argument
since Emacs-29, see bug#54764.
---
 lisp/org-macs.el         | 35 ++++++++++++++++++++
 testing/lisp/test-org.el | 71 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 435c9ea30..aec3c3adb 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1389,6 +1389,41 @@ nil, just return 0."
 	(b (org-2ft b)))
     (and (> a 0) (> b 0) (\= a b))))
 
+(if (version< emacs-version "27.1")
+    (defmacro org-encode-time (&rest time)
+      (if (cdr time)
+          `(encode-time ,@time)
+        `(apply #'encode-time ,@time)))
+  (if (ignore-errors (encode-time '(0 0 0 1 1 1971)))
+      (defmacro org-encode-time (&rest time)
+        (pcase (length time) ; Emacs-29 since d75e2c12eb
+          (1 `(encode-time ,@time))
+          ((or 6 9) `(encode-time (list ,@time)))
+          (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                    (length time)))))
+    (defmacro org-encode-time (&rest time)
+      (pcase (length time)
+        (1 `(encode-time ,@time))
+        (6 `(encode-time (list ,@time nil -1 nil)))
+        (9 `(encode-time (list ,@time)))
+        (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                  (length time)))))))
+(put 'org-encode-time 'function-documentation
+     "Compatibility and convenience helper for `encode-time'.
+May be called with 9 components list (SECONDS ... YEAR IGNORED DST ZONE)
+as the recommended way since Emacs-27 or with 6 or 9 separate arguments
+similar to the only possible variant for Emacs-26 and earlier.
+6 elements list as the only argument causes wrong type argument till Emacs-29.
+
+Warning: use -1 for DST to guess the actual value, nil means no
+daylight saving time and may be wrong at particular time.
+
+DST value is ignored prior to Emacs-27.  Since Emacs-27 DST value matters
+even when multiple arguments is passed to this macro and such
+behavior is different from `encode-time'. See
+Info node `(elisp)Time Conversion' for details and caveats,
+preferably the latest version.")
+
 (defun org-parse-time-string (s &optional nodefault)
   "Parse Org time string S.
 
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 8748cfa75..6e5be2299 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -24,6 +24,20 @@
 
 (eval-and-compile (require 'cl-lib))
 
+\f
+;;; Helpers
+
+(defmacro org-test-with-timezone (tz &rest body)
+  "Evaluate BODY with TZ environment temporary set to the passed value."
+  (declare (indent 1))
+  (org-with-gensyms (tz-saved)
+    `(let ((,tz-saved (getenv "TZ")))
+       (unwind-protect
+           (progn
+             (setenv "TZ" ,tz)
+             ,@body)
+             (setenv "TZ" ,tz-saved)))))
+
 \f
 ;;; Comments
 
@@ -179,6 +193,63 @@
 \f
 ;;; Date and time analysis
 
+(ert-deftest test-org/org-encode-time ()
+  "Test various ways to call `org-encode-time'"
+  (org-test-with-timezone "UTC"
+    ;; list as the sole argument
+    (should (string-equal
+             "2022-03-24 23:30:01"
+             (format-time-string
+              "%F %T"
+              (org-encode-time '(1 30 23 24 3 2022 nil -1 nil)))))
+    ;; SECOND...YEAR
+    (should (string-equal
+             "2022-03-24 23:30:02"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 2 30 23 24 3 2022))))
+    ;; SECOND...YEAR IGNORED DST ZONE
+    (should (string-equal
+             "2022-03-24 23:30:03"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 3 30 23 24 3 2022 nil -1 nil))))
+    ;; function call
+    (should (string-equal
+             "2022-03-24 23:30:04"
+             (format-time-string
+              "%F %T"
+              (org-encode-time (apply #'list 4 30 23 '(24 3 2022 nil -1 nil))))))
+    ;; wrong number of arguments
+    (if (not (version< emacs-version "27.1"))
+        (should-error (string-equal
+                       "2022-03-24 23:30:05"
+                       (format-time-string
+                        "%F %T"
+                        (org-encode-time 5 30 23 24 3 2022 nil))))))
+  ;; daylight saving time
+  (if (not (version< emacs-version "27.1"))
+      ;; DST value is not ignored for multiple arguments unlike for `encode-time'
+      (should (string-equal
+               "2022-04-01 00:30:06 +0200 CEST"
+               (format-time-string
+                "%F %T %z %Z"
+                (org-encode-time 6 30 23 31 3 2022 nil nil "Europe/Madrid")
+                "Europe/Madrid")))
+    (should (string-equal
+             "2022-03-31 23:30:07 +0200 CEST"
+             (format-time-string
+              "%F %T %z %Z"
+              (org-encode-time 7 30 23 31 3 2022 nil t "Europe/Madrid")
+              "Europe/Madrid"))))
+  (org-test-with-timezone "Europe/Madrid"
+    ;; Standard time is not forced when DST is not specified
+    (should (string-equal
+             "2022-03-31 23:30:08"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 8 30 23 31 3 2022))))))
+
 (ert-deftest test-org/org-read-date ()
   "Test `org-read-date' specifications."
   ;; Parse ISO date with abbreviated year and month.
-- 
2.25.1


[-- Attachment #7: 0006-Use-org-encode-time-helper-macro.patch --]
[-- Type: text/x-patch, Size: 27874 bytes --]

From 3dc08309092580063d07f557218bed92b833913b Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Mon, 2 May 2022 21:19:59 +0700
Subject: [PATCH 6/6] Use `org-encode-time' helper macro

* lisp/ol.el (org-store-link):
* lisp/org-agenda.el (org-agenda-get-timestamps)
(org-agenda-get-progress, agenda-bulk-action):
* lisp/org-capture.el (org-capture-fill-template):
* lisp/org-clock.el (org-clock-get-sum-start)
(org-clock-special-range, org-clocktable-shift)
(org-clocktable-steps):
* lisp/org-colview.el (org-colview-construct-allowed-dates):
* lisp/org-datetree.el (org-datetree-find-iso-week-create)
(org-datetree-insert-line):
* lisp/org-element.el (org-element-timestamp-interpreter):
* lisp/org-macro.el (org-macro--vc-modified-time):
* lisp/org-macs.el (org-matcher-time):
* lisp/org.el (org-current-time, org-current-effective-time)
(org-add-planning-info, org-read-date, org-read-date-display)
(org-read-date-analyze, org-eval-in-calendar)
(org-calendar-select, org-display-custom-time)
(org-calendar-select-mouse, org-time-string-to-time)
(org-time-from-absolute, org-at-clock-log-p)
(org-date-from-calendar, org-get-cursor-date)
(org-timestamp-to-time):
* testing/lisp/test-org-clock.el (org-test-clock-create-timestamp):
* lisp/ox-icalendar.el (org-icalendar-convert-timestamp):
Avoid direct calls of `encode-time', use `org-encode-time' instead.

Org supports Emacs-26, but the recommended way to call `encode-time'
changed in Emacs-27.  In Emacs-29 DST and TZ elements of the single list
arguments became optional.  In Org it is still convenient to call the
function with separate arguments without explicit DST and TZ arguments.
The `org-encode-time' should mitigate attempts to modernize Org code
directly in the Emacs repository.
---
 lisp/ol.el                     |  2 +-
 lisp/org-agenda.el             |  8 ++--
 lisp/org-capture.el            |  9 ++--
 lisp/org-clock.el              | 57 ++++++++++++------------
 lisp/org-colview.el            |  2 +-
 lisp/org-datetree.el           |  8 ++--
 lisp/org-element.el            | 27 ++++++------
 lisp/org-macro.el              |  2 +-
 lisp/org-macs.el               |  4 +-
 lisp/org.el                    | 79 ++++++++++++++++++----------------
 lisp/ox-icalendar.el           |  2 +-
 testing/lisp/test-org-clock.el |  8 ++--
 12 files changed, 108 insertions(+), 100 deletions(-)

diff --git a/lisp/ol.el b/lisp/ol.el
index 0de9c921b..3c3c71170 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -1618,7 +1618,7 @@ non-nil."
 	  (setq link
 		(format-time-string
 		 (car org-time-stamp-formats)
-		 (encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
+		 (org-encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
 	  (org-link-store-props :type "calendar" :date cd)))
 
        ((eq major-mode 'w3-mode)
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index 0479a0e1f..afc2a1015 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
@@ -5740,7 +5740,7 @@ displayed in agenda view."
 	    (substring
 	     (format-time-string
 	      (car org-time-stamp-formats)
-	      (encode-time	; DATE bound by calendar
+	      (org-encode-time	; DATE bound by calendar
 	       0 0 0 (nth 1 date) (car date) (nth 2 date)))
 	     1 11))
 	   "\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)"
@@ -6012,7 +6012,7 @@ then those holidays will be skipped."
 		   (substring
 		    (format-time-string
 		     (car org-time-stamp-formats)
-		     (encode-time  ; DATE bound by calendar
+		     (org-encode-time  ; DATE bound by calendar
 		      0 0 0 (nth 1 date) (car date) (nth 2 date)))
 		    1 11))))
 	 (org-agenda-search-headline-for-time nil)
@@ -11077,8 +11077,8 @@ The prefix arg is passed through to the command if possible."
 		     (ignore-errors
 		       (let* ((date (calendar-gregorian-from-absolute
 				     (+ (org-today) distance)))
-			      (time (encode-time 0 0 0 (nth 1 date) (nth 0 date)
-						 (nth 2 date))))
+			      (time (org-encode-time
+                                     0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
 			 (org-agenda-schedule nil time))))))))
 
 	(?f
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index 068e3eda2..eb45f7ef5 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1050,9 +1050,10 @@ Store them in the capture property list."
                       prompt-time
                     ;; Use 00:00 when no time is given for another
                     ;; date than today?
-                    (apply #'encode-time 0 0
-                           org-extend-today-until
-                           (cl-cdddr (decode-time prompt-time)))))
+                    (org-encode-time
+                     (apply #'list
+                            0 0 org-extend-today-until
+                            (cl-cdddr (decode-time prompt-time))))))
 		 (time-to-days prompt-time)))
 	      (t
 	       ;; Current date, possibly corrected for late night
@@ -1579,7 +1580,7 @@ by their respective `org-store-link-plist' properties if present."
 	 (time (let* ((c (or (org-capture-get :default-time) (current-time)))
 		      (d (decode-time c)))
 		 (if (< (nth 2 d) org-extend-today-until)
-		     (encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d))
+		     (org-encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d))
 		   c)))
 	 (v-t (format-time-string (org-time-stamp-format nil) time))
 	 (v-T (format-time-string (org-time-stamp-format t) time))
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index 9f80dea04..da5c310b7 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -1520,7 +1520,7 @@ The time is always returned as UTC."
 	     (day (nth 3 dt)))
 	(if (< hour org-extend-today-until) (setf (nth 3 dt) (1- day)))
 	(setf (nth 2 dt) org-extend-today-until)
-	(apply #'encode-time 0 0 (nthcdr 2 dt))))
+	(org-encode-time (apply #'list 0 0 (nthcdr 2 dt)))))
      ((or (equal cmt "all")
 	  (and (or (not cmt) (equal cmt "auto"))
 	       (not lr)))
@@ -2351,16 +2351,16 @@ have priority."
     (let* ((start (pcase key
 		    (`interactive (org-read-date nil t nil "Range start? "))
 		    (`untilnow nil)
-		    (_ (encode-time 0 m h d month y))))
+		    (_ (org-encode-time 0 m h d month y))))
 	   (end (pcase key
 		  (`interactive (org-read-date nil t nil "Range end? "))
 		  (`untilnow (current-time))
-		  (_ (encode-time 0
-				  m ;; (or m1 m)
-				  (or h1 h)
-				  (or d1 d)
-				  (or month1 month)
-				  (or y1 y)))))
+		  (_ (org-encode-time 0
+                                      m ;; (or m1 m)
+                                      (or h1 h)
+                                      (or d1 d)
+                                      (or month1 month)
+                                      (or y1 y)))))
 	   (text
 	    (pcase key
 	      ((or `day `today) (format-time-string "%A, %B %d, %Y" start))
@@ -2428,14 +2428,14 @@ the currently selected interval size."
 	  (cond
 	   (d (setq ins (format-time-string
 			 "%Y-%m-%d"
-			 (encode-time 0 0 0 (+ d n) nil y)))) ;; m
+			 (org-encode-time 0 0 0 (+ d n) nil y)))) ;; m
 	   ((and wp (string-match "w\\|W" wp) mw (> (length wp) 0))
 	    (require 'cal-iso)
 	    (setq date (calendar-gregorian-from-absolute
 			(calendar-iso-to-absolute (list (+ mw n) 1 y))))
 	    (setq ins (format-time-string
 		       "%G-W%V"
-		       (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
+		       (org-encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
 	   ((and wp (string-match "q\\|Q" wp) mw (> (length wp) 0))
 	    (require 'cal-iso)
 					; if the 4th + 1 quarter is requested we flip to the 1st quarter of the next year
@@ -2452,11 +2452,11 @@ the currently selected interval size."
 			(calendar-iso-to-absolute (org-quarter-to-date (+ mw n) y))))
 	    (setq ins (format-time-string
 		       (concat (number-to-string y) "-Q" (number-to-string (+ mw n)))
-		       (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
+		       (org-encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
 	   (mw
 	    (setq ins (format-time-string
 		       "%Y-%m"
-		       (encode-time 0 0 0 1 (+ mw n) y))))
+		       (org-encode-time 0 0 0 1 (+ mw n) y))))
 	   (y
 	    (setq ins (number-to-string (+ y n))))))
 	 (t (user-error "Cannot shift clocktable block")))
@@ -2844,7 +2844,7 @@ a number of clock tables."
           (pcase (if range (car range) (plist-get params :tstart))
             ((and (pred numberp) n)
              (pcase-let ((`(,m ,d ,y) (calendar-gregorian-from-absolute n)))
-               (apply #'encode-time (list 0 0 org-extend-today-until d m y))))
+               (org-encode-time 0 0 org-extend-today-until d m y)))
             (timestamp
 	     (seconds-to-time
 	      (org-matcher-time (or timestamp
@@ -2854,7 +2854,7 @@ a number of clock tables."
           (pcase (if range (nth 1 range) (plist-get params :tend))
             ((and (pred numberp) n)
              (pcase-let ((`(,m ,d ,y) (calendar-gregorian-from-absolute n)))
-               (apply #'encode-time (list 0 0 org-extend-today-until d m y))))
+               (org-encode-time 0 0 org-extend-today-until d m y)))
             (timestamp (seconds-to-time (org-matcher-time timestamp))))))
     (while (time-less-p start end)
       (unless (bolp) (insert "\n"))
@@ -2866,20 +2866,21 @@ a number of clock tables."
       ;; Compute NEXT, which is the end of the current clock table,
       ;; according to step.
       (let* ((next
-              (apply #'encode-time
-                     (pcase-let
-                         ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
-                       (pcase step
-                         (`day (list 0 0 org-extend-today-until (1+ d) m y))
-                         (`week
-                          (let ((offset (if (= dow week-start) 7
-                                          (mod (- week-start dow) 7))))
-                            (list 0 0 org-extend-today-until (+ d offset) m y)))
-                         (`semimonth (list 0 0 0
-                                           (if (< d 16) 16 1)
-                                           (if (< d 16) m (1+ m)) y))
-                         (`month (list 0 0 0 month-start (1+ m) y))
-                         (`year (list 0 0 org-extend-today-until 1 1 (1+ y)))))))
+              ;; In Emacs-27 and Emacs-28 `encode-time' does not support 6 elements
+              ;; list argument so `org-encode-time' can not be outside of `pcase'.
+              (pcase-let
+                  ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
+                (pcase step
+                  (`day (org-encode-time 0 0 org-extend-today-until (1+ d) m y))
+                  (`week
+                   (let ((offset (if (= dow week-start) 7
+                                   (mod (- week-start dow) 7))))
+                     (org-encode-time 0 0 org-extend-today-until (+ d offset) m y)))
+                  (`semimonth (org-encode-time 0 0 0
+                                               (if (< d 16) 16 1)
+                                               (if (< d 16) m (1+ m)) y))
+                  (`month (org-encode-time 0 0 0 month-start (1+ m) y))
+                  (`year (org-encode-time 0 0 org-extend-today-until 1 1 (1+ y))))))
              (table-begin (line-beginning-position 0))
 	     (step-time
               ;; Write clock table between START and NEXT.
diff --git a/lisp/org-colview.el b/lisp/org-colview.el
index c8443c135..c29a29d51 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -784,7 +784,7 @@ around it."
       (setq time-after (copy-sequence time))
       (setf (nth 3 time-before) (1- (nth 3 time)))
       (setf (nth 3 time-after) (1+ (nth 3 time)))
-      (mapcar (lambda (x) (format-time-string fmt (apply #'encode-time x)))
+      (mapcar (lambda (x) (format-time-string fmt (org-encode-time x)))
 	      (list time-before time time-after)))))
 
 (defun org-columns-open-link (&optional arg)
diff --git a/lisp/org-datetree.el b/lisp/org-datetree.el
index 30f5f99ae..f0c6bb7ba 100644
--- a/lisp/org-datetree.el
+++ b/lisp/org-datetree.el
@@ -137,7 +137,7 @@ will be built under the headline at point."
     (let* ((year (calendar-extract-year d))
 	   (month (calendar-extract-month d))
 	   (day (calendar-extract-day d))
-	   (time (encode-time 0 0 0 day month year))
+	   (time (org-encode-time 0 0 0 day month year))
 	   (iso-date (calendar-iso-from-absolute
 		      (calendar-absolute-from-gregorian d)))
 	   (weekyear (nth 2 iso-date))
@@ -197,14 +197,14 @@ inserted into the buffer."
     (when month
       (insert
        (if day
-	   (format-time-string "-%m-%d %A" (encode-time 0 0 0 day month year))
-	 (format-time-string "-%m %B" (encode-time 0 0 0 1 month year))))))
+	   (format-time-string "-%m-%d %A" (org-encode-time 0 0 0 day month year))
+	 (format-time-string "-%m %B" (org-encode-time 0 0 0 1 month year))))))
   (when (and day org-datetree-add-timestamp)
     (save-excursion
       (insert "\n")
       (org-indent-line)
       (org-insert-time-stamp
-       (encode-time 0 0 0 day month year)
+       (org-encode-time 0 0 0 day month year)
        nil
        (eq org-datetree-add-timestamp 'inactive))))
   (beginning-of-line))
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 3856079aa..c40761657 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -4041,12 +4041,12 @@ Assume point is at the beginning of the timestamp."
 				     (/= minute-start minute-end)))))
 	 (funcall
 	  build-ts-string
-	  (encode-time 0
-		       (or minute-start 0)
-		       (or hour-start 0)
-		       (org-element-property :day-start timestamp)
-		       (org-element-property :month-start timestamp)
-		       (org-element-property :year-start timestamp))
+	  (org-encode-time 0
+                           (or minute-start 0)
+                           (or hour-start 0)
+                           (org-element-property :day-start timestamp)
+                           (org-element-property :month-start timestamp)
+                           (org-element-property :year-start timestamp))
 	  (eq type 'active)
 	  (and hour-start minute-start)
 	  (and time-range-p hour-end)
@@ -4058,7 +4058,7 @@ Assume point is at the beginning of the timestamp."
 	     (hour-end (org-element-property :hour-end timestamp)))
 	 (concat
 	  (funcall
-	   build-ts-string (encode-time
+	   build-ts-string (org-encode-time
 			    0
 			    (or minute-start 0)
 			    (or hour-start 0)
@@ -4069,12 +4069,13 @@ Assume point is at the beginning of the timestamp."
 	   (and hour-start minute-start))
 	  "--"
 	  (funcall build-ts-string
-		   (encode-time 0
-				(or minute-end 0)
-				(or hour-end 0)
-				(org-element-property :day-end timestamp)
-				(org-element-property :month-end timestamp)
-				(org-element-property :year-end timestamp))
+		   (org-encode-time
+                    0
+                    (or minute-end 0)
+                    (or hour-end 0)
+                    (org-element-property :day-end timestamp)
+                    (org-element-property :month-end timestamp)
+                    (org-element-property :year-end timestamp))
 		   (eq type 'active-range)
 		   (and hour-end minute-end)))))
       (_ (org-element-property :raw-value timestamp)))))
diff --git a/lisp/org-macro.el b/lisp/org-macro.el
index 1d5cbe1b4..04e1ef36e 100644
--- a/lisp/org-macro.el
+++ b/lisp/org-macro.el
@@ -387,7 +387,7 @@ Return value as a string."
 				  (buffer-substring
 				   (point) (line-end-position)))))
 		       (when (cl-some #'identity time)
-			 (setq date (apply #'encode-time time))))))))
+			 (setq date (org-encode-time time))))))))
 	      (let ((proc (get-buffer-process buf)))
 		(while (and proc (accept-process-output proc .5 nil t)))))
 	  (kill-buffer buf))
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index aec3c3adb..0b6d57720 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1457,8 +1457,8 @@ following special strings: \"<now>\", \"<today>\",
 \"<tomorrow>\", and \"<yesterday>\".
 
 Return 0. if S is not recognized as a valid value."
-  (let ((today (float-time (apply #'encode-time
-				  (append '(0 0 0) (nthcdr 3 (decode-time)))))))
+  (let ((today (float-time (org-encode-time
+                            (append '(0 0 0) (nthcdr 3 (decode-time)))))))
     (save-match-data
       (cond
        ((string= s "<now>") (float-time))
diff --git a/lisp/org.el b/lisp/org.el
index 165c83609..3288bb7b8 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4767,8 +4767,10 @@ the rounding returns a past time."
     (if (< r 1)
 	now
       (let* ((time (decode-time now))
-	     (res (apply #'encode-time 0 (* r (round (nth 1 time) r))
-			 (nthcdr 2 time))))
+	     (res (org-encode-time
+                   (apply #'list
+                          0 (* r (round (nth 1 time) r))
+                          (nthcdr 2 time)))))
 	(if (or (not past) (time-less-p res now))
 	    res
 	  (time-subtract res (* r 60)))))))
@@ -8778,7 +8780,7 @@ nil or a string to be used for the todo mark." )
 	   (org-use-last-clock-out-time-as-effective-time
 	    (or (org-clock-get-last-clock-out-time) ct))
 	   ((and org-use-effective-time (< (nth 2 dct) org-extend-today-until))
-	    (encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)))
+	    (org-encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)))
 	   (t ct))))
     ct1))
 
@@ -9931,9 +9933,9 @@ WHAT entry will also be removed."
 	        (if (stringp time)
 		    ;; This is a string (relative or absolute), set
 		    ;; proper date.
-		    (apply #'encode-time
-		           (org-read-date-analyze
-			    time default-time (decode-time default-time)))
+		    (org-encode-time
+                     (org-read-date-analyze
+                      time default-time (decode-time default-time)))
 	          ;; If necessary, get the time from the user
 	          (or time (org-read-date nil 'to-time nil
 				       (cl-case what
@@ -13315,7 +13317,7 @@ user."
     (when (< (nth 2 org-defdecode) org-extend-today-until)
       (setf (nth 2 org-defdecode) -1)
       (setf (nth 1 org-defdecode) 59)
-      (setq org-def (apply #'encode-time org-defdecode))
+      (setq org-def (org-encode-time org-defdecode))
       (setq org-defdecode (decode-time org-def)))
     (let* ((timestr (format-time-string
 		     (if org-with-time "%Y-%m-%d %H:%M" "%Y-%m-%d")
@@ -13388,7 +13390,7 @@ user."
 		 "range representable on this machine"))
       (ding))
 
-    (setq final (apply #'encode-time final))
+    (setq final (org-encode-time final))
 
     (setq org-read-date-final-answer ans)
 
@@ -13425,7 +13427,7 @@ user."
 			  (and (boundp 'org-time-was-given) org-time-was-given))
 		      (cdr fmts)
 		    (car fmts)))
-	     (txt (format-time-string fmt (apply #'encode-time f)))
+	     (txt (format-time-string fmt (org-encode-time f)))
 	     (txt (if org-read-date-inactive (concat "[" (substring txt 1 -1) "]") txt))
 	     (txt (concat "=> " txt)))
 	(when (and org-end-time-was-given
@@ -13643,7 +13645,7 @@ user."
      ((and wday (not (nth 3 tl)))
       ;; Weekday was given, but no day, so pick that day in the week
       ;; on or after the derived date.
-      (setq wday1 (nth 6 (decode-time (encode-time 0 0 0 day month year))))
+      (setq wday1 (nth 6 (decode-time (org-encode-time 0 0 0 day month year))))
       (unless (equal wday wday1)
 	(setq day (+ day (% (- wday wday1 -7) 7))))))
     (when (and (boundp 'org-time-was-given)
@@ -13658,7 +13660,7 @@ user."
 	  (when (> year 2037)
 	    (setq year 2037 org-read-date-analyze-forced-year t)))
       (condition-case nil
-	  (ignore (encode-time second minute hour day month year))
+	  (ignore (org-encode-time second minute hour day month year))
 	(error
 	 (setq year (nth 5 org-defdecode))
 	 (setq org-read-date-analyze-forced-year t))))
@@ -13723,7 +13725,7 @@ Unless KEEPDATE is non-nil, update `org-ans2' to the cursor date."
     (eval form t)
     (when (and (not keepdate) (calendar-cursor-to-date))
       (let* ((date (calendar-cursor-to-date))
-	     (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	     (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
 	(setq org-ans2 (format-time-string "%Y-%m-%d" time))))
     (move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer))
     (select-window sw)
@@ -13735,7 +13737,7 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer."
   (interactive)
   (when (calendar-cursor-to-date)
     (let* ((date (calendar-cursor-to-date))
-	   (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	   (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
       (setq org-ans1 (format-time-string "%Y-%m-%d" time)))
     (when (active-minibuffer-window) (exit-minibuffer))))
 
@@ -13800,7 +13802,7 @@ The command returns the inserted time stamp."
 	  time (org-fix-decoded-time t1)
 	  str (org-add-props
 		  (format-time-string
-		   (substring tf 1 -1) (apply 'encode-time time))
+		   (substring tf 1 -1) (org-encode-time time))
 		  nil 'mouse-face 'highlight))
     (put-text-property beg end 'display str)))
 
@@ -13854,7 +13856,7 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer."
   (mouse-set-point ev)
   (when (calendar-cursor-to-date)
     (let* ((date (calendar-cursor-to-date))
-	   (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	   (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
       (setq org-ans1 (format-time-string "%Y-%m-%d" time)))
     (when (active-minibuffer-window) (exit-minibuffer))))
 
@@ -14055,7 +14057,7 @@ days in order to avoid rounding problems."
 
 (defun org-time-string-to-time (s)
   "Convert timestamp string S into internal time."
-  (apply #'encode-time (org-parse-time-string s)))
+  (org-encode-time (org-parse-time-string s)))
 
 (defun org-time-string-to-seconds (s)
   "Convert a timestamp string S into a number of seconds."
@@ -14118,7 +14120,7 @@ into a past one.  Any year larger than 99 is returned unchanged."
   "Return the time corresponding to date D.
 D may be an absolute day number, or a calendar-type list (month day year)."
   (when (numberp d) (setq d (calendar-gregorian-from-absolute d)))
-  (encode-time 0 0 0 (nth 1 d) (car d) (nth 2 d)))
+  (org-encode-time 0 0 0 (nth 1 d) (car d) (nth 2 d)))
 
 (defvar org-agenda-current-date)
 (defun org-calendar-holiday ()
@@ -14472,14 +14474,15 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like
 	  (setcar (cdr time0) (+ (nth 1 time0)
 				 (if (> n 0) (- rem) (- dm rem))))))
       (setq time
-	    (apply #'encode-time
-		   (or (car time0) 0)
-		   (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
-		   (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
-		   (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
-		   (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
-		   (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
-		   (nthcdr 6 time0)))
+	    (org-encode-time
+             (apply #'list
+                    (or (car time0) 0)
+                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
+                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
+                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
+                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
+                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
+                    (nthcdr 6 time0))))
       (when (and (memq timestamp? '(hour minute))
 		 extra
 		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
@@ -14497,7 +14500,7 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like
 	  (setcar time0 (or (car time0) 0))
 	  (setcar (nthcdr 1 time0) (or (nth 1 time0) 0))
 	  (setcar (nthcdr 2 time0) (or (nth 2 time0) 0))
-	  (setq time (apply 'encode-time time0))))
+	  (setq time (org-encode-time time0))))
       ;; Insert the new time-stamp, and ensure point stays in the same
       ;; category as before (i.e. not after the last position in that
       ;; category).
@@ -14643,7 +14646,7 @@ If there is already a time stamp at the cursor position, update it."
       (org-timestamp-change 0 'calendar)
     (let ((cal-date (org-get-date-from-calendar)))
       (org-insert-time-stamp
-       (encode-time 0 0 0 (nth 1 cal-date) (car cal-date) (nth 2 cal-date))))))
+       (org-encode-time 0 0 0 (nth 1 cal-date) (car cal-date) (nth 2 cal-date))))))
 
 (defcustom org-image-actual-width t
   "When non-nil, use the actual width of images when inlining them.
@@ -18204,14 +18207,14 @@ earliest time on the cursor date that Org treats as that date
     (cond
      ((eq major-mode 'calendar-mode)
       (setq date (calendar-cursor-to-date)
-	    defd (encode-time 0 (or mod 0) (or hod org-extend-today-until)
-			      (nth 1 date) (nth 0 date) (nth 2 date))))
+	    defd (org-encode-time 0 (or mod 0) (or hod org-extend-today-until)
+                                  (nth 1 date) (nth 0 date) (nth 2 date))))
      ((eq major-mode 'org-agenda-mode)
       (setq day (get-text-property (point) 'day))
       (when day
 	(setq date (calendar-gregorian-from-absolute day)
-	      defd (encode-time 0 (or mod 0) (or hod org-extend-today-until)
-				(nth 1 date) (nth 0 date) (nth 2 date))))))
+	      defd (org-encode-time 0 (or mod 0) (or hod org-extend-today-until)
+                                    (nth 1 date) (nth 0 date) (nth 2 date))))))
     (or defd (current-time))))
 
 (defun org-mark-subtree (&optional up)
@@ -19276,12 +19279,14 @@ return an active timestamp."
   "Convert TIMESTAMP object into an Emacs internal time value.
 Use end of date range or time range when END is non-nil.
 Otherwise, use its start."
-  (apply #'encode-time 0
-	 (mapcar
-	  (lambda (prop) (or (org-element-property prop timestamp) 0))
-	  (if end '(:minute-end :hour-end :day-end :month-end :year-end)
-	    '(:minute-start :hour-start :day-start :month-start
-			    :year-start)))))
+  (org-encode-time
+   (append '(0)
+           (mapcar
+            (lambda (prop) (or (org-element-property prop timestamp) 0))
+            (if end '(:minute-end :hour-end :day-end :month-end :year-end)
+              '(:minute-start :hour-start :day-start :month-start
+                              :year-start)))
+           '(nil -1 nil))))
 
 (defun org-timestamp-has-time-p (timestamp)
   "Non-nil when TIMESTAMP has a time specified."
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index abf31ce65..daac323cb 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -430,7 +430,7 @@ format (e.g. \"Europe/London\").  In either case, the value of
 					 t)))
       ;; Convert timestamp into internal time in order to use
       ;; `format-time-string' and fix any mistake (i.e. MI >= 60).
-      (encode-time 0 mi h d m y)
+      (org-encode-time 0 mi h d m y)
       (and (or (string-equal tz "UTC")
 	       (and (null tz)
 		    with-time-p
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index d2179e1ec..9650b8c8d 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -26,10 +26,10 @@ insert hours and minutes.
 Return the timestamp as a string."
   (org-element-interpret-data
    (let ((time (decode-time
-                (apply #'encode-time
-                       (mapcar (lambda (el) (or el 0))
-                               (org-read-date-analyze
-                                input nil (decode-time (current-time))))))))
+                (org-encode-time
+                 (mapcar (lambda (el) (or el 0))
+                         (org-read-date-analyze
+                          input nil (decode-time (current-time))))))))
      (list 'timestamp
            (list :type (if inactive 'inactive 'active)
                  :minute-start (and with-time (nth 1 time))
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v3] org-encode-time compatibility and convenience helper
  2022-05-03 12:14       ` [PATCH v3] " Max Nikulin
@ 2022-05-04  9:56         ` Ihor Radchenko
  2022-05-04 16:49           ` Max Nikulin
  2022-05-05 15:22           ` [PATCH v4] " Max Nikulin
  0 siblings, 2 replies; 17+ messages in thread
From: Ihor Radchenko @ 2022-05-04  9:56 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode, Paul Eggert

Max Nikulin <manikulin@gmail.com> writes:

> The attached patch set is assumed to be complete.

Thanks!

> I have no chance to thoroughly test it. Existing unit tests pass for 
> Emacs-26 and Emacs-27. Nothing has changed for Emacs-25, as for the 
> "main" branch one test fails. I have not tried Emacs-28 or the current 
> git version.

I tried to test your patch applied onto main with all the supported
Emacs versions (note that we do not need to support Emacs 25 anymore.
Emacs 28 is out). One test is failing:

Emacs 26, 27, 28, and 29:

1 unexpected results:
   FAILED  test-org-clock/clocktable/ranges

> In comparison to the previous patch version I have expanded the 
> docstring and added a bit more tests. I have tried to support recently 
> committed to Emacs 6-elements list for `encode-time', but I do not like 
> the following compile-time warning:
>
>> In toplevel form:
>> org-macs.el:1397:23:Warning: encode-time called with 1 argument, but requires
>>     6+

Since it is expected to fail in some Emacs versions, you can just wrap
the call into with-no-warnings:

(ignore-errors (with-no-warnings (encode-time '(0 0 0 1 1 1971))))

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3] org-encode-time compatibility and convenience helper
  2022-05-04  9:56         ` Ihor Radchenko
@ 2022-05-04 16:49           ` Max Nikulin
  2022-05-05 15:22           ` [PATCH v4] " Max Nikulin
  1 sibling, 0 replies; 17+ messages in thread
From: Max Nikulin @ 2022-05-04 16:49 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Paul Eggert

On 04/05/2022 16:56, Ihor Radchenko wrote:
> Max Nikulin writes:
> 
> 1 unexpected results:
>     FAILED  test-org-clock/clocktable/ranges

Thank you, Ihor. I run tests in containers with UTC timezone, so I did 
not notice such failure. So unit tests reveal a problem with tests.

`org-read-date-analyze' returns additional nil -1 nil now to allow 
passing its result directly to `encode-time'. 
`org-test-clock-create-timestamp' replaces nils by 0, so default 
timezone becomes UTC.

I will try to fix the test helper later.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-04  9:56         ` Ihor Radchenko
  2022-05-04 16:49           ` Max Nikulin
@ 2022-05-05 15:22           ` Max Nikulin
  2022-05-07  4:46             ` Ihor Radchenko
  2022-05-10 14:31             ` Max Nikulin
  1 sibling, 2 replies; 17+ messages in thread
From: Max Nikulin @ 2022-05-05 15:22 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Paul Eggert

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

On 04/05/2022 16:56, Ihor Radchenko wrote:
> Max Nikulin writes:
> 
> 1 unexpected results:
>     FAILED  test-org-clock/clocktable/ranges

Resetting timezone to UTC should be fixed in timestamps generated by a 
testing helper function. I was disappointed that `mapcar' can not be 
used with multiple lists, but I have found an old function created 
namely for zeroing nils in timestamps.

> Since it is expected to fail in some Emacs versions, you can just wrap
> the call into with-no-warnings:
> 
> (ignore-errors (with-no-warnings (encode-time '(0 0 0 1 1 1971))))

Thank you, I have added this trick.

I have created one more patch with tests quite close to the case caused 
this series.

It seems, no tests fail even in Australia/Eucla timezone famous for its 
45 min offset.

[-- Attachment #2: 0001-Use-unknown-DST-instead-of-standard-time-in-timestam.patch --]
[-- Type: text/x-patch, Size: 2862 bytes --]

From 485cd21366b651093c9c9c75e7398d662c99e92f Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 9 Apr 2022 00:17:09 -0700
Subject: [PATCH 1/7] Use unknown DST instead of standard time in timestamps

* lisp/ol.el (org-store-link): Prefer plain (encode-time ...)
to (apply 'encode-time ...), for speed.
* lisp/org-macs.el (org-parse-time-string): Return unknown DST,
not standard time.
* lisp/org.el (org-read-date-analyze): Return a timestamp with a DST
flag of -1 (unknown) rather than nil (standard time).

Max Nikulin:
A larger patch "Improve Org usage of timestamps" was suggested in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

Changes selected for this patch normalizes timestamp format:
if it is a list than it should contain 9 elements to be compatible
with Emacs-27 and Emacs-28 `encode-time' single argument, nil should not
be used for DST field since it means standard time while actual value
is unknown and should be guessed.

Ignacio Casso reported a problem with agenda
https://list.orgmode.org/PAXPR06MB7760238F410CBE3203F78EE0C61E9@PAXPR06MB7760.eurprd06.prod.outlook.com
due to Emacs commit dd0727e1ec1 changing Org code.  It was mostly reverted
by 8ef37913d3 (bug#54731).  Code in the Org repository did not have
the bug, but it safer to add protection against similar refactoring.
---
 lisp/ol.el       | 4 +---
 lisp/org-macs.el | 2 +-
 lisp/org.el      | 2 +-
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/lisp/ol.el b/lisp/ol.el
index d4bd0e40c..0de9c921b 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -1618,9 +1618,7 @@ non-nil."
 	  (setq link
 		(format-time-string
 		 (car org-time-stamp-formats)
-		 (apply 'encode-time
-			(list 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd)
-			      nil nil nil))))
+		 (encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
 	  (org-link-store-props :type "calendar" :date cd)))
 
        ((eq major-mode 'w3-mode)
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 8535bf2cd..241155064 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1412,7 +1412,7 @@ This should be a lot faster than the `parse-time-string'."
 	(string-to-number (match-string 4 s))
 	(string-to-number (match-string 3 s))
 	(string-to-number (match-string 2 s))
-	nil nil nil))
+	nil -1 nil))
 
 (defun org-matcher-time (s)
   "Interpret a time comparison value S as a floating point time.
diff --git a/lisp/org.el b/lisp/org.el
index 1d5fc3903..165c83609 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -13663,7 +13663,7 @@ user."
 	 (setq year (nth 5 org-defdecode))
 	 (setq org-read-date-analyze-forced-year t))))
     (setq org-read-date-analyze-futurep futurep)
-    (list second minute hour day month year)))
+    (list second minute hour day month year nil -1 nil)))
 
 (defvar parse-time-weekdays)
 (defun org-read-date-get-relative (s today default)
-- 
2.25.1


[-- Attachment #3: 0002-Fix-tests-for-org-parse-time-string-and-org-clock.patch --]
[-- Type: text/x-patch, Size: 2570 bytes --]

From 3a6d7320be7f56949be968fa49e1fb6a7fcb0732 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Tue, 3 May 2022 17:59:11 +0700
Subject: [PATCH 2/7] Fix tests for `org-parse-time-string' and `org-clock'

* testing/lisp/test-org.el (ert-deftest test-org/org-parse-time-string):
Update test expectations to use DST of -1 (guess) after fix of
`org-parse-time-string'.
* testing/lisp/test-org-clock.el (org-test-clock-create-timestamp):
Do not change timezone from nil to 0.  Prevent test failures in zones
other than UTC.
---
 testing/lisp/test-org-clock.el |  6 +++---
 testing/lisp/test-org.el       | 10 +++++-----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index d2179e1ec..89e0e2ade 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -27,9 +27,9 @@ Return the timestamp as a string."
   (org-element-interpret-data
    (let ((time (decode-time
                 (apply #'encode-time
-                       (mapcar (lambda (el) (or el 0))
-                               (org-read-date-analyze
-                                input nil (decode-time (current-time))))))))
+                       (org-fix-decoded-time
+                        (org-read-date-analyze
+                         input nil (decode-time (current-time))))))))
      (list 'timestamp
            (list :type (if inactive 'inactive 'active)
                  :minute-start (and with-time (nth 1 time))
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index ca0dc676b..4b5105a57 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -268,15 +268,15 @@
 (ert-deftest test-org/org-parse-time-string ()
   "Test `org-parse-time-string'."
   (should (equal (org-parse-time-string "2012-03-29 16:40")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "[2012-03-29 16:40]")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29 16:40>")
-		 '(0 40 16 29 3 2012 nil nil nil)))
+		 '(0 40 16 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29>")
-		 '(0 0 0 29 3 2012 nil nil nil)))
+		 '(0 0 0 29 3 2012 nil -1 nil)))
   (should (equal (org-parse-time-string "<2012-03-29>" t)
-		 '(0 nil nil 29 3 2012 nil nil nil))))
+		 '(0 nil nil 29 3 2012 nil -1 nil))))
 
 (ert-deftest test-org/closest-date ()
   "Test `org-closest-date' specifications."
-- 
2.25.1


[-- Attachment #4: 0003-Use-higher-level-helpers-instead-of-encode-time.patch --]
[-- Type: text/x-patch, Size: 3019 bytes --]

From a582bd2efadbb88f79592efd31023c816a86a3ae Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 9 Apr 2022 00:17:09 -0700
Subject: [PATCH 3/7] Use higher level helpers instead of `encode-time'

* lisp/org-clock.el (org-clock-sum)
(org-clock-update-time-maybe):
Prefer org-time-string-to-seconds to doing it by hand.
* lisp/org-macs.el (org-2ft):
Prefer org-time-string-to-seconds to doing it by hand.
* lisp/org-table.el (org-table-eval-formula):
Prefer org-time-string-to-time to doing it by hand.

Max Nikulin:
A larger patch "Improve Org usage of timestamps" was suggested in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54764#10

Only cosmetic changes are selected for this patch.
---
 lisp/org-clock.el | 17 ++++++-----------
 lisp/org-macs.el  |  2 +-
 lisp/org-table.el |  3 +--
 3 files changed, 8 insertions(+), 14 deletions(-)

diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index ec87aaf8a..9f80dea04 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -1913,13 +1913,10 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes."
 	  (cond
 	   ((match-end 2)
 	    ;; Two time stamps.
-	    (let* ((ts (float-time
-			(apply #'encode-time
-			       (save-match-data
-				 (org-parse-time-string (match-string 2))))))
-		   (te (float-time
-			(apply #'encode-time
-			       (org-parse-time-string (match-string 3)))))
+	    (let* ((ss (match-string 2))
+		   (se (match-string 3))
+		   (ts (org-time-string-to-seconds ss))
+		   (te (org-time-string-to-seconds se))
 		   (dt (- (if tend (min te tend) te)
 			  (if tstart (max ts tstart) ts))))
 	      (when (> dt 0) (cl-incf t1 (floor dt 60)))))
@@ -3051,10 +3048,8 @@ Otherwise, return nil."
 	  (end-of-line 1)
 	  (setq ts (match-string 1)
 		te (match-string 3))
-	  (setq s (- (float-time
-		      (apply #'encode-time (org-parse-time-string te)))
-		     (float-time
-		      (apply #'encode-time (org-parse-time-string ts))))
+	  (setq s (- (org-time-string-to-seconds te)
+		     (org-time-string-to-seconds ts))
 		neg (< s 0)
 		s (abs s)
 		h (floor (/ s 3600))
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 241155064..435c9ea30 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1355,7 +1355,7 @@ nil, just return 0."
    ((numberp s) s)
    ((stringp s)
     (condition-case nil
-	(float-time (apply #'encode-time (org-parse-time-string s)))
+	(org-time-string-to-seconds s)
       (error 0)))
    (t 0)))
 
diff --git a/lisp/org-table.el b/lisp/org-table.el
index 2f31bef56..40a425197 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -2607,8 +2607,7 @@ location of point."
 		     (format-time-string
 		      (org-time-stamp-format
 		       (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts))
-		      (apply #'encode-time
-			     (save-match-data (org-parse-time-string ts))))))
+		      (save-match-data (org-time-string-to-time ts)))))
 		 form t t))
 
 	  (setq ev (if (and duration (string-match "^[0-9]+:[0-9]+\\(?::[0-9]+\\)?$" form))
-- 
2.25.1


[-- Attachment #5: 0004-testing-lisp-Use-org-time-string-to-time.patch --]
[-- Type: text/x-patch, Size: 7991 bytes --]

From 69c6fe5f3e8c640b5050e4374b4f9d421752e1b3 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Mon, 2 May 2022 17:41:34 +0700
Subject: [PATCH 4/7] testing/lisp: Use `org-time-string-to-time'

* testing/lisp/test-org.el (test-org/org-read-date, test-org/deadline)
(test-org/schedule, test-org/time-stamp, test-org/timestamp-from-time):
* testing/org-test.el (org-test-at-time): Use `org-time-string-to-time'
instead of composition of `org-parse-time-string' and `encode-time'.

The actual goal is to prepare to replace `encode-time' by
`org-encode-time' compatibility and convenience macro.
---
 testing/lisp/test-org.el | 44 ++++++++++++++++------------------------
 testing/org-test.el      |  2 +-
 2 files changed, 19 insertions(+), 27 deletions(-)

diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 4b5105a57..8748cfa75 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -201,14 +201,14 @@
     (org-test-at-time "2014-03-04"
       (org-read-date
        t nil "+1y" nil
-       (apply #'encode-time (org-parse-time-string "2012-03-29"))))))
+       (org-time-string-to-time "2012-03-29")))))
   (should
    (equal
     "2013-03-29"
     (org-test-at-time "2014-03-04"
       (org-read-date
        t nil "++1y" nil
-       (apply #'encode-time (org-parse-time-string "2012-03-29"))))))
+       (org-time-string-to-time "2012-03-29")))))
   ;; When `org-read-date-prefer-future' is non-nil, prefer future
   ;; dates (relatively to now) when incomplete.  Otherwise, use
   ;; default date.
@@ -255,7 +255,7 @@
       (let ((org-read-date-prefer-future t))
 	(org-read-date
 	 t nil "1" nil
-	 (apply #'encode-time (org-parse-time-string "2012-03-29")))))))
+	 (org-time-string-to-time "2012-03-29"))))))
   (should
    (equal
     "2014-03-25"
@@ -263,7 +263,7 @@
       (let ((org-read-date-prefer-future t))
 	(org-read-date
 	 t nil "25" nil
-	 (apply #'encode-time (org-parse-time-string "2012-03-29"))))))))
+	 (org-time-string-to-time "2012-03-29")))))))
 
 (ert-deftest test-org/org-parse-time-string ()
   "Test `org-parse-time-string'."
@@ -5443,8 +5443,7 @@ Paragraph<point>"
    (equal "* H\nDEADLINE: <2012-03-29 -705d>"
 	  (cl-letf (((symbol-function 'org-read-date)
 		     (lambda (&rest args)
-		       (apply #'encode-time
-			      (org-parse-time-string "2014-03-04")))))
+		       (org-time-string-to-time "2014-03-04"))))
 	    (org-test-with-temp-text "* H\nDEADLINE: <2012-03-29>"
 	      (let ((org-adapt-indentation nil)
 		    (org-last-inserted-timestamp nil))
@@ -5453,8 +5452,7 @@ Paragraph<point>"
   (should-error
    (cl-letf (((symbol-function 'org-read-date)
 	      (lambda (&rest args)
-		(apply #'encode-time
-		       (org-parse-time-string "2014-03-04")))))
+		(org-time-string-to-time "2014-03-04"))))
      (org-test-with-temp-text "* H"
        (let ((org-adapt-indentation nil)
 	     (org-last-inserted-timestamp nil))
@@ -5557,8 +5555,7 @@ Paragraph<point>"
    (equal "* H\nSCHEDULED: <2012-03-29 -705d>"
 	  (cl-letf (((symbol-function 'org-read-date)
 		     (lambda (&rest args)
-		       (apply #'encode-time
-			      (org-parse-time-string "2014-03-04")))))
+		       (org-time-string-to-time "2014-03-04"))))
 	    (org-test-with-temp-text "* H\nSCHEDULED: <2012-03-29>"
 	      (let ((org-adapt-indentation nil)
 		    (org-last-inserted-timestamp nil))
@@ -5567,8 +5564,7 @@ Paragraph<point>"
   (should-error
    (cl-letf (((symbol-function 'org-read-date)
 	      (lambda (&rest args)
-		(apply #'encode-time
-		       (org-parse-time-string "2014-03-04")))))
+		(org-time-string-to-time "2014-03-04"))))
      (org-test-with-temp-text "* H"
        (let ((org-adapt-indentation nil)
 	     (org-last-inserted-timestamp nil))
@@ -7786,7 +7782,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; With a prefix argument, also insert time.
@@ -7796,8 +7792,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time
-			  (org-parse-time-string "2014-03-04 00:41")))))
+		   (org-time-string-to-time "2014-03-04 00:41"))))
 	(org-time-stamp '(4))
 	(buffer-string)))))
   ;; With two universal prefix arguments, insert an active timestamp
@@ -7816,7 +7811,7 @@ CLOSED: %s
     (org-test-with-temp-text "Te<point>xt"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil t)
 	(buffer-string)))))
   ;; When called from a timestamp, replace existing one.
@@ -7826,7 +7821,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu.>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   (should
@@ -7835,7 +7830,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu.>--<2014-03-04 tue.>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; When replacing a timestamp, preserve repeater, if any.
@@ -7845,7 +7840,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29<point> thu. +2y>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(org-time-stamp nil)
 	(buffer-string)))))
   ;; When called twice in a raw, build a date range.
@@ -7855,7 +7850,7 @@ CLOSED: %s
     (org-test-with-temp-text "<2012-03-29 thu.><point>"
       (cl-letf (((symbol-function 'org-read-date)
 		 (lambda (&rest args)
-		   (apply #'encode-time (org-parse-time-string "2014-03-04")))))
+		   (org-time-string-to-time "2014-03-04"))))
 	(let ((last-command 'org-time-stamp)
 	      (this-command 'org-time-stamp))
 	  (org-time-stamp nil))
@@ -8030,8 +8025,7 @@ CLOSED: %s
     "<2012-03-29 .+>"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))))))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")))))
   ;; When optional argument WITH-TIME is non-nil, provide time
   ;; information.
   (should
@@ -8039,8 +8033,7 @@ CLOSED: %s
     "<2012-03-29 .+ 16:40>"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")
       t))))
   ;; When optional argument INACTIVE is non-nil, return an inactive
   ;; timestamp.
@@ -8049,8 +8042,7 @@ CLOSED: %s
     "[2012-03-29 .+]"
     (org-element-interpret-data
      (org-timestamp-from-time
-      (apply #'encode-time
-	     (org-parse-time-string "<2012-03-29 Thu 16:40>"))
+      (org-time-string-to-time "<2012-03-29 Thu 16:40>")
       nil t)))))
 
 (ert-deftest test-org/timestamp-to-time ()
diff --git a/testing/org-test.el b/testing/org-test.el
index 0f1e254aa..d19c7f044 100644
--- a/testing/org-test.el
+++ b/testing/org-test.el
@@ -455,7 +455,7 @@ TIME can be a non-nil Lisp time value, or a string specifying a date and time."
 	(at (cl-gensym)))
     `(let* ((,tm ,time)
 	    (,at (if (stringp ,tm)
-		     (apply #'encode-time (org-parse-time-string ,tm))
+		     (org-time-string-to-time ,tm)
 		   ,tm)))
        (cl-letf
 	   ;; Wrap builtins whose behavior can depend on the current time.
-- 
2.25.1


[-- Attachment #6: 0005-org-macs.el-Introduce-a-helper-for-encode-time.patch --]
[-- Type: text/x-patch, Size: 6221 bytes --]

From da62b7543f0b74e016f314d67e774ae094d93034 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Fri, 8 Apr 2022 23:10:50 +0700
Subject: [PATCH 5/7] org-macs.el: Introduce a helper for `encode-time'

* lisp/org-macs.el (org-encode-time): New compatibility and convenience
helper macro to allow a list for time components or separate arguments
independently of Emacs version.
* testing/lisp/test-org.el (org-test-with-timezone): New macro to ensure
that some code is executed with certain TZ environment value and thus
particular daylight saving time or other time shift rules are active.
* testing/lisp/test-org.el (test-org/org-encode-time): Tests for various
ways to call `org-encode-time'.

Ensure recommended way to call `encode-time' for Emacs-27 and newer with
hope to avoid bugs due to attempts to modernize the code similar to
bug#54731.  6-elements list may be allowed as `encode-time' argument
since Emacs-29, see bug#54764.
---
 lisp/org-macs.el         | 35 ++++++++++++++++++++
 testing/lisp/test-org.el | 71 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 435c9ea30..8b6e8f351 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1389,6 +1389,41 @@ nil, just return 0."
 	(b (org-2ft b)))
     (and (> a 0) (> b 0) (\= a b))))
 
+(if (version< emacs-version "27.1")
+    (defmacro org-encode-time (&rest time)
+      (if (cdr time)
+          `(encode-time ,@time)
+        `(apply #'encode-time ,@time)))
+  (if (ignore-errors (with-no-warnings (encode-time '(0 0 0 1 1 1971))))
+      (defmacro org-encode-time (&rest time)
+        (pcase (length time) ; Emacs-29 since d75e2c12eb
+          (1 `(encode-time ,@time))
+          ((or 6 9) `(encode-time (list ,@time)))
+          (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                    (length time)))))
+    (defmacro org-encode-time (&rest time)
+      (pcase (length time)
+        (1 `(encode-time ,@time))
+        (6 `(encode-time (list ,@time nil -1 nil)))
+        (9 `(encode-time (list ,@time)))
+        (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
+                  (length time)))))))
+(put 'org-encode-time 'function-documentation
+     "Compatibility and convenience helper for `encode-time'.
+May be called with 9 components list (SECONDS ... YEAR IGNORED DST ZONE)
+as the recommended way since Emacs-27 or with 6 or 9 separate arguments
+similar to the only possible variant for Emacs-26 and earlier.
+6 elements list as the only argument causes wrong type argument till Emacs-29.
+
+Warning: use -1 for DST to guess the actual value, nil means no
+daylight saving time and may be wrong at particular time.
+
+DST value is ignored prior to Emacs-27.  Since Emacs-27 DST value matters
+even when multiple arguments is passed to this macro and such
+behavior is different from `encode-time'. See
+Info node `(elisp)Time Conversion' for details and caveats,
+preferably the latest version.")
+
 (defun org-parse-time-string (s &optional nodefault)
   "Parse Org time string S.
 
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 8748cfa75..6e5be2299 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -24,6 +24,20 @@
 
 (eval-and-compile (require 'cl-lib))
 
+\f
+;;; Helpers
+
+(defmacro org-test-with-timezone (tz &rest body)
+  "Evaluate BODY with TZ environment temporary set to the passed value."
+  (declare (indent 1))
+  (org-with-gensyms (tz-saved)
+    `(let ((,tz-saved (getenv "TZ")))
+       (unwind-protect
+           (progn
+             (setenv "TZ" ,tz)
+             ,@body)
+             (setenv "TZ" ,tz-saved)))))
+
 \f
 ;;; Comments
 
@@ -179,6 +193,63 @@
 \f
 ;;; Date and time analysis
 
+(ert-deftest test-org/org-encode-time ()
+  "Test various ways to call `org-encode-time'"
+  (org-test-with-timezone "UTC"
+    ;; list as the sole argument
+    (should (string-equal
+             "2022-03-24 23:30:01"
+             (format-time-string
+              "%F %T"
+              (org-encode-time '(1 30 23 24 3 2022 nil -1 nil)))))
+    ;; SECOND...YEAR
+    (should (string-equal
+             "2022-03-24 23:30:02"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 2 30 23 24 3 2022))))
+    ;; SECOND...YEAR IGNORED DST ZONE
+    (should (string-equal
+             "2022-03-24 23:30:03"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 3 30 23 24 3 2022 nil -1 nil))))
+    ;; function call
+    (should (string-equal
+             "2022-03-24 23:30:04"
+             (format-time-string
+              "%F %T"
+              (org-encode-time (apply #'list 4 30 23 '(24 3 2022 nil -1 nil))))))
+    ;; wrong number of arguments
+    (if (not (version< emacs-version "27.1"))
+        (should-error (string-equal
+                       "2022-03-24 23:30:05"
+                       (format-time-string
+                        "%F %T"
+                        (org-encode-time 5 30 23 24 3 2022 nil))))))
+  ;; daylight saving time
+  (if (not (version< emacs-version "27.1"))
+      ;; DST value is not ignored for multiple arguments unlike for `encode-time'
+      (should (string-equal
+               "2022-04-01 00:30:06 +0200 CEST"
+               (format-time-string
+                "%F %T %z %Z"
+                (org-encode-time 6 30 23 31 3 2022 nil nil "Europe/Madrid")
+                "Europe/Madrid")))
+    (should (string-equal
+             "2022-03-31 23:30:07 +0200 CEST"
+             (format-time-string
+              "%F %T %z %Z"
+              (org-encode-time 7 30 23 31 3 2022 nil t "Europe/Madrid")
+              "Europe/Madrid"))))
+  (org-test-with-timezone "Europe/Madrid"
+    ;; Standard time is not forced when DST is not specified
+    (should (string-equal
+             "2022-03-31 23:30:08"
+             (format-time-string
+              "%F %T"
+              (org-encode-time 8 30 23 31 3 2022))))))
+
 (ert-deftest test-org/org-read-date ()
   "Test `org-read-date' specifications."
   ;; Parse ISO date with abbreviated year and month.
-- 
2.25.1


[-- Attachment #7: 0006-Use-org-encode-time-helper-macro.patch --]
[-- Type: text/x-patch, Size: 27826 bytes --]

From 72d5936d1021ce0f5f81ad6feb8153009b91ade0 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Mon, 2 May 2022 21:19:59 +0700
Subject: [PATCH 6/7] Use `org-encode-time' helper macro

* lisp/ol.el (org-store-link):
* lisp/org-agenda.el (org-agenda-get-timestamps)
(org-agenda-get-progress, agenda-bulk-action):
* lisp/org-capture.el (org-capture-fill-template):
* lisp/org-clock.el (org-clock-get-sum-start)
(org-clock-special-range, org-clocktable-shift)
(org-clocktable-steps):
* lisp/org-colview.el (org-colview-construct-allowed-dates):
* lisp/org-datetree.el (org-datetree-find-iso-week-create)
(org-datetree-insert-line):
* lisp/org-element.el (org-element-timestamp-interpreter):
* lisp/org-macro.el (org-macro--vc-modified-time):
* lisp/org-macs.el (org-matcher-time):
* lisp/org.el (org-current-time, org-current-effective-time)
(org-add-planning-info, org-read-date, org-read-date-display)
(org-read-date-analyze, org-eval-in-calendar)
(org-calendar-select, org-display-custom-time)
(org-calendar-select-mouse, org-time-string-to-time)
(org-time-from-absolute, org-at-clock-log-p)
(org-date-from-calendar, org-get-cursor-date)
(org-timestamp-to-time):
* testing/lisp/test-org-clock.el (org-test-clock-create-timestamp):
* lisp/ox-icalendar.el (org-icalendar-convert-timestamp):
Avoid direct calls of `encode-time', use `org-encode-time' instead.

Org supports Emacs-26, but the recommended way to call `encode-time'
changed in Emacs-27.  In Emacs-29 DST and TZ elements of the single list
arguments became optional.  In Org it is still convenient to call the
function with separate arguments without explicit DST and TZ arguments.
The `org-encode-time' should mitigate attempts to modernize Org code
directly in the Emacs repository.
---
 lisp/ol.el                     |  2 +-
 lisp/org-agenda.el             |  8 ++--
 lisp/org-capture.el            |  9 ++--
 lisp/org-clock.el              | 57 ++++++++++++------------
 lisp/org-colview.el            |  2 +-
 lisp/org-datetree.el           |  8 ++--
 lisp/org-element.el            | 27 ++++++------
 lisp/org-macro.el              |  2 +-
 lisp/org-macs.el               |  4 +-
 lisp/org.el                    | 79 ++++++++++++++++++----------------
 lisp/ox-icalendar.el           |  2 +-
 testing/lisp/test-org-clock.el |  8 ++--
 12 files changed, 108 insertions(+), 100 deletions(-)

diff --git a/lisp/ol.el b/lisp/ol.el
index 0de9c921b..3c3c71170 100644
--- a/lisp/ol.el
+++ b/lisp/ol.el
@@ -1618,7 +1618,7 @@ non-nil."
 	  (setq link
 		(format-time-string
 		 (car org-time-stamp-formats)
-		 (encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
+		 (org-encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
 	  (org-link-store-props :type "calendar" :date cd)))
 
        ((eq major-mode 'w3-mode)
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index 0479a0e1f..afc2a1015 100644
--- a/lisp/org-agenda.el
+++ b/lisp/org-agenda.el
@@ -5740,7 +5740,7 @@ displayed in agenda view."
 	    (substring
 	     (format-time-string
 	      (car org-time-stamp-formats)
-	      (encode-time	; DATE bound by calendar
+	      (org-encode-time	; DATE bound by calendar
 	       0 0 0 (nth 1 date) (car date) (nth 2 date)))
 	     1 11))
 	   "\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)"
@@ -6012,7 +6012,7 @@ then those holidays will be skipped."
 		   (substring
 		    (format-time-string
 		     (car org-time-stamp-formats)
-		     (encode-time  ; DATE bound by calendar
+		     (org-encode-time  ; DATE bound by calendar
 		      0 0 0 (nth 1 date) (car date) (nth 2 date)))
 		    1 11))))
 	 (org-agenda-search-headline-for-time nil)
@@ -11077,8 +11077,8 @@ The prefix arg is passed through to the command if possible."
 		     (ignore-errors
 		       (let* ((date (calendar-gregorian-from-absolute
 				     (+ (org-today) distance)))
-			      (time (encode-time 0 0 0 (nth 1 date) (nth 0 date)
-						 (nth 2 date))))
+			      (time (org-encode-time
+                                     0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
 			 (org-agenda-schedule nil time))))))))
 
 	(?f
diff --git a/lisp/org-capture.el b/lisp/org-capture.el
index 068e3eda2..eb45f7ef5 100644
--- a/lisp/org-capture.el
+++ b/lisp/org-capture.el
@@ -1050,9 +1050,10 @@ Store them in the capture property list."
                       prompt-time
                     ;; Use 00:00 when no time is given for another
                     ;; date than today?
-                    (apply #'encode-time 0 0
-                           org-extend-today-until
-                           (cl-cdddr (decode-time prompt-time)))))
+                    (org-encode-time
+                     (apply #'list
+                            0 0 org-extend-today-until
+                            (cl-cdddr (decode-time prompt-time))))))
 		 (time-to-days prompt-time)))
 	      (t
 	       ;; Current date, possibly corrected for late night
@@ -1579,7 +1580,7 @@ by their respective `org-store-link-plist' properties if present."
 	 (time (let* ((c (or (org-capture-get :default-time) (current-time)))
 		      (d (decode-time c)))
 		 (if (< (nth 2 d) org-extend-today-until)
-		     (encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d))
+		     (org-encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d))
 		   c)))
 	 (v-t (format-time-string (org-time-stamp-format nil) time))
 	 (v-T (format-time-string (org-time-stamp-format t) time))
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index 9f80dea04..da5c310b7 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -1520,7 +1520,7 @@ The time is always returned as UTC."
 	     (day (nth 3 dt)))
 	(if (< hour org-extend-today-until) (setf (nth 3 dt) (1- day)))
 	(setf (nth 2 dt) org-extend-today-until)
-	(apply #'encode-time 0 0 (nthcdr 2 dt))))
+	(org-encode-time (apply #'list 0 0 (nthcdr 2 dt)))))
      ((or (equal cmt "all")
 	  (and (or (not cmt) (equal cmt "auto"))
 	       (not lr)))
@@ -2351,16 +2351,16 @@ have priority."
     (let* ((start (pcase key
 		    (`interactive (org-read-date nil t nil "Range start? "))
 		    (`untilnow nil)
-		    (_ (encode-time 0 m h d month y))))
+		    (_ (org-encode-time 0 m h d month y))))
 	   (end (pcase key
 		  (`interactive (org-read-date nil t nil "Range end? "))
 		  (`untilnow (current-time))
-		  (_ (encode-time 0
-				  m ;; (or m1 m)
-				  (or h1 h)
-				  (or d1 d)
-				  (or month1 month)
-				  (or y1 y)))))
+		  (_ (org-encode-time 0
+                                      m ;; (or m1 m)
+                                      (or h1 h)
+                                      (or d1 d)
+                                      (or month1 month)
+                                      (or y1 y)))))
 	   (text
 	    (pcase key
 	      ((or `day `today) (format-time-string "%A, %B %d, %Y" start))
@@ -2428,14 +2428,14 @@ the currently selected interval size."
 	  (cond
 	   (d (setq ins (format-time-string
 			 "%Y-%m-%d"
-			 (encode-time 0 0 0 (+ d n) nil y)))) ;; m
+			 (org-encode-time 0 0 0 (+ d n) nil y)))) ;; m
 	   ((and wp (string-match "w\\|W" wp) mw (> (length wp) 0))
 	    (require 'cal-iso)
 	    (setq date (calendar-gregorian-from-absolute
 			(calendar-iso-to-absolute (list (+ mw n) 1 y))))
 	    (setq ins (format-time-string
 		       "%G-W%V"
-		       (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
+		       (org-encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
 	   ((and wp (string-match "q\\|Q" wp) mw (> (length wp) 0))
 	    (require 'cal-iso)
 					; if the 4th + 1 quarter is requested we flip to the 1st quarter of the next year
@@ -2452,11 +2452,11 @@ the currently selected interval size."
 			(calendar-iso-to-absolute (org-quarter-to-date (+ mw n) y))))
 	    (setq ins (format-time-string
 		       (concat (number-to-string y) "-Q" (number-to-string (+ mw n)))
-		       (encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
+		       (org-encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date)))))
 	   (mw
 	    (setq ins (format-time-string
 		       "%Y-%m"
-		       (encode-time 0 0 0 1 (+ mw n) y))))
+		       (org-encode-time 0 0 0 1 (+ mw n) y))))
 	   (y
 	    (setq ins (number-to-string (+ y n))))))
 	 (t (user-error "Cannot shift clocktable block")))
@@ -2844,7 +2844,7 @@ a number of clock tables."
           (pcase (if range (car range) (plist-get params :tstart))
             ((and (pred numberp) n)
              (pcase-let ((`(,m ,d ,y) (calendar-gregorian-from-absolute n)))
-               (apply #'encode-time (list 0 0 org-extend-today-until d m y))))
+               (org-encode-time 0 0 org-extend-today-until d m y)))
             (timestamp
 	     (seconds-to-time
 	      (org-matcher-time (or timestamp
@@ -2854,7 +2854,7 @@ a number of clock tables."
           (pcase (if range (nth 1 range) (plist-get params :tend))
             ((and (pred numberp) n)
              (pcase-let ((`(,m ,d ,y) (calendar-gregorian-from-absolute n)))
-               (apply #'encode-time (list 0 0 org-extend-today-until d m y))))
+               (org-encode-time 0 0 org-extend-today-until d m y)))
             (timestamp (seconds-to-time (org-matcher-time timestamp))))))
     (while (time-less-p start end)
       (unless (bolp) (insert "\n"))
@@ -2866,20 +2866,21 @@ a number of clock tables."
       ;; Compute NEXT, which is the end of the current clock table,
       ;; according to step.
       (let* ((next
-              (apply #'encode-time
-                     (pcase-let
-                         ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
-                       (pcase step
-                         (`day (list 0 0 org-extend-today-until (1+ d) m y))
-                         (`week
-                          (let ((offset (if (= dow week-start) 7
-                                          (mod (- week-start dow) 7))))
-                            (list 0 0 org-extend-today-until (+ d offset) m y)))
-                         (`semimonth (list 0 0 0
-                                           (if (< d 16) 16 1)
-                                           (if (< d 16) m (1+ m)) y))
-                         (`month (list 0 0 0 month-start (1+ m) y))
-                         (`year (list 0 0 org-extend-today-until 1 1 (1+ y)))))))
+              ;; In Emacs-27 and Emacs-28 `encode-time' does not support 6 elements
+              ;; list argument so `org-encode-time' can not be outside of `pcase'.
+              (pcase-let
+                  ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
+                (pcase step
+                  (`day (org-encode-time 0 0 org-extend-today-until (1+ d) m y))
+                  (`week
+                   (let ((offset (if (= dow week-start) 7
+                                   (mod (- week-start dow) 7))))
+                     (org-encode-time 0 0 org-extend-today-until (+ d offset) m y)))
+                  (`semimonth (org-encode-time 0 0 0
+                                               (if (< d 16) 16 1)
+                                               (if (< d 16) m (1+ m)) y))
+                  (`month (org-encode-time 0 0 0 month-start (1+ m) y))
+                  (`year (org-encode-time 0 0 org-extend-today-until 1 1 (1+ y))))))
              (table-begin (line-beginning-position 0))
 	     (step-time
               ;; Write clock table between START and NEXT.
diff --git a/lisp/org-colview.el b/lisp/org-colview.el
index c8443c135..c29a29d51 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -784,7 +784,7 @@ around it."
       (setq time-after (copy-sequence time))
       (setf (nth 3 time-before) (1- (nth 3 time)))
       (setf (nth 3 time-after) (1+ (nth 3 time)))
-      (mapcar (lambda (x) (format-time-string fmt (apply #'encode-time x)))
+      (mapcar (lambda (x) (format-time-string fmt (org-encode-time x)))
 	      (list time-before time time-after)))))
 
 (defun org-columns-open-link (&optional arg)
diff --git a/lisp/org-datetree.el b/lisp/org-datetree.el
index 30f5f99ae..f0c6bb7ba 100644
--- a/lisp/org-datetree.el
+++ b/lisp/org-datetree.el
@@ -137,7 +137,7 @@ will be built under the headline at point."
     (let* ((year (calendar-extract-year d))
 	   (month (calendar-extract-month d))
 	   (day (calendar-extract-day d))
-	   (time (encode-time 0 0 0 day month year))
+	   (time (org-encode-time 0 0 0 day month year))
 	   (iso-date (calendar-iso-from-absolute
 		      (calendar-absolute-from-gregorian d)))
 	   (weekyear (nth 2 iso-date))
@@ -197,14 +197,14 @@ inserted into the buffer."
     (when month
       (insert
        (if day
-	   (format-time-string "-%m-%d %A" (encode-time 0 0 0 day month year))
-	 (format-time-string "-%m %B" (encode-time 0 0 0 1 month year))))))
+	   (format-time-string "-%m-%d %A" (org-encode-time 0 0 0 day month year))
+	 (format-time-string "-%m %B" (org-encode-time 0 0 0 1 month year))))))
   (when (and day org-datetree-add-timestamp)
     (save-excursion
       (insert "\n")
       (org-indent-line)
       (org-insert-time-stamp
-       (encode-time 0 0 0 day month year)
+       (org-encode-time 0 0 0 day month year)
        nil
        (eq org-datetree-add-timestamp 'inactive))))
   (beginning-of-line))
diff --git a/lisp/org-element.el b/lisp/org-element.el
index 3856079aa..c40761657 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -4041,12 +4041,12 @@ Assume point is at the beginning of the timestamp."
 				     (/= minute-start minute-end)))))
 	 (funcall
 	  build-ts-string
-	  (encode-time 0
-		       (or minute-start 0)
-		       (or hour-start 0)
-		       (org-element-property :day-start timestamp)
-		       (org-element-property :month-start timestamp)
-		       (org-element-property :year-start timestamp))
+	  (org-encode-time 0
+                           (or minute-start 0)
+                           (or hour-start 0)
+                           (org-element-property :day-start timestamp)
+                           (org-element-property :month-start timestamp)
+                           (org-element-property :year-start timestamp))
 	  (eq type 'active)
 	  (and hour-start minute-start)
 	  (and time-range-p hour-end)
@@ -4058,7 +4058,7 @@ Assume point is at the beginning of the timestamp."
 	     (hour-end (org-element-property :hour-end timestamp)))
 	 (concat
 	  (funcall
-	   build-ts-string (encode-time
+	   build-ts-string (org-encode-time
 			    0
 			    (or minute-start 0)
 			    (or hour-start 0)
@@ -4069,12 +4069,13 @@ Assume point is at the beginning of the timestamp."
 	   (and hour-start minute-start))
 	  "--"
 	  (funcall build-ts-string
-		   (encode-time 0
-				(or minute-end 0)
-				(or hour-end 0)
-				(org-element-property :day-end timestamp)
-				(org-element-property :month-end timestamp)
-				(org-element-property :year-end timestamp))
+		   (org-encode-time
+                    0
+                    (or minute-end 0)
+                    (or hour-end 0)
+                    (org-element-property :day-end timestamp)
+                    (org-element-property :month-end timestamp)
+                    (org-element-property :year-end timestamp))
 		   (eq type 'active-range)
 		   (and hour-end minute-end)))))
       (_ (org-element-property :raw-value timestamp)))))
diff --git a/lisp/org-macro.el b/lisp/org-macro.el
index 1d5cbe1b4..04e1ef36e 100644
--- a/lisp/org-macro.el
+++ b/lisp/org-macro.el
@@ -387,7 +387,7 @@ Return value as a string."
 				  (buffer-substring
 				   (point) (line-end-position)))))
 		       (when (cl-some #'identity time)
-			 (setq date (apply #'encode-time time))))))))
+			 (setq date (org-encode-time time))))))))
 	      (let ((proc (get-buffer-process buf)))
 		(while (and proc (accept-process-output proc .5 nil t)))))
 	  (kill-buffer buf))
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index 8b6e8f351..886a95bb2 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -1457,8 +1457,8 @@ following special strings: \"<now>\", \"<today>\",
 \"<tomorrow>\", and \"<yesterday>\".
 
 Return 0. if S is not recognized as a valid value."
-  (let ((today (float-time (apply #'encode-time
-				  (append '(0 0 0) (nthcdr 3 (decode-time)))))))
+  (let ((today (float-time (org-encode-time
+                            (append '(0 0 0) (nthcdr 3 (decode-time)))))))
     (save-match-data
       (cond
        ((string= s "<now>") (float-time))
diff --git a/lisp/org.el b/lisp/org.el
index 165c83609..3288bb7b8 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4767,8 +4767,10 @@ the rounding returns a past time."
     (if (< r 1)
 	now
       (let* ((time (decode-time now))
-	     (res (apply #'encode-time 0 (* r (round (nth 1 time) r))
-			 (nthcdr 2 time))))
+	     (res (org-encode-time
+                   (apply #'list
+                          0 (* r (round (nth 1 time) r))
+                          (nthcdr 2 time)))))
 	(if (or (not past) (time-less-p res now))
 	    res
 	  (time-subtract res (* r 60)))))))
@@ -8778,7 +8780,7 @@ nil or a string to be used for the todo mark." )
 	   (org-use-last-clock-out-time-as-effective-time
 	    (or (org-clock-get-last-clock-out-time) ct))
 	   ((and org-use-effective-time (< (nth 2 dct) org-extend-today-until))
-	    (encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)))
+	    (org-encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)))
 	   (t ct))))
     ct1))
 
@@ -9931,9 +9933,9 @@ WHAT entry will also be removed."
 	        (if (stringp time)
 		    ;; This is a string (relative or absolute), set
 		    ;; proper date.
-		    (apply #'encode-time
-		           (org-read-date-analyze
-			    time default-time (decode-time default-time)))
+		    (org-encode-time
+                     (org-read-date-analyze
+                      time default-time (decode-time default-time)))
 	          ;; If necessary, get the time from the user
 	          (or time (org-read-date nil 'to-time nil
 				       (cl-case what
@@ -13315,7 +13317,7 @@ user."
     (when (< (nth 2 org-defdecode) org-extend-today-until)
       (setf (nth 2 org-defdecode) -1)
       (setf (nth 1 org-defdecode) 59)
-      (setq org-def (apply #'encode-time org-defdecode))
+      (setq org-def (org-encode-time org-defdecode))
       (setq org-defdecode (decode-time org-def)))
     (let* ((timestr (format-time-string
 		     (if org-with-time "%Y-%m-%d %H:%M" "%Y-%m-%d")
@@ -13388,7 +13390,7 @@ user."
 		 "range representable on this machine"))
       (ding))
 
-    (setq final (apply #'encode-time final))
+    (setq final (org-encode-time final))
 
     (setq org-read-date-final-answer ans)
 
@@ -13425,7 +13427,7 @@ user."
 			  (and (boundp 'org-time-was-given) org-time-was-given))
 		      (cdr fmts)
 		    (car fmts)))
-	     (txt (format-time-string fmt (apply #'encode-time f)))
+	     (txt (format-time-string fmt (org-encode-time f)))
 	     (txt (if org-read-date-inactive (concat "[" (substring txt 1 -1) "]") txt))
 	     (txt (concat "=> " txt)))
 	(when (and org-end-time-was-given
@@ -13643,7 +13645,7 @@ user."
      ((and wday (not (nth 3 tl)))
       ;; Weekday was given, but no day, so pick that day in the week
       ;; on or after the derived date.
-      (setq wday1 (nth 6 (decode-time (encode-time 0 0 0 day month year))))
+      (setq wday1 (nth 6 (decode-time (org-encode-time 0 0 0 day month year))))
       (unless (equal wday wday1)
 	(setq day (+ day (% (- wday wday1 -7) 7))))))
     (when (and (boundp 'org-time-was-given)
@@ -13658,7 +13660,7 @@ user."
 	  (when (> year 2037)
 	    (setq year 2037 org-read-date-analyze-forced-year t)))
       (condition-case nil
-	  (ignore (encode-time second minute hour day month year))
+	  (ignore (org-encode-time second minute hour day month year))
 	(error
 	 (setq year (nth 5 org-defdecode))
 	 (setq org-read-date-analyze-forced-year t))))
@@ -13723,7 +13725,7 @@ Unless KEEPDATE is non-nil, update `org-ans2' to the cursor date."
     (eval form t)
     (when (and (not keepdate) (calendar-cursor-to-date))
       (let* ((date (calendar-cursor-to-date))
-	     (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	     (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
 	(setq org-ans2 (format-time-string "%Y-%m-%d" time))))
     (move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer))
     (select-window sw)
@@ -13735,7 +13737,7 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer."
   (interactive)
   (when (calendar-cursor-to-date)
     (let* ((date (calendar-cursor-to-date))
-	   (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	   (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
       (setq org-ans1 (format-time-string "%Y-%m-%d" time)))
     (when (active-minibuffer-window) (exit-minibuffer))))
 
@@ -13800,7 +13802,7 @@ The command returns the inserted time stamp."
 	  time (org-fix-decoded-time t1)
 	  str (org-add-props
 		  (format-time-string
-		   (substring tf 1 -1) (apply 'encode-time time))
+		   (substring tf 1 -1) (org-encode-time time))
 		  nil 'mouse-face 'highlight))
     (put-text-property beg end 'display str)))
 
@@ -13854,7 +13856,7 @@ This is used by `org-read-date' in a temporary keymap for the calendar buffer."
   (mouse-set-point ev)
   (when (calendar-cursor-to-date)
     (let* ((date (calendar-cursor-to-date))
-	   (time (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
+	   (time (org-encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date))))
       (setq org-ans1 (format-time-string "%Y-%m-%d" time)))
     (when (active-minibuffer-window) (exit-minibuffer))))
 
@@ -14055,7 +14057,7 @@ days in order to avoid rounding problems."
 
 (defun org-time-string-to-time (s)
   "Convert timestamp string S into internal time."
-  (apply #'encode-time (org-parse-time-string s)))
+  (org-encode-time (org-parse-time-string s)))
 
 (defun org-time-string-to-seconds (s)
   "Convert a timestamp string S into a number of seconds."
@@ -14118,7 +14120,7 @@ into a past one.  Any year larger than 99 is returned unchanged."
   "Return the time corresponding to date D.
 D may be an absolute day number, or a calendar-type list (month day year)."
   (when (numberp d) (setq d (calendar-gregorian-from-absolute d)))
-  (encode-time 0 0 0 (nth 1 d) (car d) (nth 2 d)))
+  (org-encode-time 0 0 0 (nth 1 d) (car d) (nth 2 d)))
 
 (defvar org-agenda-current-date)
 (defun org-calendar-holiday ()
@@ -14472,14 +14474,15 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like
 	  (setcar (cdr time0) (+ (nth 1 time0)
 				 (if (> n 0) (- rem) (- dm rem))))))
       (setq time
-	    (apply #'encode-time
-		   (or (car time0) 0)
-		   (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
-		   (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
-		   (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
-		   (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
-		   (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
-		   (nthcdr 6 time0)))
+	    (org-encode-time
+             (apply #'list
+                    (or (car time0) 0)
+                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
+                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
+                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
+                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
+                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
+                    (nthcdr 6 time0))))
       (when (and (memq timestamp? '(hour minute))
 		 extra
 		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
@@ -14497,7 +14500,7 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like
 	  (setcar time0 (or (car time0) 0))
 	  (setcar (nthcdr 1 time0) (or (nth 1 time0) 0))
 	  (setcar (nthcdr 2 time0) (or (nth 2 time0) 0))
-	  (setq time (apply 'encode-time time0))))
+	  (setq time (org-encode-time time0))))
       ;; Insert the new time-stamp, and ensure point stays in the same
       ;; category as before (i.e. not after the last position in that
       ;; category).
@@ -14643,7 +14646,7 @@ If there is already a time stamp at the cursor position, update it."
       (org-timestamp-change 0 'calendar)
     (let ((cal-date (org-get-date-from-calendar)))
       (org-insert-time-stamp
-       (encode-time 0 0 0 (nth 1 cal-date) (car cal-date) (nth 2 cal-date))))))
+       (org-encode-time 0 0 0 (nth 1 cal-date) (car cal-date) (nth 2 cal-date))))))
 
 (defcustom org-image-actual-width t
   "When non-nil, use the actual width of images when inlining them.
@@ -18204,14 +18207,14 @@ earliest time on the cursor date that Org treats as that date
     (cond
      ((eq major-mode 'calendar-mode)
       (setq date (calendar-cursor-to-date)
-	    defd (encode-time 0 (or mod 0) (or hod org-extend-today-until)
-			      (nth 1 date) (nth 0 date) (nth 2 date))))
+	    defd (org-encode-time 0 (or mod 0) (or hod org-extend-today-until)
+                                  (nth 1 date) (nth 0 date) (nth 2 date))))
      ((eq major-mode 'org-agenda-mode)
       (setq day (get-text-property (point) 'day))
       (when day
 	(setq date (calendar-gregorian-from-absolute day)
-	      defd (encode-time 0 (or mod 0) (or hod org-extend-today-until)
-				(nth 1 date) (nth 0 date) (nth 2 date))))))
+	      defd (org-encode-time 0 (or mod 0) (or hod org-extend-today-until)
+                                    (nth 1 date) (nth 0 date) (nth 2 date))))))
     (or defd (current-time))))
 
 (defun org-mark-subtree (&optional up)
@@ -19276,12 +19279,14 @@ return an active timestamp."
   "Convert TIMESTAMP object into an Emacs internal time value.
 Use end of date range or time range when END is non-nil.
 Otherwise, use its start."
-  (apply #'encode-time 0
-	 (mapcar
-	  (lambda (prop) (or (org-element-property prop timestamp) 0))
-	  (if end '(:minute-end :hour-end :day-end :month-end :year-end)
-	    '(:minute-start :hour-start :day-start :month-start
-			    :year-start)))))
+  (org-encode-time
+   (append '(0)
+           (mapcar
+            (lambda (prop) (or (org-element-property prop timestamp) 0))
+            (if end '(:minute-end :hour-end :day-end :month-end :year-end)
+              '(:minute-start :hour-start :day-start :month-start
+                              :year-start)))
+           '(nil -1 nil))))
 
 (defun org-timestamp-has-time-p (timestamp)
   "Non-nil when TIMESTAMP has a time specified."
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index abf31ce65..daac323cb 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -430,7 +430,7 @@ format (e.g. \"Europe/London\").  In either case, the value of
 					 t)))
       ;; Convert timestamp into internal time in order to use
       ;; `format-time-string' and fix any mistake (i.e. MI >= 60).
-      (encode-time 0 mi h d m y)
+      (org-encode-time 0 mi h d m y)
       (and (or (string-equal tz "UTC")
 	       (and (null tz)
 		    with-time-p
diff --git a/testing/lisp/test-org-clock.el b/testing/lisp/test-org-clock.el
index 89e0e2ade..14c9ec274 100644
--- a/testing/lisp/test-org-clock.el
+++ b/testing/lisp/test-org-clock.el
@@ -26,10 +26,10 @@ insert hours and minutes.
 Return the timestamp as a string."
   (org-element-interpret-data
    (let ((time (decode-time
-                (apply #'encode-time
-                       (org-fix-decoded-time
-                        (org-read-date-analyze
-                         input nil (decode-time (current-time))))))))
+                (org-encode-time
+                 (org-fix-decoded-time
+                  (org-read-date-analyze
+                   input nil (decode-time (current-time))))))))
      (list 'timestamp
            (list :type (if inactive 'inactive 'active)
                  :minute-start (and with-time (nth 1 time))
-- 
2.25.1


[-- Attachment #8: 0007-test-org.el-Add-some-tests-for-org-test-with-timezon.patch --]
[-- Type: text/x-patch, Size: 1672 bytes --]

From a66b15b87d06d087e4d82c226dd5f18a3c478af0 Mon Sep 17 00:00:00 2001
From: Max Nikulin <manikulin@gmail.com>
Date: Thu, 5 May 2022 21:54:03 +0700
Subject: [PATCH 7/7] test-org.el: Add some tests for `org-test-with-timezone'

* testing/lisp/test-org.el (test-org/org-time-string-to-time):
Check that no daylight saving time value is forced for
`org-parse-time-string' and `org-encode-time' calls.
---
 testing/lisp/test-org.el | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 6e5be2299..f79d00c88 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -250,6 +250,26 @@
               "%F %T"
               (org-encode-time 8 30 23 31 3 2022))))))
 
+(ert-deftest test-org/org-time-string-to-time ()
+  "Test `org-time-string-to-time' around DST transition."
+  (org-test-with-timezone "UTC"
+    (should (string-equal
+             "2022-03-31 23:31:00"
+             (format-time-string
+              "%F %T"
+              (org-time-string-to-time "2022-03-31 23:31")))))
+  (org-test-with-timezone "Europe/Madrid"
+    (should (string-equal
+             "2022-03-24 23:32:00 +0100 CET"
+             (format-time-string
+              "%F %T %z %Z"
+              (org-time-string-to-time "2022-03-24 23:32"))))
+    (should (string-equal
+             "2022-03-31 23:33:00 +0200 CEST"
+             (format-time-string
+              "%F %T %z %Z"
+              (org-time-string-to-time "2022-03-31 23:33"))))))
+
 (ert-deftest test-org/org-read-date ()
   "Test `org-read-date' specifications."
   ;; Parse ISO date with abbreviated year and month.
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-05 15:22           ` [PATCH v4] " Max Nikulin
@ 2022-05-07  4:46             ` Ihor Radchenko
  2022-07-17  8:50               ` Ihor Radchenko
  2022-05-10 14:31             ` Max Nikulin
  1 sibling, 1 reply; 17+ messages in thread
From: Ihor Radchenko @ 2022-05-07  4:46 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode, Paul Eggert

Max Nikulin <manikulin@gmail.com> writes:

> On 04/05/2022 16:56, Ihor Radchenko wrote:
>> Max Nikulin writes:
>> 
>> 1 unexpected results:
>>     FAILED  test-org-clock/clocktable/ranges
>
> Resetting timezone to UTC should be fixed in timestamps generated by a 
> testing helper function. I was disappointed that `mapcar' can not be 
> used with multiple lists, but I have found an old function created 
> namely for zeroing nils in timestamps.

FYI, there is cl-mapcar, but your solution here is also fine.

> I have created one more patch with tests quite close to the case caused 
> this series.

Thanks! LGTM.

All the tests are passing now on my side.

I'll postpone merging to next week to make sure that there are no more
comments.

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-05 15:22           ` [PATCH v4] " Max Nikulin
  2022-05-07  4:46             ` Ihor Radchenko
@ 2022-05-10 14:31             ` Max Nikulin
  2022-05-11 13:20               ` Ihor Radchenko
  1 sibling, 1 reply; 17+ messages in thread
From: Max Nikulin @ 2022-05-10 14:31 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Paul Eggert

On 05/05/2022 22:22, Max Nikulin wrote:
> On 04/05/2022 16:56, Ihor Radchenko wrote:
>> Max Nikulin writes:
>>
> Resetting timezone to UTC should be fixed in timestamps generated by a 
> testing helper function. I was disappointed that `mapcar' can not be 
> used with multiple lists, but I have found an old function created 
> namely for zeroing nils in timestamps.

Most of the changes are rather trivial. Some code I do not like though 
or unsure that I chose a proper approach.

> +      (defmacro org-encode-time (&rest time)
> +        (pcase (length time) ; Emacs-29 since d75e2c12eb
> +          (1 `(encode-time ,@time))
> +          ((or 6 9) `(encode-time (list ,@time)))
> +          (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
> +                    (length time)))))

Should it be something like the following?

(signal 'wrong-type-argument (list '(1 6 9) (length time)))

or even

(signal 'wrong-type-argument
          (list '(lambda (n-args) (memq n-args) '(1 6 9)) (length time)))

Usually "wrong type argument" errors give no clue even related to called 
function til enabling enter debugger on error and realizing how to 
reproduce the problem.

> diff --git a/lisp/org-clock.el b/lisp/org-clock.el
> index 9f80dea04..da5c310b7 100644
> --- a/lisp/org-clock.el
> +++ b/lisp/org-clock.el
> @@ -2866,20 +2866,21 @@ a number of clock tables."
>        ;; Compute NEXT, which is the end of the current clock table,
>        ;; according to step.
>        (let* ((next
> -              (apply #'encode-time
> -                     (pcase-let
> -                         ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
> -                       (pcase step
> -                         (`day (list 0 0 org-extend-today-until (1+ d) m y))
> -                         (`week
> -                          (let ((offset (if (= dow week-start) 7
> -                                          (mod (- week-start dow) 7))))
> -                            (list 0 0 org-extend-today-until (+ d offset) m y)))
> -                         (`semimonth (list 0 0 0
> -                                           (if (< d 16) 16 1)
> -                                           (if (< d 16) m (1+ m)) y))
> -                         (`month (list 0 0 0 month-start (1+ m) y))
> -                         (`year (list 0 0 org-extend-today-until 1 1 (1+ y)))))))
> +              ;; In Emacs-27 and Emacs-28 `encode-time' does not support 6 elements
> +              ;; list argument so `org-encode-time' can not be outside of `pcase'.
> +              (pcase-let
> +                  ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
> +                (pcase step
> +                  (`day (org-encode-time 0 0 org-extend-today-until (1+ d) m y))
> +                  (`week
> +                   (let ((offset (if (= dow week-start) 7
> +                                   (mod (- week-start dow) 7))))
> +                     (org-encode-time 0 0 org-extend-today-until (+ d offset) m y)))
> +                  (`semimonth (org-encode-time 0 0 0
> +                                               (if (< d 16) 16 1)
> +                                               (if (< d 16) m (1+ m)) y))
> +                  (`month (org-encode-time 0 0 0 month-start (1+ m) y))
> +                  (`year (org-encode-time 0 0 org-extend-today-until 1 1 (1+ y))))))
>               (table-begin (line-beginning-position 0))
>  	     (step-time
>                ;; Write clock table between START and NEXT.

I do not like repeating of `org-encode-time' but do not see another way 
till Emacs-29 will become the lowest supported version.

> diff --git a/lisp/org.el b/lisp/org.el
> index 165c83609..3288bb7b8 100644
> --- a/lisp/org.el
> +++ b/lisp/org.el
> @@ -14472,14 +14474,15 @@ When SUPPRESS-TMP-DELAY is non-nil, suppress delays like
>  	  (setcar (cdr time0) (+ (nth 1 time0)
>  				 (if (> n 0) (- rem) (- dm rem))))))
>        (setq time
> -	    (apply #'encode-time
> -		   (or (car time0) 0)
> -		   (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
> -		   (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
> -		   (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
> -		   (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
> -		   (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
> -		   (nthcdr 6 time0)))
> +	    (org-encode-time
> +             (apply #'list
> +                    (or (car time0) 0)
> +                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
> +                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
> +                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
> +                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
> +                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
> +                    (nthcdr 6 time0))))
>        (when (and (memq timestamp? '(hour minute))
>  		 extra
>  		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))

I am tempting to write something like

   (let* ((ts (copy-sequence time0))
	 (ord (memq timestamp? '(year month day hour minute)))
	 (field (and ord (nthcdr (length ord) ts))))
     (when field
       (setcar field (+ (car field) n)))
     (org-encode-time ts))

but I am afraid it will make the code rather obscure.

> @@ -19276,12 +19279,14 @@ return an active timestamp."
>    "Convert TIMESTAMP object into an Emacs internal time value.
>  Use end of date range or time range when END is non-nil.
>  Otherwise, use its start."
> -  (apply #'encode-time 0
> -	 (mapcar
> -	  (lambda (prop) (or (org-element-property prop timestamp) 0))
> -	  (if end '(:minute-end :hour-end :day-end :month-end :year-end)
> -	    '(:minute-start :hour-start :day-start :month-start
> -			    :year-start)))))
> +  (org-encode-time
> +   (append '(0)
> +           (mapcar
> +            (lambda (prop) (or (org-element-property prop timestamp) 0))
> +            (if end '(:minute-end :hour-end :day-end :month-end :year-end)
> +              '(:minute-start :hour-start :day-start :month-start
> +                              :year-start)))
> +           '(nil -1 nil))))
>  
>  (defun org-timestamp-has-time-p (timestamp)
>    "Non-nil when TIMESTAMP has a time specified."

Hardly may be considered as an example of elegant code.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-10 14:31             ` Max Nikulin
@ 2022-05-11 13:20               ` Ihor Radchenko
  2022-05-13 15:14                 ` Max Nikulin
  0 siblings, 1 reply; 17+ messages in thread
From: Ihor Radchenko @ 2022-05-11 13:20 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode, Paul Eggert

Max Nikulin <manikulin@gmail.com> writes:

>> +      (defmacro org-encode-time (&rest time)
>> +        (pcase (length time) ; Emacs-29 since d75e2c12eb
>> +          (1 `(encode-time ,@time))
>> +          ((or 6 9) `(encode-time (list ,@time)))
>> +          (_ (error "`org-encode-time' may be called with 1, 6, or 9 arguments but %d given"
>> +                    (length time)))))
>
> Should it be something like the following?
>
> (signal 'wrong-type-argument (list '(1 6 9) (length time)))
>
> or even
>
> (signal 'wrong-type-argument
>           (list '(lambda (n-args) (memq n-args) '(1 6 9)) (length time)))
>
> Usually "wrong type argument" errors give no clue even related to called 
> function til enabling enter debugger on error and realizing how to 
> reproduce the problem.

The current error is fine. I'd rather propose Emacs to change the "wrong
type argument" message to mention the function name.

>> +              ;; In Emacs-27 and Emacs-28 `encode-time' does not support 6 elements
>> +              ;; list argument so `org-encode-time' can not be outside of `pcase'.
>> +              (pcase-let
>> +                  ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
>> +                (pcase step
>> +                  (`day (org-encode-time 0 0 org-extend-today-until (1+ d) m y))
>> +                  (`week
>> +                   (let ((offset (if (= dow week-start) 7
>> +                                   (mod (- week-start dow) 7))))
>> +                     (org-encode-time 0 0 org-extend-today-until (+ d offset) m y)))
>> +                  (`semimonth (org-encode-time 0 0 0
>> +                                               (if (< d 16) 16 1)
>> +                                               (if (< d 16) m (1+ m)) y))
>> +                  (`month (org-encode-time 0 0 0 month-start (1+ m) y))
>> +                  (`year (org-encode-time 0 0 org-extend-today-until 1 1 (1+ y))))))
>
> I do not like repeating of `org-encode-time' but do not see another way 
> till Emacs-29 will become the lowest supported version.

This is fine. AFAIK, other parts of time handling code is full of conds
and pcases.

>> +	    (org-encode-time
>> +             (apply #'list
>> +                    (or (car time0) 0)
>> +                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
>> +                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
>> +                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
>> +                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
>> +                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
>> +                    (nthcdr 6 time0))))
>>        (when (and (memq timestamp? '(hour minute))
>>  		 extra
>>  		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
>
> I am tempting to write something like
>
>    (let* ((ts (copy-sequence time0))
> 	 (ord (memq timestamp? '(year month day hour minute)))
> 	 (field (and ord (nthcdr (length ord) ts))))
>      (when field
>        (setcar field (+ (car field) n)))
>      (org-encode-time ts))
>
> but I am afraid it will make the code rather obscure.

Yes, the second version is rather hard to understand. The proper
solution would be writing (or using) some high-level time handling
library and then using it in Org. Then, we would not need to deal with
low-level time representations so frequently.

>> +  (org-encode-time
>> +   (append '(0)
>> +           (mapcar
>> +            (lambda (prop) (or (org-element-property prop timestamp) 0))
>> +            (if end '(:minute-end :hour-end :day-end :month-end :year-end)
>> +              '(:minute-start :hour-start :day-start :month-start
>> +                              :year-start)))
>> +           '(nil -1 nil))))
>>  
>>  (defun org-timestamp-has-time-p (timestamp)
>>    "Non-nil when TIMESTAMP has a time specified."
>
> Hardly may be considered as an example of elegant code.

It is ok. You also could do it as

`(0 ,@(mapcar (lambda (prop) ...) (if ...)) nil -1 nil)

AFAIK, there is nothing much you can improve further without using
function composition from dash.el.

Best,
Ihor




^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-11 13:20               ` Ihor Radchenko
@ 2022-05-13 15:14                 ` Max Nikulin
  2022-05-14  6:06                   ` Ihor Radchenko
  0 siblings, 1 reply; 17+ messages in thread
From: Max Nikulin @ 2022-05-13 15:14 UTC (permalink / raw)
  To: Ihor Radchenko; +Cc: emacs-orgmode, Paul Eggert

On 11/05/2022 20:20, Ihor Radchenko wrote:
> Max Nikulin writes:
> 
>>> +              ;; In Emacs-27 and Emacs-28 `encode-time' does not support 6 elements
>>> +              ;; list argument so `org-encode-time' can not be outside of `pcase'.
>>> +              (pcase-let
>>> +                  ((`(,_ ,_ ,_ ,d ,m ,y ,dow . ,_) (decode-time start)))
>>> +                (pcase step
>>> +                  (`day (org-encode-time 0 0 org-extend-today-until (1+ d) m y))
>>> +                  (`week
>>> +                   (let ((offset (if (= dow week-start) 7
>>> +                                   (mod (- week-start dow) 7))))
>>> +                     (org-encode-time 0 0 org-extend-today-until (+ d offset) m y)))
>>> +                  (`semimonth (org-encode-time 0 0 0
>>> +                                               (if (< d 16) 16 1)
>>> +                                               (if (< d 16) m (1+ m)) y))
>>> +                  (`month (org-encode-time 0 0 0 month-start (1+ m) y))
>>> +                  (`year (org-encode-time 0 0 org-extend-today-until 1 1 (1+ y))))))
>>
>> I do not like repeating of `org-encode-time' but do not see another way
>> till Emacs-29 will become the lowest supported version.
> 
> This is fine. AFAIK, other parts of time handling code is full of conds
> and pcases.

I mean that before my patch there was single `encode-time' outside of 
`pcase', I replace `list' by `org-encode-time' inside each pattern.

>>> +	    (org-encode-time
>>> +             (apply #'list
>>> +                    (or (car time0) 0)
>>> +                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
>>> +                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
>>> +                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
>>> +                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
>>> +                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
>>> +                    (nthcdr 6 time0))))
>>>         (when (and (memq timestamp? '(hour minute))
>>>   		 extra
>>>   		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
>>
>> I am tempting to write something like
>>
>>     (let* ((ts (copy-sequence time0))
>> 	 (ord (memq timestamp? '(year month day hour minute)))
>> 	 (field (and ord (nthcdr (length ord) ts))))
>>       (when field
>>         (setcar field (+ (car field) n)))
>>       (org-encode-time ts))
>>
>> but I am afraid it will make the code rather obscure.
> 
> Yes, the second version is rather hard to understand. The proper
> solution would be writing (or using) some high-level time handling
> library and then using it in Org. Then, we would not need to deal with
> low-level time representations so frequently.

 From my point of view

   (cl-mapcar
    (lambda (value part)
      (if (and part (eq part timestamp?))
	 (+ n value)
        value))
    time0 '(second minute hour day month year nil nil nil))

is better than the original code, but...

Nicolas Goaziou to emacs-orgmode. [Patch] to correctly sort the items 
with emphasis marks in a list. Mon, 19 Apr 2021 18:08:17 +0200.
https://list.orgmode.org/87r1j6b6ku.fsf@nicolasgoaziou.fr
 >
> I stay away from CL as much as possible, otherwise newcomers will have
> to learn two languages to start contributing, Elisp and CL (cl-loop,
> ewww). CL is still necessary however, as we cannot use `seq' yet.


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-13 15:14                 ` Max Nikulin
@ 2022-05-14  6:06                   ` Ihor Radchenko
  0 siblings, 0 replies; 17+ messages in thread
From: Ihor Radchenko @ 2022-05-14  6:06 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode, Paul Eggert

Max Nikulin <manikulin@gmail.com> writes:

>>> I do not like repeating of `org-encode-time' but do not see another way
>>> till Emacs-29 will become the lowest supported version.
>> 
>> This is fine. AFAIK, other parts of time handling code is full of conds
>> and pcases.
>
> I mean that before my patch there was single `encode-time' outside of 
> `pcase', I replace `list' by `org-encode-time' inside each pattern.

I do not see it is a big deal. At least, readability of the code did not
degrade, IMHO.

>>>> +	    (org-encode-time
>>>> +             (apply #'list
>>>> +                    (or (car time0) 0)
>>>> +                    (+ (if (eq timestamp? 'minute) n 0) (nth 1 time0))
>>>> +                    (+ (if (eq timestamp? 'hour) n 0)   (nth 2 time0))
>>>> +                    (+ (if (eq timestamp? 'day) n 0)    (nth 3 time0))
>>>> +                    (+ (if (eq timestamp? 'month) n 0)  (nth 4 time0))
>>>> +                    (+ (if (eq timestamp? 'year) n 0)   (nth 5 time0))
>>>> +                    (nthcdr 6 time0))))
>>>>         (when (and (memq timestamp? '(hour minute))
>>>>   		 extra
>>>>   		 (string-match "-\\([012][0-9]\\):\\([0-5][0-9]\\)" extra))
>
>  From my point of view
>
>    (cl-mapcar
>     (lambda (value part)
>       (if (and part (eq part timestamp?))
> 	 (+ n value)
>         value))
>     time0 '(second minute hour day month year nil nil nil))
>
> is better than the original code, but...
>
> Nicolas Goaziou to emacs-orgmode. [Patch] to correctly sort the items 
> with emphasis marks in a list. Mon, 19 Apr 2021 18:08:17 +0200.
> https://list.orgmode.org/87r1j6b6ku.fsf@nicolasgoaziou.fr
>  >
>> I stay away from CL as much as possible, otherwise newcomers will have
>> to learn two languages to start contributing, Elisp and CL (cl-loop,
>> ewww). CL is still necessary however, as we cannot use `seq' yet.

I think that Nicolas was mostly referring to things like cl-loop, which
introduce a whole new piece of language concepts into Elisp.

cl-mapcar is merely a convenience function. Just as any other Elisp
function. It behaves without surprises within the general Elisp
programming concepts. I do not see why we should not use it if it
improves the code clarity.

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v4] org-encode-time compatibility and convenience helper
  2022-05-07  4:46             ` Ihor Radchenko
@ 2022-07-17  8:50               ` Ihor Radchenko
  0 siblings, 0 replies; 17+ messages in thread
From: Ihor Radchenko @ 2022-07-17  8:50 UTC (permalink / raw)
  To: Max Nikulin; +Cc: emacs-orgmode, Paul Eggert

Ihor Radchenko <yantar92@gmail.com> writes:

> All the tests are passing now on my side.
>
> I'll postpone merging to next week to make sure that there are no more
> comments.

Applied onto main via e08ce5b27, ae1db7df3, 8908a1bda, f3802b017,
a4105d094, a18969768, and 132a9d304 after resolving a trivial merge
conflict.

Best,
Ihor


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2022-07-17  8:50 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-11 15:22 [DRAFT][PATCH] org-encode-time compatibility and convenience helper Max Nikulin
2022-04-11 17:43 ` Paul Eggert
2022-04-23  8:25 ` Ihor Radchenko
2022-04-23 19:37   ` Paul Eggert
2022-04-24  3:35     ` Ihor Radchenko
2022-04-24 11:34   ` [DRAFT][PATCH v2] " Max Nikulin
2022-04-26  9:07     ` Ihor Radchenko
2022-05-03 12:14       ` [PATCH v3] " Max Nikulin
2022-05-04  9:56         ` Ihor Radchenko
2022-05-04 16:49           ` Max Nikulin
2022-05-05 15:22           ` [PATCH v4] " Max Nikulin
2022-05-07  4:46             ` Ihor Radchenko
2022-07-17  8:50               ` Ihor Radchenko
2022-05-10 14:31             ` Max Nikulin
2022-05-11 13:20               ` Ihor Radchenko
2022-05-13 15:14                 ` Max Nikulin
2022-05-14  6:06                   ` Ihor Radchenko

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.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).