Thanks a lot for the response, Ihor!
> we currently do not accept LLM contributions
Thank you for the clarification. I agree that the implementation should
be done from scratch once we settle on all the details.
> Is there any particular reason you did not use the format we settled
> upon in the threads you linked to?
No particular reason. I did not pay enough attention to that part of
the thread, assuming that the discussion might no longer be relevant
due to age.
Upon re-reading the thread, the argument in favor of the @ syntax makes
sense to me.
I would like, however, to respond to the other aspects of the proposed
TZSPEC.
** Pure numeric TZSPEC, @+02, @+0230 **
I originally left numeric offsets out because my main use case was human
planning, where IANA zones are usually the right representation.
AFAIU, numeric offsets can be useful for recorded facts, imported
timestamps, logs, and cases where the exact instant or observed offset
matters independently of future timezone-rule changes. So supporting
@+02, @+0230, and @Z makes sense to me.
** Mixed offset + zone **
I am less convinced about the mixed offset+zone forms.
For planning, I would expect the IANA zone to be the source of truth,
because it is what lets Org preserve intent across DST transitions and
future timezone-rule changes. For recording past events, the numeric
offset is enough to preserve the observed instant. If the original
zone name is also useful as human-facing context, could we keep it in
the existing trailing timestamp syntax instead of making it part of
TZSPEC?
For example:
<2026-07-19 Sun 12:00 @+0200 Europe/Kyiv>
My concern is that @+02,Europe/Kyiv and the ! variants would
significantly complicate the implementation. They require parsing and
storing two timezone-like values, validating them against each other,
deciding which one wins on mismatch, preserving that decision through
editing, and making sure all timestamp operations follow the same
conflict policy. I do not yet see a use case that justifies that
complexity.
> > #+begin_src elisp
> > (:instant TIME
> > :zone ZONE
> > :effective-zone EFFECTIVE-ZONE
> > :granularity GRANULARITY)
> > #+end_src
>
> Do we really need it? Can't we derive the time from
> :year-start/:month-start/:day-start/:hour-start/:minute-start?
I feel that all arithmetic, comparison, agenda sorting, and export
should rely on a normalized form. This is the surest way to avoid
mistakes around timezone conversion and DST transitions.
We could parse to the existing timestamp plist and normalize every time
an operation needs it. However, to me it is easier to reason about the
code if we parse to the normalized form internally and treat the
existing timestamp plist as one possible representation of that
normalized data. The public API can still expose the existing plist
shape, but the operations that need time semantics should go through
the normalized representation.
> Also, what about ranges?
A range is just two normalized forms
(:start (:instant TIME :zone ZONE ...)
:end (:instant TIME :zone ZONE ...))
Current proof-of-concept implementation supports that.
> > =:effective-zone=
>
> Do we need to store it?
> We can derive the timezone from the environment any time.
> Ideally, the parser should only store what is in Org document.
I opted for storing :effective-zone in the normalized form to avoid
re-deriving it every time I need it. I do not have a strong argument
for storing it.
** Summary **
I think the two points where we are not yet on the same page are:
- whether Org should use the normalized internal form throughout
timestamp operations, or keep the existing timestamp plist as the
main representation and normalize only immediately before each
operation that needs it;
- whether mixed offset+zone TZSPEC forms, such as @+02,Europe/Kyiv,
should be part of the design.
Please let me know your thoughts on these two points. Once we settle
them, I will work on a fresh, clean implementation.
Best regards,
Dmytro
—
em-dashes are my own