On 09/04/2022 14:52, Paul Eggert wrote:
On 4/7/22 05:37, Max Nikulin wrote:

 From my point of view it is better to change implementation of `encode-time' so that it may accept 6-component list SECOND...YEAR. It should not add noticeable performance penalty but makes the function more convenient in use.

Unfortunately it makes the function more convenient to use incorrectly. This was part of the motivation for the API change. The obsolescent calling convention has no way to deal with ambiguous timestamps like 2022-11-06 01:30 when TZ="America/Los_Angeles". Org mode surely has bugs in this area, although I don't have time to scout them out.

Handling DST is a step forward, it is an important case. Unfortunately there are enough peculiarities in the time zoo. I do not think new interface can be used correctly in the following case of time transition with no change of DST:

#+name: encode-time-and-format
#+begin_src elisp :var time=nil :var tz=() :var dst=-1
  (let* ((seconds-year
          (reverse (mapcar #'string-to-number (split-string time "[- :]"))))
         (time-list (append seconds-year (list nil dst tz))))
    (format-time-string "%F %T %Z %z" (encode-time time-list) tz))
#+end_src

zdump -v Africa/Juba
...
Africa/Juba Sun Jan 31 20:59:59 2021 UT = Sun Jan 31 23:59:59 2021 EAT isdst=0 gmtoff=10800 Africa/Juba Sun Jan 31 21:00:00 2021 UT = Sun Jan 31 23:00:00 2021 CAT isdst=0 gmtoff=7200

#+call: encode-time-and-format(time="2021-01-31 23:30:00", tz="Africa/Juba", dst=-1)

#+RESULTS:
: 2021-01-31 23:30:00 CAT +0200

#+call: encode-time-and-format(time="2021-01-31 23:30:00", tz="Africa/Juba", dst=())

#+RESULTS:
: 2021-01-31 23:30:00 CAT +0200

#+call: encode-time-and-format(time="2021-01-31 23:30:00", tz="Africa/Juba", dst='t) : Debugger entered--Lisp error: (error "Specified time is not representable")


I do not see a way to get 23:30 EAT +0300. For you example with regular DST transition there is no any problem:


#+call: encode-time-and-format(time="2022-11-06 01:30:00", tz="America/Los_Angeles", dst=-1)

#+RESULTS:
: 2022-11-06 01:30:00 PDT -0700

#+call: encode-time-and-format(time="2022-11-06 01:30:00", tz="America/Los_Angeles", dst=())

#+RESULTS:
: 2022-11-06 01:30:00 PST -0800

#+call: encode-time-and-format(time="2022-11-06 01:30:00", tz="America/Los_Angeles", dst='t)

#+RESULTS:
: 2022-11-06 01:30:00 PDT -0700

PS. Org mode usually uses encode-time for calendrical calculations. This is dicey, as some days don't exist (for example, December 30, 2011 does not exist if TZ="Pacific/Apia", because Samoa moved across the International Date Line that day). And it's also dicey when Org mode uses 00:00:00 (midnight at the start of the day) as a timestamp representing the entire day, as it's all too common for midnight to not exist (or to be duplicated) due to a DST transition. Generally speaking, when Org mode is doing calendrical calculations it should use calendrical functions rather than encode-time+decode-time, which are best used for time calculations not calendar calculations. (I realize that fixing this in Org would be nontrivial; perhaps I should file this "PS" as an Org bug report for whoever has time to fix it....)

Then `encode-time' should only accept time zone as time offset and should not allow default or named value that may be ambiguous. However my opinion is that is should be possible to provide hints to `encode-time' to get deterministic behavior in the case of time transitions.

diff --git a/lisp/org/org-compat.el b/lisp/org/org-compat.el
index 819ce74d93..247373d6b9 100644
--- a/lisp/org/org-compat.el
+++ b/lisp/org/org-compat.el
@@ -115,6 +115,27 @@ org-table1-hline-regexp
   (defun org-time-convert-to-list (time)
     (seconds-to-time (float-time time))))
+;; Like Emacs 27+ `encode-time' with one argument.
+(if (ignore-errors (encode-time (decode-time)))
+    (defsubst org-encode-time-1 (time)
+      (encode-time time))
+  (defun org-encode-time-1 (time)
+    (let ((dst-zone (nthcdr 7 time)))
+      (unless (consp (cdr dst-zone))
+       (signal wrong-type-argument (list time)))
+      (let ((etime (apply #'encode-time time))
+           (dst (car dst-zone))
+           (zone (cadr dst-zone)))
+       (when (and (symbolp dst) (not (integerp zone)) (not (consp zone)))
+         (let* ((detime (decode-time etime))
+                (dedst (nth 7 detime)))
+           (when (and (not (eq dedst dst)) (symbolp dedst))
+             ;; Assume one-hour DST and adjust the timestamp.
+             (setq etime (time-add etime (seconds-to-time
+                                          (- (if dedst 3600 0)
+                                             (if dst 3600 0))))))))

I am against this workaround. It fixes (to some extent) usual DST transitions, but it adds another bug

Australia/Lord_Howe Sat Apr 2 14:59:59 2022 UT = Sun Apr 3 01:59:59 2022 +11 isdst=1 gmtoff=39600 Australia/Lord_Howe Sat Apr 2 15:00:00 2022 UT = Sun Apr 3 01:30:00 2022 +1030 isdst=0 gmtoff=37800 Australia/Lord_Howe Sat Oct 1 15:29:59 2022 UT = Sun Oct 2 01:59:59 2022 +1030 isdst=0 gmtoff=37800 Australia/Lord_Howe Sat Oct 1 15:30:00 2022 UT = Sun Oct 2 02:30:00 2022 +11 isdst=1 gmtoff=39600

+       etime))))
+
 ;; `newline-and-indent' did not take a numeric argument before 27.1.
 (if (version< emacs-version "27")
     (defsubst org-newline-and-indent (&optional _arg)

Reply via email to