Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does date + timedelta become date, not datetime?

In Python, in an operation of numbers of mixed type, the narrower type is widened to that of the other, such as int + floatfloat:

In [57]: 3 + 0.1
Out[57]: 3.1

But for datetime.date, we have datetime.date + datetime.timedeltadatetime.date, not datetime.datetime:

In [58]: datetime.date(2013, 1, 1) + datetime.timedelta(seconds=42)
Out[58]: datetime.date(2013, 1, 1)

Why is the widening reasoning applied to numbers, but not to date/datetime/timedelta?

(Background: I'm writing a reading routine for a file format where one field is year, one field is day-of-year, one field is milliseconds-since-midnight. Of course, the simple and explicit solution is datetime.datetime(2013, 1, 1, 0, 0, 0) + datetime.timedelta(seconds=42), but one could equally reason that one should rewrite 3 + 0.1 as 3.0 + 0.1)

like image 468
gerrit Avatar asked May 11 '16 14:05

gerrit


People also ask

What is Timedelta datetime?

timedelta Objects. A timedelta object represents a duration, the difference between two dates or times. class datetime. timedelta (days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

What does Timedelta mean?

Timedeltas are differences in times, expressed in difference units, e.g. days, hours, minutes, seconds. They can be both positive and negative. Timedelta is a subclass of datetime.

How do I get Timedelta with only time?

I assume you want to extract the time component as a string, removing all reference to "days" even if your Timedelta is greater than 1 day. One way is to convert your Timedelta to a normalised datetime and then use the time attribute. Below is also a trivial way to convert your Timedelta objects to strings.

What does Timedelta do in Python?

timedelta() function. Python timedelta() function is present under datetime library which is generally used for calculating differences in dates and also can be used for date manipulations in Python. It is one of the easiest ways to perform date manipulations.


2 Answers

The timedelta object doesn't store any information on whether or not it just concerns dates, or also times. (The fact that the number of hours/minutes/seconds/micros is 0 may just be a coincidence!)

Hence, suppose we have someone who just wants to manipulate dates, ignoring times, she'd do something like my_new_date = my_old_date + timedelta(days=1). She'd be very surprised and possibly annoyed to find that my_new_date is now a datetime object rather than a date object.

like image 123
acdr Avatar answered Nov 07 '22 07:11

acdr


The behaviour is documented:

date2 is moved forward in time if timedelta.days > 0, or backward if timedelta.days < 0. Afterward date2 - date1 == timedelta.days. timedelta.seconds and timedelta.microseconds are ignored.

(My emphasis. This behaviour has remained unchanged since date objects were added in Python 2.3.)

I haven't been able to find any evidence as to why the module is designed like this. Certainly there are use cases like yours where you want to represent the point in time corresponding to the midnight at the start of a day. In these cases it is annoying to have to convert back and forth. But there are other use cases in which you want to represent a whole day (and not just some point in time on that day), in which case you don't want to accidentally end up with partial days when you add timedeltas.

Chris Withers suggested that the behaviour be changed, in issue 3249, but Tim Peters noted that:

an incompatible change to documented always-worked-this-way behavior is unlikely to be accepted.

If you want an object that behaves like a datetime.date, but where arithmetic operations return datetime.datetime objects, then it shouldn't be not too hard to write one:

from datetime import date, datetime, time, timedelta

def _part_day(t):
    """Return True if t is a timedelta object that does not consist of
    whole days.

    """
    return isinstance(t, timedelta) and (t.seconds or t.microseconds)

class mydate(date):
    """Subclass of datetime.date where arithmetic operations with a
    timedelta object return a datetime.datetime object unless the
    timedelta object consists of whole days.

    """
    def datetime(self):
        """Return datetime corresponding to the midnight at start of this
        date.

        """
        return datetime.combine(self, time())

    def __add__(self, other):
        if _part_day(other):
            return self.datetime() + other
        else:
            return super().__add__(other)

    __radd__ = __add__

    def __sub__(self, other):
        if _part_day(other):
            return self.datetime() - other
        else:
            return super().__sub__(other)

(This is untested, but it's shouldn't be hard to get it working from here.)

like image 23
Gareth Rees Avatar answered Nov 07 '22 08:11

Gareth Rees