[Python-checkins] python/dist/src/Modules datetimemodule.c,1.8,1.9
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Sat, 21 Dec 2002 19:43:41 -0800
Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv23750/python/Modules
Modified Files:
datetimemodule.c
Log Message:
Implemented a Wiki suggestion:
{timetz,datetimetz}.{utcoffset,dst}() now return a timedelta (or None)
instead of an int (or None).
tzinfo.{utcoffset,dst)() can now return a timedelta (or an int, or None).
Curiously, this was much easier to do in the C implementation than in the
Python implementation (which lives in the Zope3 code tree) -- the C code
already had lots of hair to extract C ints from offset objects, and used
C ints internally.
Index: datetimemodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/datetimemodule.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** datetimemodule.c 20 Dec 2002 01:31:27 -0000 1.8
--- datetimemodule.c 22 Dec 2002 03:43:39 -0000 1.9
***************
*** 550,553 ****
--- 550,587 ----
*/
+ /* Ensure that p is None or of a tzinfo subclass. Return 0 if OK; if not
+ * raise TypeError and return -1.
+ */
+ static int
+ check_tzinfo_subclass(PyObject *p)
+ {
+ if (p == Py_None || PyTZInfo_Check(p))
+ return 0;
+ PyErr_Format(PyExc_TypeError,
+ "tzinfo argument must be None or of a tzinfo subclass, "
+ "not type '%s'",
+ p->ob_type->tp_name);
+ return -1;
+ }
+
+ /* Return tzinfo.methname(self), without any checking of results.
+ * If tzinfo is None, returns None.
+ */
+ static PyObject *
+ call_tzinfo_method(PyObject *self, PyObject *tzinfo, char *methname)
+ {
+ PyObject *result;
+
+ assert(self && tzinfo && methname);
+ assert(check_tzinfo_subclass(tzinfo) >= 0);
+ if (tzinfo == Py_None) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ result = PyObject_CallMethod(tzinfo, methname, "O", self);
+ return result;
+ }
+
/* If self has a tzinfo member, return a BORROWED reference to it. Else
* return NULL, which is NOT AN ERROR. There are no error returns here,
***************
*** 567,592 ****
}
- /* Ensure that p is None or of a tzinfo subclass. Return 0 if OK; if not
- * raise TypeError and return -1.
- */
- static int
- check_tzinfo_subclass(PyObject *p)
- {
- if (p == Py_None || PyTZInfo_Check(p))
- return 0;
- PyErr_Format(PyExc_TypeError,
- "tzinfo argument must be None or of a tzinfo subclass, "
- "not type '%s'",
- p->ob_type->tp_name);
- return -1;
- }
-
/* Internal helper.
* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the
* result. tzinfo must be an instance of the tzinfo class. If the method
* returns None, this returns 0 and sets *none to 1. If the method doesn't
! * return a Python int or long, TypeError is raised and this returns -1.
! * If it does return an int or long, but is outside the valid range for
! * a UTC minute offset, ValueError is raised and this returns -1.
* Else *none is set to 0 and the integer method result is returned.
*/
--- 601,613 ----
}
/* Internal helper.
* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the
* result. tzinfo must be an instance of the tzinfo class. If the method
* returns None, this returns 0 and sets *none to 1. If the method doesn't
! * return a Python int or long or timedelta, TypeError is raised and this
! * returns -1. If it returns an int or long, but is outside the valid
! * range for a UTC minute offset, or it returns a timedelta and the value is
! * out of range or isn't a whole number of minutes, ValueError is raised and
! * this returns -1.
* Else *none is set to 0 and the integer method result is returned.
*/
***************
*** 603,607 ****
*none = 0;
! u = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg);
if (u == NULL)
return -1;
--- 624,628 ----
*none = 0;
! u = call_tzinfo_method(tzinfoarg, tzinfo, name);
if (u == NULL)
return -1;
***************
*** 615,624 ****
if (PyInt_Check(u))
result = PyInt_AS_LONG(u);
else if (PyLong_Check(u))
result = PyLong_AsLong(u);
else {
PyErr_Format(PyExc_TypeError,
! "tzinfo.%s() must return None or int or long",
! name);
goto Done;
}
--- 636,668 ----
if (PyInt_Check(u))
result = PyInt_AS_LONG(u);
+
else if (PyLong_Check(u))
result = PyLong_AsLong(u);
+
+ else if (PyDelta_Check(u)) {
+ const int days = GET_TD_DAYS(u);
+ if (days < -1 || days > 0)
+ result = 24*60; /* trigger ValueError below */
+ else {
+ /* next line can't overflow because we know days
+ * is -1 or 0 now
+ */
+ int ss = days * 24 * 3600 + GET_TD_SECONDS(u);
+ result = divmod(ss, 60, &ss);
+ if (ss || GET_TD_MICROSECONDS(u)) {
+ PyErr_Format(PyExc_ValueError,
+ "tzinfo.%s() must return a "
+ "whole number of minutes",
+ name);
+ result = -1;
+ goto Done;
+ }
+ }
+ }
else {
PyErr_Format(PyExc_TypeError,
! "tzinfo.%s() must return None, integer or "
! "timedelta, not '%s'",
! name, u->ob_type->tp_name);
goto Done;
}
***************
*** 650,653 ****
--- 694,723 ----
}
+ static PyObject *new_delta(int d, int sec, int usec, int normalize);
+
+ /* Call tzinfo.name(self) and return the offset as a timedelta or None. */
+ static PyObject *
+ offset_as_timedelta(PyObject *self, PyObject *tzinfo, char *name) {
+ PyObject *result;
+
+ if (tzinfo == Py_None) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else {
+ int none;
+ int offset = call_utc_tzinfo_method(tzinfo, name, self, &none);
+ if (offset < 0 && PyErr_Occurred())
+ return NULL;
+ if (none) {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ result = new_delta(0, offset * 60, 0, 1);
+ }
+ return result;
+ }
+
/* Call tzinfo.dst(tzinfoarg), and extract an integer from the
* result. tzinfo must be an instance of the tzinfo class. If dst()
***************
*** 664,683 ****
}
! /* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be
! * an instance of the tzinfo class. If tzname() doesn't return None or
! * a string, TypeError is raised and this returns NULL.
*/
static PyObject *
! call_tzname(PyObject *tzinfo, PyObject *tzinfoarg)
{
PyObject *result;
assert(tzinfo != NULL);
! assert(PyTZInfo_Check(tzinfo));
! assert(tzinfoarg != NULL);
! result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg);
! if (result != NULL && result != Py_None && !PyString_Check(result)) {
! PyErr_Format(PyExc_TypeError, ".tzinfo.tzname() must "
"return None or a string, not '%s'",
result->ob_type->tp_name);
--- 734,760 ----
}
! /* Call tzinfo.tzname(self), and return the result. tzinfo must be
! * an instance of the tzinfo class or None. If tzinfo isn't None, and
! * tzname() doesn't return None ora string, TypeError is raised and this
! * returns NULL.
*/
static PyObject *
! call_tzname(PyObject *self, PyObject *tzinfo)
{
PyObject *result;
+ assert(self != NULL);
assert(tzinfo != NULL);
! assert(check_tzinfo_subclass(tzinfo) >= 0);
! if (tzinfo == Py_None) {
! result = Py_None;
! Py_INCREF(result);
! }
! else
! result = PyObject_CallMethod(tzinfo, "tzname", "O", self);
!
! if (result != NULL && result != Py_None && ! PyString_Check(result)) {
! PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must "
"return None or a string, not '%s'",
result->ob_type->tp_name);
***************
*** 700,704 ****
* datetime,
* datetimetz with None tzinfo,
! * datetimetz where utcoffset() return None
* time,
* timetz with None tzinfo,
--- 777,781 ----
* datetime,
* datetimetz with None tzinfo,
! * datetimetz where utcoffset() returns None
* time,
* timetz with None tzinfo,
***************
*** 920,925 ****
if (Zreplacement == NULL) goto Done;
if (tzinfo != Py_None && tzinfo != NULL) {
! PyObject *temp = call_tzname(tzinfo,
! object);
if (temp == NULL) goto Done;
if (temp != Py_None) {
--- 997,1002 ----
if (Zreplacement == NULL) goto Done;
if (tzinfo != Py_None && tzinfo != NULL) {
! PyObject *temp = call_tzname(object,
! tzinfo);
if (temp == NULL) goto Done;
if (temp != Py_None) {
***************
*** 3918,3953 ****
/*
! * Indirect access to tzinfo methods. One more "convenience function" and
! * it won't be possible to find the useful methods anymore <0.5 wink>.
*/
- static PyObject *
- timetz_convienience(PyDateTime_TimeTZ *self, char *name)
- {
- PyObject *result;
-
- if (self->tzinfo == Py_None) {
- result = Py_None;
- Py_INCREF(result);
- }
- else
- result = PyObject_CallMethod(self->tzinfo, name, "O", self);
- return result;
- }
-
/* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject *
timetz_utcoffset(PyDateTime_TimeTZ *self, PyObject *unused) {
! return timetz_convienience(self, "utcoffset");
}
static PyObject *
! timetz_tzname(PyDateTime_TimeTZ *self, PyObject *unused) {
! return timetz_convienience(self, "tzname");
}
static PyObject *
! timetz_dst(PyDateTime_TimeTZ *self, PyObject *unused) {
! return timetz_convienience(self, "dst");
}
--- 3995,4016 ----
/*
! * Indirect access to tzinfo methods.
*/
/* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject *
timetz_utcoffset(PyDateTime_TimeTZ *self, PyObject *unused) {
! return offset_as_timedelta((PyObject *)self, self->tzinfo,
! "utcoffset");
}
static PyObject *
! timetz_dst(PyDateTime_TimeTZ *self, PyObject *unused) {
! return offset_as_timedelta((PyObject *)self, self->tzinfo, "dst");
}
static PyObject *
! timetz_tzname(PyDateTime_TimeTZ *self, PyObject *unused) {
! return call_tzname((PyObject *)self, self->tzinfo);
}
***************
*** 4326,4360 ****
*/
- /* Internal helper.
- * Call a tzinfo object's method, or return None if tzinfo is None.
- */
- static PyObject *
- datetimetz_convienience(PyDateTime_DateTimeTZ *self, char *name)
- {
- PyObject *result;
-
- if (self->tzinfo == Py_None) {
- result = Py_None;
- Py_INCREF(result);
- }
- else
- result = PyObject_CallMethod(self->tzinfo, name, "O", self);
- return result;
- }
-
/* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject *
datetimetz_utcoffset(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return datetimetz_convienience(self, "utcoffset");
}
static PyObject *
! datetimetz_tzname(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return datetimetz_convienience(self, "tzname");
}
static PyObject *
! datetimetz_dst(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return datetimetz_convienience(self, "dst");
}
--- 4389,4407 ----
*/
/* These are all METH_NOARGS, so don't need to check the arglist. */
static PyObject *
datetimetz_utcoffset(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return offset_as_timedelta((PyObject *)self, self->tzinfo,
! "utcoffset");
}
static PyObject *
! datetimetz_dst(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return offset_as_timedelta((PyObject *)self, self->tzinfo, "dst");
}
static PyObject *
! datetimetz_tzname(PyDateTime_DateTimeTZ *self, PyObject *unused) {
! return call_tzname((PyObject *)self, self->tzinfo);
}