[Datetime-SIG] Timeline arithmetic?

Carl Meyer carl at oddbird.net
Sun Sep 13 08:16:23 CEST 2015

Hi Tim,

On 09/10/2015 08:41 PM, Tim Peters wrote:
> It's become beyond obvious that I'll never be able to make enough time
> to respond to all of these, so I'll address just this for now. because
> it's impossible to make progress on anything unless there's agreement
> on what technical terms mean:
> [Carl Meyer <carl at oddbird.net>]
>>>> If you are doing any kind of "integer arithmetic on POSIX timestamps", you
>>>> are _always_ doing timeline arithmetic.
> [Tim]
>>> True.
> [Carl]
>>>> Classic arithmetic may be many things, but the one thing it definitively is
>>>> _not_ is "arithmetic on POSIX timestamps."
> [Tim]
>>> False.  UTC is an eternally-fixed-offset zone.  There are no
>>> transitions to be accounted for in UTC.  Classic and timeline
>>> arithmetic are exactly the same thing in any eternally-fixed-offset
>>> zone.  Because POSIX timestamps _are_ "in UTC", any arithmetic
>>> performed on one is being done in UTC too.  Your illustration next
>>> goes way beyond anything I could possibly read as doing arithmetic on
>>> POSIX timestamps:
> [Carl]
>> Translation: "I refuse to countenance the possibility of Model A."
> Not at all.  I've tried several times to get it across in English, so
> this time I'll try code instead:
>     def dt_add(dt, td, timeline=False):
>         ofs = dt.utcoffset()
>         as_utc = dt.replace(tzinfo=timezone.utc)
>         # and the following is identical to converting to
>         # a timestamp, "using POSIX timestamp arithmetic",
>         # then converting back to calendar notation
>         as_utc -= ofs
>         as_utc += td
>         if timeline:
>             return as_utc.astimezone(dt.tzinfo)
>         else: # classic
>             return (as_utc + ofs).replace(tzinfo=dt.tzinfo)

Well, sure. Of course it is possible to use "arithmetic on POSIX
timestamps" within an implementation of either kind of arithmetic, if
you try hard enough; I've never said anything to the contrary (that
would be a provably silly thing to say).

What your code does make clear is that if you convert from a DST-using
timezone to a POSIX timestamp, do "arithmetic on POSIX timestamps" and
then do a normal (what you would in any other context call a "correct")
conversion back to the first timezone afterwards, the result you get is
timeline arithmetic. Sure, if you do a specific sort of weird (what you
would in any other context call "wrong") conversion from the POSIX
timestamp back to the other timezone afterward, then you can get classic
arithmetic instead. I'm not sure what you think that demonstrates. I
think it demonstrates that both timeline and classic arithmetic _can_ be
described in terms that include "arithmetic on POSIX timestamps," but
timeline arithmetic is much more naturally seen that way.

Your original assertion was that "Classic arithmetic is equivalent to
doing integer arithmetic on integer POSIX timestamps" as a justification
for why datetime chose classic arithmetic, implying that classic
arithmetic is somehow _more_ or _more naturally_ seen as "equivalent to
integer arithmetic on integer POSIX timestamps" than timeline
arithmetic. I found that assertion puzzling, and I still do.

I'd still conclude the same thing I already said in an earlier reply:

So, "timeline arithmetic is just arithmetic on POSIX timestamps" means
viewing all aware datetimes as isomorphic to POSIX timestamps.

"Classic arithmetic is just arithmetic on POSIX timestamps" means
viewing aware datetimes as naive datetimes which one can pretend are in
a hypothetical (maybe UTC, if you like) fixed-offset timezone which is
isomorphic to actual POSIX timestamps (even though their actual timezone
may not be fixed-offset).

I accept that those are both true and useful in the implementation of
their respective model. I just don't think either one is inherently
obvious or useful as a justification of their respective mental models;
rather, which one you find "obvious" just reveals your preferred mental

> That adds an aware datetime to a timedelta, doing either classic or
> timeline arithmetic depending on the optional flag.  If you want to
> claim this doesn't do either kind of arithmetic correctly, prove it
> with a specific example

