[Datetime-SIG] PEP-0500 (Alternative datetime arithmetic) Was: PEP 495 ... is ready ...

Guido van Rossum guido at python.org
Wed Aug 19 01:12:36 CEST 2015

On Tue, Aug 18, 2015 at 3:22 PM, Alexander Belopolsky <
alexander.belopolsky at gmail.com> wrote:

> [Alexander Belopolsky]
>> However, I don't see this additional freedom as a big complication.
>>> Even in the common case, it may be easier to implement d(x, y) than to
>>> figure out f(x).  The problem with f(x) is that it is the UTC offset
>>> as a function of local time while most TZ database interfaces only
>>> provide UTC offset as a function of UTC time.  As a result, it is
>>> often easier to implement d(x, y) (for example, as d(x, y) = g(x) -
>>> g(y)) than to implement f(x).
>> [Guido van Rossum]
>> This  discussion sounds overly abstract. ISTM that d(x, y) in timeline
>> arithmetic can be computed as x.timestamp() - y.timestamp(), (and
>> converting to a timedelta).
> It can be, but currently, x.timestamp() is implemented as (t -
> datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds(), so you end up
> defining datetime subtraction in terms of datetime subtraction.
> Let's consider a specific example.  Suppose I want to implement a very
> simple timezone like US/Eastern, where I have some simple rules (with a few
> historical variations) that given year, month, day, hour and the "first"
> flag will tell me whether DST is in effect.   For such timezone, I can
> easily write a function in C like this:
> long long hours_between(int year1, int month1, int day1, int hour1, int
> first1,
>                         int year2, int month2, int day2, int hour2, int
> first2)
> {
>   return 24 * (jd(year2, month2, day2) - jd(year1, month1, day1)) +
>          hour2 - dst(year2, month2, day2, hour2, first2) -
>          hour1 + dst(year1, month1, day1, hour1, first1);
> }
> where jd and dst are the Julian day and DST functions, each taking under
> 30 machine cycles to execute.  With PEP 500 approach, you have a couple of
> attribute accesses and unpacking of two datetime buffers between t1 - t2 in
> Python and  the hours_between function, and then you are a few operations
> with seconds and microseconds and one new_delta call away from the result.
> [Guido van Rossum]
>> Similar for adding a datetime and a timedelta. Optimizing this should be
>> IMO the only question is how should a datetime object choose between
>> classic arithmetic[1] and timeline arithmetic. My proposal here is to make
>> that a boolean property of the tzinfo object -- we could either use a
>> marker subclass or an attribute whose absence implies classic arithmetic.
> With this proposal, we will need something like this:
> def __sub__(self, other):
>     if self.tzinfo is not None and self.tzinfo.strict:
>         self_offset = self.utcoffset()
>         other_offset = other.utcoffset()
>         naive_self = self.replace(tzinfo=None)
>         naive_other = other.replace(tzinfo=None)
>         return naive_self - self_offset - naive_other + other_offset
>     # old logic
> So we need to create six intermediate Python objects just to do the math.
> On top of that, we need the utcoffset() method which is a pain to write in
> C, so we will wrap our optimized dst() function and compute utcoffset() as
> dst(t) + timedelta(hours=-5), creating four more intermediate Python
> objects.  At the end of the day, I will not be surprised if aware datetime
> subtraction is 10x slower than naive and every Python textbook recommends
> to avoid doing arithmetic with aware datetime objects.
I doubt it. Most textbooks aren't that concerned with saving a few cycles.
(Do most Python textbooks even discuss the cost of object creation or
function calls?) Anyways, wouldn't PEP 500 be even slower?

--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/datetime-sig/attachments/20150818/9c32d8d7/attachment-0001.html>

More information about the Datetime-SIG mailing list