time + timedelta
josh at yucs.org
josh at yucs.org
Mon Feb 7 11:34:26 EST 2005
sorry, patch really attached to this message.
On Mon, Feb 07, 2005 at 11:24:20AM -0500, josh wrote:
> datetime.time should support timedelta arithmetic, patch attached.
>
> Times greater than 24 hours should not raise an exception, but always
> wrap around. Any other behavior is too surprising. (People expect to be
> able to call you up at 11pm, and say "meet me in two hours" (if you're a
> night person)).
>
> Clock time is cyclical. Though it's convenient to assign hours numbers,
> an unqualified hour 23.5 is not naturally "greater than" hour 0.5.
> (Though I'm not suggesting that we break this.)
>
> The special meaning for hour 0 is only relevant for datetimes. When
> dates are not involved, it shouldn't get any special treatment.
> When dates are involved, it's easy to "assert datetime1.date() ==
> datetime2.date()"
>
> If this change is made however, maybe datetime.timedelta(hours=-1)
> should no longer normalize to datetime.timedelta(-1, 82800)
>
-------------- next part --------------
*** Doc/lib/libdatetime.tex1 2005-02-03 21:53:06.000000000 -0500
--- Doc/lib/libdatetime.tex 2005-02-03 22:11:52.000000000 -0500
***************
*** 1059,1069 ****
Supported operations:
! \begin{itemize}
! \item
! comparison of \class{time} to \class{time},
! where \var{a} is considered less than \var{b} when \var{a} precedes
! \var{b} in time. If one comparand is naive and the other is aware,
\exception{TypeError} is raised. If both comparands are aware, and
have the same \member{tzinfo} member, the common \member{tzinfo}
member is ignored and the base times are compared. If both
--- 1059,1122 ----
Supported operations:
! \begin{tableii}{c|l}{code}{Operation}{Result}
! \lineii{\var{time2} = \var{time1} + \var{timedelta}}{(1)}
!
! \lineii{\var{time2} = \var{time1} - \var{timedelta}}{(2)}
!
! \lineii{\var{timedelta} = \var{time1} - \var{time2}}{(3)}
!
! \lineii{\var{time1} < \var{time2}}
! {Compares \class{time} to \class{time}.
! (4)}
!
! \end{tableii}
!
! \begin{description}
!
! \item[(1)]
!
! time2 is a duration of timedelta removed from time1, moving
! forward in time if \code{\var{timedelta}.seconds} > 0, or backward if
! \code{\var{timedelta}.seconds} < 0. The result has the same \member{tzinfo} member
! as the input time, and time2 - time1 == timedelta after.
! Note that no time zone adjustments are done even if the input is an
! aware object.
!
! \item[(2)]
! Computes the time2 such that time2 + timedelta == time1.
! As for addition, the result has the same \member{tzinfo} member
! as the input time, and no time zone adjustments are done even
! if the input is aware.
! This isn't quite equivalent to time1 + (-timedelta), because
! -timedelta in isolation can overflow in cases where
! time1 - timedelta does not.
!
! \item[(3)]
! Subtraction of a \class{time} from a
! \class{time} is defined only if both
! operands are naive, or if both are aware. If one is aware and the
! other is naive, \exception{TypeError} is raised.
!
! If both are naive, or both are aware and have the same \member{tzinfo}
! member, the \member{tzinfo} members are ignored, and the result is
! a \class{timedelta} object \var{t} such that
! \code{\var{time2} + \var{t} == \var{time1}}. No time zone
! adjustments are done in this case.
!
! If both are aware and have different \member{tzinfo} members,
! \code{a-b} acts as if \var{a} and \var{b} were first converted to
! naive UTC times first. The result is
! \code{(\var{a}.replace(tzinfo=None) - \var{a}.utcoffset()) -
! (\var{b}.replace(tzinfo=None) - \var{b}.utcoffset())}
! except that the implementation never overflows.
!
! \item[(4)]
!
! \var{time1} is considered less than \var{time2}
! when \var{time1} precedes \var{time2} in time.
!
! If one comparand is naive and the other is aware,
\exception{TypeError} is raised. If both comparands are aware, and
have the same \member{tzinfo} member, the common \member{tzinfo}
member is ignored and the base times are compared. If both
***************
*** 1076,1093 ****
raised unless the comparison is \code{==} or \code{!=}. The latter
cases return \constant{False} or \constant{True}, respectively.
! \item
! hash, use as dict key
! \item
! efficient pickling
!
! \item
! in Boolean contexts, a \class{time} object is considered to be
! true if and only if, after converting it to minutes and
! subtracting \method{utcoffset()} (or \code{0} if that's
! \code{None}), the result is non-zero.
! \end{itemize}
Instance methods:
--- 1129,1141 ----
raised unless the comparison is \code{==} or \code{!=}. The latter
cases return \constant{False} or \constant{True}, respectively.
! \end{description}
! \class{time} objects are hashable (usable as dictionary keys),
! support efficient pickling, and in Boolean contexts, a \class{time}
! object is considered to be true if and only if, after converting it to
! minutes and subtracting \method{utcoffset()} (or \code{0} if that's
! \code{None}), the result is non-zero.
Instance methods:
*** Modules/datetimemodule.c1 2005-02-03 21:50:40.000000000 -0500
--- Modules/datetimemodule.c 2005-02-03 21:51:07.000000000 -0500
***************
*** 559,564 ****
--- 559,577 ----
return result;
}
+ /* Force all the time fields into range. The parameters are both
+ * inputs and outputs. Cannot fail.
+ */
+ static void
+ normalize_time(int *day, int *hour, int *minute, int *second,
+ int *microsecond)
+ {
+ normalize_pair(second, microsecond, 1000000);
+ normalize_pair(minute, second, 60);
+ normalize_pair(hour, minute, 60);
+ normalize_pair(day, hour, 24);
+ }
+
/* Force all the datetime fields into range. The parameters are both
* inputs and outputs. Returns < 0 on error.
*/
***************
*** 567,576 ****
int *hour, int *minute, int *second,
int *microsecond)
{
! normalize_pair(second, microsecond, 1000000);
! normalize_pair(minute, second, 60);
! normalize_pair(hour, minute, 60);
! normalize_pair(day, hour, 24);
return normalize_date(year, month, day);
}
--- 580,586 ----
int *hour, int *minute, int *second,
int *microsecond)
{
! normalize_time(day, hour, minute, second, microsecond);
return normalize_date(year, month, day);
}
***************
*** 3276,3281 ****
--- 3286,3398 ----
}
/*
+ * Time arithmetic.
+ */
+
+ /* time + timedelta -> time. If arg negate is true, subtract the timedelta
+ * instead.
+ */
+ static PyObject *
+ add_time_timedelta(PyDateTime_Time *time, PyDateTime_Delta *delta, int negate)
+ {
+ int delta_s = GET_TD_SECONDS(delta);
+ int delta_us = GET_TD_MICROSECONDS(delta);
+ int day; /* ignored */
+ int hour = TIME_GET_HOUR(time);
+ int minute = TIME_GET_MINUTE(time);
+ int second = TIME_GET_SECOND(time) + (negate ? -delta_s : delta_s);
+ int microsecond = TIME_GET_MICROSECOND(time) + (negate ? -delta_us : delta_us);
+
+ normalize_time(&day, &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 (PyDateTime_Check(left) || PyDateTime_Check(right)) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ if (PyTime_Check(left)) {
+ /* time + ??? */
+ if (PyDelta_Check(right))
+ /* time + delta */
+ return add_time_timedelta((PyDateTime_Time *) left,
+ (PyDateTime_Delta *) right,
+ 0);
+ }
+ else {
+ /* ??? + time
+ * 'right' must be one of us, or we wouldn't have been called
+ */
+ if (PyDelta_Check(left))
+ /* delta + time */
+ return add_time_timedelta((PyDateTime_Time *) right,
+ (PyDateTime_Delta *) left,
+ 0);
+ }
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ static PyObject *
+ time_subtract(PyObject *left, PyObject *right)
+ {
+ if (PyDateTime_Check(left) || PyDateTime_Check(right)) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ if (PyTime_Check(left)) {
+ 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;
+
+ return new_delta(0, delta_s, delta_us, 1);
+ }
+ if (PyDelta_Check(right)) {
+ /* time - delta */
+ return add_time_timedelta((PyDateTime_Date *) left,
+ (PyDateTime_Delta *) right,
+ 1);
+ }
+ }
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ /*
* Miscellaneous methods.
*/
***************
*** 3502,3509 ****
a tzinfo subclass. The remaining arguments may be ints or longs.\n");
static PyNumberMethods time_as_number = {
! 0, /* nb_add */
! 0, /* nb_subtract */
0, /* nb_multiply */
0, /* nb_divide */
0, /* nb_remainder */
--- 3619,3626 ----
a tzinfo subclass. The remaining arguments may be ints or longs.\n");
static PyNumberMethods time_as_number = {
! time_add, /* nb_add */
! time_subtract, /* nb_subtract */
0, /* nb_multiply */
0, /* nb_divide */
0, /* nb_remainder */
More information about the Python-list
mailing list