I'm not sure why you'd think I'd have any issue with that code, or any
desire to prove it wrong.

> I believe you have _pictured_ the POSIX timestamp number line
> annotated with local calendar notations in your head, but those labels
> have nothing to do with the timestamp arithmetic.

It would be more accurate to say that a Model A view pictures only a
single timeline, which is physical (Newtonian) time. A point on that
timeline is an instant. Any given instant is annotated with any number
of labels, each one a unique and unambiguous description of that instant
in some labeling system. A labeling system can be very simple (e.g.
POSIX timestamps), less simple (proleptic Gregorian in UTC, or to a
lesser extent any fixed-offset timezone), or slightly ridiculous
(timezones with folds and gaps, where now we need a `fold` attribute or
an explicit offset at each instant or something similar to keep each
label unique and unambiguous). This mental model implies (and requires)
that all of these labeling systems are isomorphic to each other and to
the physical-time timeline, and that arithmetic in any of them is
isomorphic to arithmetic in any other (and is thus obviously timeline

Really my only point in this entire thread has been that this model
(contrary to some of the denigration of it on this mailing list) is
actually quite intuitive, not difficult to teach, and possible to do all
sorts of useful work in (_even_ when you have to also teach pytz's
unfortunate API for it). If you can agree with that - great, we're done
here. If you don't agree with that, we may as well still be done,
because I have too much personal experience suggesting it to be true for
you to be likely able to convince me otherwise :-)

I've also come to recognize, through this thread, that Model B (where
the "local clock time in a given timezone" "timeline" is elevated to
sort-of-equal status with the physical timeline, rather than just
considered a weird complex labeling system for physical time) is also
useful (more useful for some tasks) and makes intuitive sense too.

> 1. The "as_utc -= ofs" line is theoretically impure, because it's
> treating a local time _as if_ it were a UTC time.  There's no real way
> around that.  We have to convert from local to UTC _somehow_, and
> POSIX dodges the issue by providing mktime() to do that "by magic".
> Here we're _inside_ the sausage factory, doing it ourselves.  Some rat
> guts are visible at this level.  If you look inside a C mktime()
> implementation, you'll find rat guts all over that too.

This seems like a really hand-wavy rationalization of an operation that
can only really be described as an incorrect timezone conversion. Of
course that incorrect timezone conversion operation is useful for
implementing classic arithmetic in the way you've implemented it, but
taken out of that context it's just an incorrect conversion. The reason
you _need_ that incorrect conversion is because for some reason you're
really wanting to do your arithmetic in terms of POSIX timestamps (which
are defined as being in UTC), but you don't _really_ want correct
conversion to UTC and back (because if you do that, you'll get timeline

> But it's no problem for Guido ;-)  We just set the hands on a UTC
> clock to match the local clock, then move the hands on the UTC clock
> by the amount the local clock is "ahead of" or "behind" UTC.  In that
> way you can indeed picture the operation as being entirely "in UTC".

Sure, you can, if you're motivated enough :-)

> 2. This would be a foolish _implementation_ of classic arithmetic, but
> not for semantic reasons.  It's just grossly inefficient.  Stare at
> the code, and in the classic case it subtracts the UTC offset at first
> only to add the same offset back later.  Those cancel out, so there's
> no _semantic_ need to do either..  It's only excessive concern for
> theoretical purity that could stop one from spelling it as
>     return dt + td
> from the start.  That's technically absurd, since it's doing POSIX
> timestamp arithmetic on a timestamp that's _not_ a UTC seconds count.
> Its only virtue is that it gets the same answer far faster ;-)

I actually think this implementation would be _less_ technically absurd.
I'm not sure why you'd insist that any arithmetic on a count of seconds
must be "POSIX timestamp arithmetic." In this case you're just doing
integer arithmetic on a naive count of seconds since some point in the
local timezone clock, rather than on a count of seconds in UTC. That's a
much more natural way to view classic arithmetic, and also happens to be
the way datetime actually does it (where "some point" is datetime(1, 1, 1)).


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/datetime-sig/attachments/20150913/0a3e7ce8/attachment.sig>

More information about the Datetime-SIG mailing list