[Datetime-SIG] Local time disambiguation proposal

ISAAC J SCHWABACHER ischwabacher at wisc.edu
Tue Aug 4 17:47:58 CEST 2015

[Alexander Belopolsky]
> > ...
> > I am also working on converting my write-up [1] for publication as a
> > PEP.  Please note that I have made substantial changes since my
> > initial post, so if you comment on the proposal please make sure that
> > you've read the latest version.
> >
> > [1]: https://github.com/abalkin/ltdf/blob/master/README.rst

[Tim Peters]
> Since that appears to be obsolete now, here are some comments on:
>     https://github.com/abalkin/ltdf/blob/master/pep-0495.txt
> instead.
> """
> The ``replace()`` methods of the ``datetime.time`` and
> ``datetime.datetime`` classes will get a new keyword-only argument
> called ``first`` with the default value ``True``
> """
> That's dubious:  .replace() treats all other missing arguments as
> meaning "copy this attribute's current value".  I don't see why
> `.first` should be different.  If there's a strong reason (I don't see
> one) for
>     dt2 = dt1.replace(minutes=2)
> to force dt2.first to True when dt1.first is False, it would be better
> to raise an exception than change the value silently.

Not so long ago I think I finally got a point that Tim has been dancing around throughout this whole discussion, without quite saying it outright. (Another possibility would be Reading Comprehension Fail on my part...) Classic arithmetic and `replace()` are low level operations on datetimes, whereas duration and period arithmetic and time zone conversion are high level ones. The original design of datetime was to expose all of the low level details so that people could implement their own high level stuff, because we're programmers and that's what we do, ya know?

All of this is to say that I agree with Tim that `first=True` is not a good default for `replace()`. While that method verifies that all of its numerical arguments are within their valid ranges, it doesn't verify that the resulting time exists in its time zone, so for consistency I would expect that it simply keep the value of the `first` flag without validation or modification if that argument is not provided. But this raises the question of how an unambiguous datetime with first=False should be handled by other code. My preference is that all high level operations should treat such datetimes as having first=True.

Also, given this divide, it would be good to document in the datetime module which methods are high level and which are low.

> """
> The ``timestamp()`` method of ``datetime.datetime`` will return value
> advanced by 3600 if ``self`` represents an ambiguous hour and
> ``first`` is False.
> """
> Are all known DST adjustments, and changes to standard UTC offsets --
> in, say, zoneinfo -- exactly one hour?  For example, I read this on
> the web, so it must be true:  "Lord Howe Island (Australia) advances
> its clocks by half an hour in the summer" ;-)  Since I expect we
> expect to support all the goofy timezones in zoneinfo, best to get
> that right from the start.

Also relevant is the fact that this rationale fails in such cases:

We chose the minute byte to store the the "first" bit because this choice preserves the natural ordering.

> """
> The value of "first" will be ignored in all operations except those
> that involve conversion between timezones.
> """
> "Involve" is vague. Subtraction and comparison can "involve"
> conversion to UTC, at least conceptually, when two datetimes don't
> share a tzinfo member.  It's explicitly stated later that:
> """
> Comparison
> ----------
> Instances of ``datetime.time`` and ``datetime.datetime`` classes that
> differ only by the value of their ``first`` attribute will compare as
> equal.
> """
> but it's not explicitly stated that subtraction of such instances will
> return timedelta(0).  If there's a reason to explicitly point out one,
> then both should be mentioned, since comparison actually inherits its
> behavior from subtraction.
> The real reason the cases named will always compare as equal is that
> they share a common tzinfo member (or both have none).  That
> short-circuits the conceptional conversion to UTC before comparison.
> But if you plug a workalike tzinfo member into one of them (same
> timezone represented by a distinct tzinfo object), then the results of
> comparison (and subtraction) _may_ change "just because" .first
> differs between them.  That all depends on what .utcoffset() returns.
> In the text about comparisons, I expect you're just trying to say that
> .first isn't used as a tie breaker, as if datetime comparisons were
> akin to tuple comparisons, comparing field by field.  But that's not
> how it's done, and there's no real point to explaining how a model
> that didn't apply to begin with would be affected if it had applied to
> begin with ;-)
> So I'd drop the text about comparisons.  But I'd add some text
> explaining that, while this PEP isn't aiming at timeline arithmetic,
> some cases of subtraction and comparison, which have used timeline
> arithmetic all along, will return different results now due to
> .utcoffset() returning different results now.  To make the reader
> happy, you could even mention that these different results are also
> correct now ;-)

I agree with dropping the part about comparisons, since that will no longer be true for datetimes with tzstrict time zones if that part of the discussion comes to fruition.


More information about the Datetime-SIG mailing list