Index: datetimemodule.c =================================================================== --- datetimemodule.c (revision 64644) +++ datetimemodule.c (working copy) @@ -578,6 +578,24 @@ return normalize_date(year, month, day); } +/* Force all the time fields into range. The parameters are both + * inputs and outputs. + */ +static void +normalize_time(int *hour, int *minute, int *second, + int *microsecond) +{ + normalize_pair(second, microsecond, 1000000); + normalize_pair(minute, second, 60); + normalize_pair(hour, minute, 60); + + /* This wraps the value of *hour around 24. Thus, time arithmetic + * operations that overflow become valid. + */ + *hour = *hour % 24; + +} + /* --------------------------------------------------------------------------- * Basic object allocation: tp_alloc implementations. These allocate * Python objects of the right size and type, and do the Python object- @@ -3197,6 +3215,119 @@ } /* + * time arithmetic. + */ + +/* factor must be 1 (to add) or -1 (to subtract). The result inherits + * the tzinfo state of time. + */ +static PyObject * +add_time_timedelta(PyDateTime_Time *time, PyDateTime_Delta *delta, + int factor) +{ + /* Note that the C-level additions can't overflow, because of + * invariant bounds on the member values. + */ + + /* Delta days are ignored */ + int hour = TIME_GET_HOUR(time); + int minute = TIME_GET_MINUTE(time); + int second = TIME_GET_SECOND(time) + GET_TD_SECONDS(delta) * factor; + int microsecond = TIME_GET_MICROSECOND(time) + + GET_TD_MICROSECONDS(delta) * factor; + + assert(factor == 1 || factor == -1); + normalize_time(&hour, &minute, &second, µsecond); + return new_time(hour, minute, second, microsecond, + HASTZINFO(time) ? time->tzinfo : Py_None); +} + +static PyObject * +time_add(PyObject *left, PyObject *right) +{ + if (PyTime_Check(left)) { + /* time + ??? */ + if (PyDelta_Check(right)) + /* time + delta */ + return add_time_timedelta( + (PyDateTime_Time *)left, + (PyDateTime_Delta *)right, + 1); + } + else if (PyDelta_Check(left)) { + /* delta + time */ + return add_time_timedelta((PyDateTime_Time *) right, + (PyDateTime_Delta *) left, + 1); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +time_subtract(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyTime_Check(left)) { + /* time - ??? */ + if (PyTime_Check(right)) { + /* time - time */ + naivety n1, n2; + int offset1, offset2; + int delta_s, delta_us; + + if (classify_two_utcoffsets(left, &offset1, &n1, left, + right, &offset2, &n2, + right) < 0) + return NULL; + assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); + if (n1 != n2) { + PyErr_SetString(PyExc_TypeError, + "can't subtract offset-naive and " + "offset-aware times"); + return NULL; + } + + /* These can't overflow, since the values are + * normalized. At most this gives the number of + * seconds in one day. + */ + delta_s = (TIME_GET_HOUR(left) - + TIME_GET_HOUR(right)) * 3600 + + (TIME_GET_MINUTE(left) - + TIME_GET_MINUTE(right)) * 60 + + (TIME_GET_SECOND(left) - + TIME_GET_SECOND(right)); + delta_us = TIME_GET_MICROSECOND(left) - + TIME_GET_MICROSECOND(right); + + /* (left - offset1) - (right - offset2) = + * (left - right) + (offset2 - offset1) + */ + delta_s += (offset2 - offset1) * 60; + + /* Zero days is passed to new delta. Upon normalization + * in new_delta, a negative delta_s will be adjusted to + * -1 days and a positive seconds value. + */ + result = new_delta(0, delta_s, delta_us, 1); + } + else if (PyDelta_Check(right)) { + /* time - delta */ + result = add_time_timedelta( + (PyDateTime_Time *)left, + (PyDateTime_Delta *)right, + -1); + } + } + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +/* * Various ways to turn a time into a string. */ @@ -3514,8 +3645,8 @@ a tzinfo subclass. The remaining arguments may be ints or longs.\n"); static PyNumberMethods time_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ + time_add, /* nb_add */ + time_subtract, /* nb_subtract */ 0, /* nb_multiply */ 0, /* nb_divide */ 0, /* nb_remainder */