[Python-checkins] python/nondist/sandbox/datetime datetime.c,1.23,1.24 obj_delta.c,1.5,1.6 test_datetime.py,1.47,1.48
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Mon, 25 Nov 2002 21:11:28 -0800
Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv24895
Modified Files:
datetime.c obj_delta.c test_datetime.py
Log Message:
Deeper into the weeds! Moved the timedelta tests from the Python
test suite into test_both. The C test suite didn't have any tests of
this, and there were, and still are, lots of problems. Added a bunch
of new numeric methods to the timedelta C implementation, removed
needless bounds checks, added needed bounds checks, and generally
recovered from that C "/" doesn't compute the floor as these algorithms
need. Much more is still needed here, but I'm hitting the wall for
one day.
Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.23
retrieving revision 1.24
diff -C2 -d -r1.23 -r1.24
*** datetime.c 22 Nov 2002 03:34:42 -0000 1.23
--- datetime.c 26 Nov 2002 05:11:25 -0000 1.24
***************
*** 19,24 ****
long hashcode;
long days;
! long seconds;
! long microseconds;
} PyDateTime_Delta;
--- 19,24 ----
long hashcode;
long days;
! long seconds; /* 0 <= seconds < 24*3600 is invariant */
! long microseconds; /* 0 <= microseconds < 1000000 is invariant */
} PyDateTime_Delta;
***************
*** 64,67 ****
--- 64,91 ----
*/
+ /* Compute Python divmod(x, y), returning the quotient and storing the
+ * remainder into *r. The quotient is the floor of x/y, and that's
+ * the real point of this. C will probably truncate instead (C89
+ * requires truncation; C89 left it implementation-defined).
+ * Simplification: we *require* that y > 0 here. That's appropriate
+ * for all the uses made of it. This simplifies the code and makes
+ * the overflow case impossible.
+ */
+ static long
+ divmod(long x, long y, long *r)
+ {
+ long quo;
+
+ assert(y > 0);
+ quo = x / y;
+ *r = x - quo * y;
+ if (*r < 0) {
+ --quo;
+ *r += y;
+ }
+ assert(0 <= *r && *r < y);
+ return quo;
+ }
+
/* For each month ordinal in 1..12, the number of days in that month,
* and the number of days before that month in the same year. These
***************
*** 333,347 ****
static PyObject *
! new_delta(long int days, long int seconds, long int microseconds)
{
! PyDateTime_Delta *self;
! if (microseconds >= 1000000 || microseconds <= -1000000) {
! seconds += microseconds / 1000000;
! microseconds %= 1000000;
}
! if (seconds >= 24*3600 || seconds <= 24*3600) {
! days += seconds / (24*3600);
! seconds %= (24*3600);
}
self = PyObject_New(PyDateTime_Delta, &PyDateTime_DeltaType);
--- 357,396 ----
static PyObject *
! new_delta(long days, long seconds, long microseconds)
{
! PyDateTime_Delta *self = NULL;
! /* k = i+j overflows iff k differs in sign from both inputs,
! * iff k^i has sign bit set and k^j has sign bit set,
! * iff (k^i)&(k^j) has sign bit set.
! */
! if (microseconds < 0 || microseconds >= 1000000) {
! long whole_seconds;
! long new_seconds;
!
! whole_seconds = divmod(microseconds, 1000000, µseconds);
! assert(microseconds >= 0);
! new_seconds = seconds + whole_seconds;
! if (((new_seconds ^ seconds) &
! (new_seconds ^ whole_seconds)) < 0) {
! PyErr_SetString(PyExc_OverflowError, "timedelta "
! "seconds component too large");
! goto done;
! }
! seconds = new_seconds;
}
! if (seconds < 0 || seconds >= 24*3600) {
! long whole_days;
! long new_days;
!
! whole_days = divmod(seconds, 24*3600, &seconds);
! assert(seconds >= 0);
! new_days = days + whole_days;
! if (((new_days ^ days) & (new_days ^ whole_days)) < 0) {
! PyErr_SetString(PyExc_OverflowError, "timedelta "
! "days component too large");
! goto done;
! }
! days = new_days;
}
self = PyObject_New(PyDateTime_Delta, &PyDateTime_DeltaType);
***************
*** 352,355 ****
--- 401,405 ----
SET_TD_MICROSECONDS(self, microseconds);
}
+ done:
return (PyObject *) self;
}
Index: obj_delta.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_delta.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** obj_delta.c 26 Nov 2002 03:29:17 -0000 1.5
--- obj_delta.c 26 Nov 2002 05:11:25 -0000 1.6
***************
*** 19,22 ****
--- 19,23 ----
if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
/* delta + delta */
+ /* XXX Overflow checking is needed here. */
const long days = GET_TD_DAYS(delta) +
GET_TD_DAYS(right);
***************
*** 42,45 ****
--- 43,103 ----
}
+ static PyObject *
+ delta_negative(PyDateTime_Delta *self)
+ {
+ return new_delta(-GET_TD_DAYS(self),
+ -GET_TD_SECONDS(self),
+ -GET_TD_MICROSECONDS(self));
+ }
+
+ static PyObject *
+ delta_positive(PyDateTime_Delta *self)
+ {
+ /* Could optimize this (by returning self) if this isn't a
+ * subclass -- but who uses unary + ? Approximately nobody.
+ */
+ return new_delta(GET_TD_DAYS(self),
+ GET_TD_SECONDS(self),
+ GET_TD_MICROSECONDS(self));
+ }
+
+ static PyObject *
+ delta_abs(PyDateTime_Delta *self)
+ {
+ PyObject *result;
+
+ assert(GET_TD_MICROSECONDS(self) >= 0);
+ assert(GET_TD_SECONDS(self) >= 0);
+
+ if (GET_TD_DAYS(self) < 0)
+ result = delta_negative(self);
+ else
+ result = delta_positive(self);
+
+ return result;
+ }
+
+ static PyObject *
+ delta_subtract(PyObject *left, PyObject *right)
+ {
+ PyObject *result = NULL;
+
+ /* XXX It's unclear to me exactly which rules we intend here. */
+ if (PyType_IsSubtype(left->ob_type, &PyDateTime_DeltaType) &&
+ PyType_IsSubtype(right->ob_type, &PyDateTime_DeltaType)) {
+ /* delta - delta */
+ PyObject *minus_right = PyNumber_Negative(right);
+ if (minus_right) {
+ result = delta_add(left, minus_right);
+ Py_DECREF(minus_right);
+ }
+ }
+ else
+ result = Py_NotImplemented;
+
+ Py_XINCREF(result);
+ return result;
+ }
+
static int
delta_compare(PyDateTime_Delta *self, PyObject *other)
***************
*** 107,118 ****
static PyObject *
- delta_negative(PyDateTime_Delta *self)
- {
- return new_delta(-GET_TD_DAYS(self),
- -GET_TD_SECONDS(self),
- -GET_TD_MICROSECONDS(self));
- }
-
- static PyObject *
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
--- 165,168 ----
***************
*** 120,123 ****
--- 170,175 ----
long int days, seconds = 0, microseconds = 0;
+ /* XXX We're missing 4 keyword args from the Python version. */
+ /* XXX The Python version doesn't require a days argument. */
static char *keywords[] = {
"days", "seconds", "microseconds", NULL
***************
*** 126,139 ****
if (PyArg_ParseTupleAndKeywords(args, kw, "l|ll:__new__", keywords,
&days, &seconds, µseconds)) {
- if (seconds < 0 || seconds >= (24 * 3600)) {
- PyErr_SetString(PyExc_ValueError,
- "seconds must be in 0..86399");
- return NULL;
- }
- if (microseconds < 0 || microseconds >= 1000000) {
- PyErr_SetString(PyExc_ValueError,
- "microseconds must be in 0..999999");
- return NULL;
- }
self = new_delta(days, seconds, microseconds);
}
--- 178,181 ----
***************
*** 141,151 ****
}
- static PyObject *
- delta_subtract(PyObject *left, PyObject *right)
- {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- }
-
static int
delta_nonzero(PyDateTime_Delta *self)
--- 183,186 ----
***************
*** 200,205 ****
0, /* nb_power */
(unaryfunc)delta_negative, /* nb_negative */
! 0, /* nb_positive */
! 0, /* nb_absolute */
(inquiry)delta_nonzero, /* nb_nonzero */
};
--- 235,240 ----
0, /* nb_power */
(unaryfunc)delta_negative, /* nb_negative */
! (unaryfunc)delta_positive, /* nb_positive */
! (unaryfunc)delta_abs, /* nb_absolute */
(inquiry)delta_nonzero, /* nb_nonzero */
};
Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.47
retrieving revision 1.48
diff -C2 -d -r1.47 -r1.48
*** test_datetime.py 26 Nov 2002 01:43:03 -0000 1.47
--- test_datetime.py 26 Nov 2002 05:11:25 -0000 1.48
***************
*** 52,119 ****
n += 1
- class TestTimeDelta(unittest.TestCase):
-
- def test_timedelta(self):
- a = timedelta(7) # One week
- b = timedelta(0, 60) # One minute
- c = timedelta(0, 0, 1000) # One millisecond
- self.assertEqual(a+b+c, timedelta(7, 60, 1000))
- self.assertEqual(a-b, timedelta(6, 24*3600 - 60))
- self.assertEqual(-a, timedelta(-7))
- self.assertEqual(+a, timedelta(7))
- self.assertEqual(-b, timedelta(-1, 24*3600 - 60))
- self.assertEqual(-c, timedelta(-1, 24*3600 - 1, 999000))
- self.assertEqual(abs(a), a)
- self.assertEqual(abs(-a), a)
- self.assertEqual(timedelta(6, 24*3600), a)
- self.assertEqual(timedelta(0, 0, 60*1000000), b)
- self.assertEqual(a*10, timedelta(70))
- self.assertEqual(a*10, 10*a)
- self.assertEqual(a*10L, 10*a)
- self.assertEqual(b*10, timedelta(0, 600))
- self.assertEqual(10*b, timedelta(0, 600))
- self.assertEqual(b*10L, timedelta(0, 600))
- self.assertEqual(c*10, timedelta(0, 0, 10000))
- self.assertEqual(10*c, timedelta(0, 0, 10000))
- self.assertEqual(c*10L, timedelta(0, 0, 10000))
- self.assertEqual(a*-1, -a)
- self.assertEqual(b*-2, -b-b)
- self.assertEqual(c*-2, -c+-c)
- self.assertEqual(b*(60*24), (b*60)*24)
- self.assertEqual(b*(60*24), (60*b)*24)
- self.assertEqual(c*1000, timedelta(0, 1))
- self.assertEqual(1000*c, timedelta(0, 1))
- self.assertEqual(a//7, timedelta(1))
- self.assertEqual(b//10, timedelta(0, 6))
- self.assertEqual(c//1000, timedelta(0, 0, 1))
- self.assertEqual(a//10, timedelta(0, 7*24*360))
- self.assertEqual(a//3600000, timedelta(0, 0, 7*24*1000))
- # Add/sub ints, longs, floats should be illegal
- for i in 1, 1L, 1.0:
- self.assertRaises(TypeError, lambda: a+i)
- self.assertRaises(TypeError, lambda: a-i)
- self.assertRaises(TypeError, lambda: i+a)
- self.assertRaises(TypeError, lambda: i-a)
- # Check keyword args to constructor
- eq = self.assertEqual
- td = timedelta
- eq(td(1), td(days=1))
- eq(td(0, 1), td(seconds=1))
- eq(td(0, 0, 1), td(microseconds=1))
- eq(td(weeks=1), td(days=7))
- eq(td(days=1), td(hours=24))
- eq(td(hours=1), td(minutes=60))
- eq(td(minutes=1), td(seconds=60))
- eq(td(seconds=1), td(milliseconds=1000))
- eq(td(milliseconds=1), td(microseconds=1000))
- # Check float args to constructor
- eq(td(weeks=1.0/7), td(days=1))
- eq(td(days=1.0/24), td(hours=1))
- eq(td(hours=1.0/60), td(minutes=1))
- eq(td(minutes=1.0/60), td(seconds=1))
- eq(td(seconds=0.001), td(milliseconds=1))
- eq(td(milliseconds=0.001), td(microseconds=1))
-
-
class TestTime(unittest.TestCase):
--- 52,55 ----
***************
*** 558,562 ****
def test_suite():
- s1 = unittest.makeSuite(TestTimeDelta, 'test')
s2 = unittest.makeSuite(TestDate, 'test')
s3 = unittest.makeSuite(TestTime, 'test')
--- 494,497 ----
***************
*** 564,568 ****
s5 = unittest.makeSuite(TestDateTime, 'test')
s6 = unittest.makeSuite(TestDateTimeTZ, 'test')
! return unittest.TestSuite([s1, s2, s3, s4, s5, s6])
def test_main():
--- 499,503 ----
s5 = unittest.makeSuite(TestDateTime, 'test')
s6 = unittest.makeSuite(TestDateTimeTZ, 'test')
! return unittest.TestSuite([s2, s3, s4, s5, s6])
def test_main():