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)