Got rid of the timetz type entirely. This was a bit trickier than I
hoped it would be, but not too bad. A test had to change:
time.__setstate__() can no longer add a non-None tzinfo member to a time
object that didn't already have one, since storage for a tzinfo member
doesn't exist in that case.
diff --git a/Include/datetime.h b/Include/datetime.h
index 7bfbf0b..5c31162 100644
--- a/Include/datetime.h
+++ b/Include/datetime.h
@@ -27,6 +27,11 @@
/* # of bytes for year, month, day, hour, minute, second, and usecond. */
#define _PyDateTime_DATETIME_DATASIZE 10
+#define _PyTZINFO_HEAD \
+ PyObject_HEAD \
+ long hashcode; \
+ char hastzinfo; /* boolean flag */
+
typedef struct
{
PyObject_HEAD
@@ -49,20 +54,23 @@
PyObject *tzinfo;
} PyDateTime_DateTimeTZ;
-typedef struct
-{
- PyObject_HEAD
- long hashcode;
+
+
+#define _PyDateTime_TIMEHEAD \
+ _PyTZINFO_HEAD \
unsigned char data[_PyDateTime_TIME_DATASIZE];
-} PyDateTime_Time;
typedef struct
{
- PyObject_HEAD
- long hashcode;
- unsigned char data[_PyDateTime_TIME_DATASIZE];
+ _PyDateTime_TIMEHEAD
+} _PyDateTime_BaseTime; /* hastzinfo false */
+
+typedef struct
+{
+ _PyDateTime_TIMEHEAD
PyObject *tzinfo;
-} PyDateTime_TimeTZ;
+} PyDateTime_Time; /* hastzinfo true */
+
typedef struct
{
@@ -92,7 +100,7 @@
(((PyDateTime_DateTime*)o)->data[8] << 8) | \
((PyDateTime_DateTime*)o)->data[9])
-/* Apply for time and timetz instances. */
+/* Apply for time instances. */
#define PyDateTime_TIME_GET_HOUR(o) (((PyDateTime_Time*)o)->data[0])
#define PyDateTime_TIME_GET_MINUTE(o) (((PyDateTime_Time*)o)->data[1])
#define PyDateTime_TIME_GET_SECOND(o) (((PyDateTime_Time*)o)->data[2])
@@ -113,9 +121,6 @@
#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType)
#define PyTime_CheckExact(op) ((op)->ob_type == &PyDateTime_TimeType)
-#define PyTimeTZ_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeTZType)
-#define PyTimeTZ_CheckExact(op) ((op)->ob_type == &PyDateTime_TimeTZType)
-
#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType)
#define PyDelta_CheckExact(op) ((op)->ob_type == &PyDateTime_DeltaType)
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
index 614fed6..0cda5d0 100644
--- a/Lib/test/test_datetime.py
+++ b/Lib/test/test_datetime.py
@@ -1857,7 +1857,7 @@
tinfo = PicklableFixedOffset(-300, 'cookie')
orig = self.theclass(5, 6, 7, tzinfo=tinfo)
state = orig.__getstate__()
- derived = self.theclass()
+ derived = self.theclass(tzinfo=FixedOffset(0, "UTC", 0))
derived.__setstate__(state)
self.assertEqual(orig, derived)
self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset))
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index 642c11f..5840fb8 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -82,7 +82,6 @@
static PyTypeObject PyDateTime_DeltaType;
static PyTypeObject PyDateTime_TimeType;
static PyTypeObject PyDateTime_TZInfoType;
-static PyTypeObject PyDateTime_TimeTZType;
/* ---------------------------------------------------------------------------
* Math utilities.
@@ -607,8 +606,8 @@
if (PyDateTimeTZ_Check(self))
tzinfo = ((PyDateTime_DateTimeTZ *)self)->tzinfo;
- else if (PyTimeTZ_Check(self))
- tzinfo = ((PyDateTime_TimeTZ *)self)->tzinfo;
+ else if (PyTime_Check(self) && ((PyDateTime_Time *)self)->hastzinfo)
+ tzinfo = ((PyDateTime_Time *)self)->tzinfo;
return tzinfo;
}
@@ -783,8 +782,8 @@
/* an exception has been set; the caller should pass it on */
OFFSET_ERROR,
- /* type isn't date, datetime, datetimetz subclass, time, or
- * timetz subclass
+ /* type isn't date, datetime, datetimetz subclass, or time
+ * subclass
*/
OFFSET_UNKNOWN,
@@ -792,13 +791,13 @@
* datetime,
* datetimetz with None tzinfo,
* datetimetz where utcoffset() returns None
- * time,
- * timetz with None tzinfo,
- * timetz where utcoffset() returns None
+ * time with !hastzinfo
+ * time with None tzinfo,
+ * time where utcoffset() returns None
*/
OFFSET_NAIVE,
- /* timetz where utcoffset() doesn't return None,
+ /* time where utcoffset() doesn't return None,
* datetimetz where utcoffset() doesn't return None
*/
OFFSET_AWARE,
@@ -1264,7 +1263,7 @@
DATE_SET_MICROSECOND(self, us);
}
-/* For time and timetz. */
+/* For time. */
static void
set_time_fields(PyDateTime_Time *self, int h, int m, int s, int us)
{
@@ -1327,30 +1326,23 @@
/* Create a time instance with no range checking. */
static PyObject *
-new_time(int hour, int minute, int second, int usecond)
+new_time(int hour, int minute, int second, int usecond, PyObject *tzinfo)
{
PyDateTime_Time *self;
+ char aware = tzinfo != Py_None;
- self = PyObject_New(PyDateTime_Time, &PyDateTime_TimeType);
- if (self != NULL)
- set_time_fields(self, hour, minute, second, usecond);
- return (PyObject *) self;
-}
-
-/* Create a timetz instance with no range checking. */
-static PyObject *
-new_timetz(int hour, int minute, int second, int usecond, PyObject *tzinfo)
-{
- PyDateTime_TimeTZ *self;
-
- self = PyObject_New(PyDateTime_TimeTZ, &PyDateTime_TimeTZType);
- if (self != NULL) {
- set_time_fields((PyDateTime_Time *)self,
- hour, minute, second, usecond);
+ self = (PyDateTime_Time *)PyObject_MALLOC(aware ?
+ sizeof(PyDateTime_Time) :
+ sizeof(_PyDateTime_BaseTime));
+ if (self == NULL)
+ return PyErr_NoMemory();
+ self->hastzinfo = aware;
+ set_time_fields(self, hour, minute, second, usecond);
+ if (aware) {
Py_INCREF(tzinfo);
self->tzinfo = tzinfo;
}
- return (PyObject *) self;
+ return (PyObject *)PyObject_INIT(self, &PyDateTime_TimeType);
}
/* Create a timedelta instance. Normalize the members iff normalize is
@@ -1401,7 +1393,7 @@
static PyObject *date_unpickler_object = NULL;
static PyObject *datetimetz_unpickler_object = NULL;
static PyObject *tzinfo_unpickler_object = NULL;
-static PyObject *timetz_unpickler_object = NULL;
+static PyObject *time_unpickler_object = NULL;
/* ---------------------------------------------------------------------------
* Class implementations.
@@ -2953,7 +2945,7 @@
}
/* Return new datetime or datetimetz from date/datetime/datetimetz and
- * time/timetz arguments.
+ * time arguments.
*/
static PyObject *
datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
@@ -2974,9 +2966,11 @@
TIME_GET_MINUTE(time),
TIME_GET_SECOND(time),
TIME_GET_MICROSECOND(time));
- if (result && PyTimeTZ_Check(time) && PyDateTimeTZ_Check(result)) {
+ if (result &&
+ ((PyDateTime_Time *)time)->hastzinfo &&
+ PyDateTimeTZ_Check(result)) {
/* Copy the tzinfo field. */
- replace_tzinfo(result, ((PyDateTime_TimeTZ *)time)->tzinfo);
+ replace_tzinfo(result, ((PyDateTime_Time *)time)->tzinfo);
}
return result;
}
@@ -3343,11 +3337,11 @@
static PyObject *
datetime_gettime(PyDateTime_DateTime *self)
{
- return new_timetz(DATE_GET_HOUR(self),
- DATE_GET_MINUTE(self),
- DATE_GET_SECOND(self),
- DATE_GET_MICROSECOND(self),
- Py_None);
+ return new_time(DATE_GET_HOUR(self),
+ DATE_GET_MINUTE(self),
+ DATE_GET_SECOND(self),
+ DATE_GET_MICROSECOND(self),
+ Py_None);
}
static PyMethodDef datetime_methods[] = {
@@ -3398,7 +3392,7 @@
PyDoc_STR("Return datetime with new specified fields.")},
{"astimezone", (PyCFunction)datetime_astimezone, METH_KEYWORDS,
- PyDoc_STR("tz -> datetimetz with same date & time, and tzinfo=tz\n")},
+ PyDoc_STR("tz -> datetime with same date & time, and tzinfo=tz\n")},
{NULL, NULL}
};
@@ -3465,361 +3459,13 @@
};
/*
- * PyDateTime_Time implementation.
- */
-
-/* Accessor properties. */
-
-static PyObject *
-time_hour(PyDateTime_Time *self, void *unused)
-{
- return PyInt_FromLong(TIME_GET_HOUR(self));
-}
-
-static PyObject *
-time_minute(PyDateTime_Time *self, void *unused)
-{
- return PyInt_FromLong(TIME_GET_MINUTE(self));
-}
-
-static PyObject *
-py_time_second(PyDateTime_Time *self, void *unused)
-{
- return PyInt_FromLong(TIME_GET_SECOND(self));
-}
-
-static PyObject *
-time_microsecond(PyDateTime_Time *self, void *unused)
-{
- return PyInt_FromLong(TIME_GET_MICROSECOND(self));
-}
-
-static PyGetSetDef time_getset[] = {
- {"hour", (getter)time_hour},
- {"minute", (getter)time_minute},
- {"second", (getter)py_time_second},
- {"microsecond", (getter)time_microsecond},
- {NULL}
-};
-
-/* Constructors. */
-
-static char *time_kws[] = {"hour", "minute", "second", "microsecond", NULL};
-
-static PyObject *
-time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
-{
- PyObject *self = NULL;
- int hour = 0;
- int minute = 0;
- int second = 0;
- int usecond = 0;
-
-
- if (PyArg_ParseTupleAndKeywords(args, kw, "|iiii", time_kws,
- &hour, &minute, &second, &usecond)) {
- if (check_time_args(hour, minute, second, usecond) < 0)
- return NULL;
- self = new_time(hour, minute, second, usecond);
- }
- return self;
-}
-
-/* Various ways to turn a time into a string. */
-
-static PyObject *
-time_repr(PyDateTime_Time *self)
-{
- char buffer[100];
- char *typename = self->ob_type->tp_name;
- int h = TIME_GET_HOUR(self);
- int m = TIME_GET_MINUTE(self);
- int s = TIME_GET_SECOND(self);
- int us = TIME_GET_MICROSECOND(self);
-
- if (us)
- PyOS_snprintf(buffer, sizeof(buffer),
- "%s(%d, %d, %d, %d)", typename, h, m, s, us);
- else if (s)
- PyOS_snprintf(buffer, sizeof(buffer),
- "%s(%d, %d, %d)", typename, h, m, s);
- else
- PyOS_snprintf(buffer, sizeof(buffer),
- "%s(%d, %d)", typename, h, m);
- return PyString_FromString(buffer);
-}
-
-static PyObject *
-time_str(PyDateTime_Time *self)
-{
- return PyObject_CallMethod((PyObject *)self, "isoformat", "()");
-}
-
-static PyObject *
-time_isoformat(PyDateTime_Time *self)
-{
- char buffer[100];
- /* Reuse the time format code from the datetime type. */
- PyDateTime_DateTime datetime;
- PyDateTime_DateTime *pdatetime = &datetime;
-
- /* Copy over just the time bytes. */
- memcpy(pdatetime->data + _PyDateTime_DATE_DATASIZE,
- self->data,
- _PyDateTime_TIME_DATASIZE);
-
- isoformat_time(pdatetime, buffer, sizeof(buffer));
- return PyString_FromString(buffer);
-}
-
-static PyObject *
-time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
-{
- PyObject *result;
- PyObject *format;
- PyObject *tuple;
- static char *keywords[] = {"format", NULL};
-
- if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
- &PyString_Type, &format))
- return NULL;
-
- /* Python's strftime does insane things with the year part of the
- * timetuple. The year is forced to (the otherwise nonsensical)
- * 1900 to worm around that.
- */
- tuple = Py_BuildValue("iiiiiiiii",
- 1900, 0, 0, /* year, month, day */
- TIME_GET_HOUR(self),
- TIME_GET_MINUTE(self),
- TIME_GET_SECOND(self),
- 0, 0, -1); /* weekday, daynum, dst */
- if (tuple == NULL)
- return NULL;
- assert(PyTuple_Size(tuple) == 9);
- result = wrap_strftime((PyObject *)self, format, tuple, Py_None);
- Py_DECREF(tuple);
- return result;
-}
-
-/* Miscellaneous methods. */
-
-/* This is more natural as a tp_compare, but doesn't work then: for whatever
- * reason, Python's try_3way_compare ignores tp_compare unless
- * PyInstance_Check returns true, but these aren't old-style classes.
- * Note that this routine handles all comparisons for time and timetz.
- */
-static PyObject *
-time_richcompare(PyDateTime_Time *self, PyObject *other, int op)
-{
- int diff;
- naivety n1, n2;
- int offset1, offset2;
-
- if (! PyTime_Check(other)) {
- /* Stop this from falling back to address comparison. */
- PyErr_Format(PyExc_TypeError,
- "can't compare '%s' to '%s'",
- self->ob_type->tp_name,
- other->ob_type->tp_name);
- return NULL;
- }
- if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, Py_None,
- other, &offset2, &n2, Py_None) < 0)
- return NULL;
- assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN);
- /* If they're both naive, or both aware and have the same offsets,
- * we get off cheap. Note that if they're both naive, offset1 ==
- * offset2 == 0 at this point.
- */
- if (n1 == n2 && offset1 == offset2) {
- diff = memcmp(self->data, ((PyDateTime_Time *)other)->data,
- _PyDateTime_TIME_DATASIZE);
- return diff_to_bool(diff, op);
- }
-
- if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) {
- assert(offset1 != offset2); /* else last "if" handled it */
- /* Convert everything except microseconds to seconds. These
- * can't overflow (no more than the # of seconds in 2 days).
- */
- offset1 = TIME_GET_HOUR(self) * 3600 +
- (TIME_GET_MINUTE(self) - offset1) * 60 +
- TIME_GET_SECOND(self);
- offset2 = TIME_GET_HOUR(other) * 3600 +
- (TIME_GET_MINUTE(other) - offset2) * 60 +
- TIME_GET_SECOND(other);
- diff = offset1 - offset2;
- if (diff == 0)
- diff = TIME_GET_MICROSECOND(self) -
- TIME_GET_MICROSECOND(other);
- return diff_to_bool(diff, op);
- }
-
- assert(n1 != n2);
- PyErr_SetString(PyExc_TypeError,
- "can't compare offset-naive and "
- "offset-aware times");
- return NULL;
-}
-
-static long
-time_hash(PyDateTime_Time *self)
-{
- if (self->hashcode == -1) {
- naivety n;
- int offset;
- PyObject *temp;
-
- n = classify_utcoffset((PyObject *)self, Py_None, &offset);
- assert(n != OFFSET_UNKNOWN);
- if (n == OFFSET_ERROR)
- return -1;
-
- /* Reduce this to a hash of another object. */
- if (offset == 0)
- temp = PyString_FromStringAndSize((char *)self->data,
- _PyDateTime_TIME_DATASIZE);
- else {
- int hour;
- int minute;
-
- assert(n == OFFSET_AWARE);
- assert(PyTimeTZ_Check(self));
- hour = divmod(TIME_GET_HOUR(self) * 60 +
- TIME_GET_MINUTE(self) - offset,
- 60,
- &minute);
- if (0 <= hour && hour < 24)
- temp = new_time(hour, minute,
- TIME_GET_SECOND(self),
- TIME_GET_MICROSECOND(self));
- else
- temp = Py_BuildValue("iiii",
- hour, minute,
- TIME_GET_SECOND(self),
- TIME_GET_MICROSECOND(self));
- }
- if (temp != NULL) {
- self->hashcode = PyObject_Hash(temp);
- Py_DECREF(temp);
- }
- }
- return self->hashcode;
-}
-
-static PyObject *
-time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw)
-{
- PyObject *clone;
- PyObject *tuple;
- int hh = TIME_GET_HOUR(self);
- int mm = TIME_GET_MINUTE(self);
- int ss = TIME_GET_SECOND(self);
- int us = TIME_GET_MICROSECOND(self);
-
- if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiii:replace",
- time_kws,
- &hh, &mm, &ss, &us))
- return NULL;
- tuple = Py_BuildValue("iiii", hh, mm, ss, us);
- if (tuple == NULL)
- return NULL;
- clone = time_new(self->ob_type, tuple, NULL);
- Py_DECREF(tuple);
- return clone;
-}
-
-static int
-time_nonzero(PyDateTime_Time *self)
-{
- return TIME_GET_HOUR(self) ||
- TIME_GET_MINUTE(self) ||
- TIME_GET_SECOND(self) ||
- TIME_GET_MICROSECOND(self);
-}
-
-static PyMethodDef time_methods[] = {
- {"isoformat", (PyCFunction)time_isoformat, METH_KEYWORDS,
- PyDoc_STR("Return string in ISO 8601 format, HH:MM:SS[.mmmmmm].")},
-
- {"strftime", (PyCFunction)time_strftime, METH_KEYWORDS,
- PyDoc_STR("format -> strftime() style string.")},
-
- {"replace", (PyCFunction)time_replace, METH_KEYWORDS,
- PyDoc_STR("Return datetime with new specified fields.")},
- {NULL, NULL}
-};
-
-static char time_doc[] =
-PyDoc_STR("Basic time type.");
-
-static PyNumberMethods time_as_number = {
- 0, /* nb_add */
- 0, /* nb_subtract */
- 0, /* nb_multiply */
- 0, /* nb_divide */
- 0, /* nb_remainder */
- 0, /* nb_divmod */
- 0, /* nb_power */
- 0, /* nb_negative */
- 0, /* nb_positive */
- 0, /* nb_absolute */
- (inquiry)time_nonzero, /* nb_nonzero */
-};
-
-statichere PyTypeObject PyDateTime_TimeType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
- "datetime.time", /* tp_name */
- sizeof(PyDateTime_Time), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)PyObject_Del, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- (reprfunc)time_repr, /* tp_repr */
- &time_as_number, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- (hashfunc)time_hash, /* tp_hash */
- 0, /* tp_call */
- (reprfunc)time_str, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
- Py_TPFLAGS_BASETYPE, /* tp_flags */
- time_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- (richcmpfunc)time_richcompare, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- time_methods, /* tp_methods */
- 0, /* tp_members */
- time_getset, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- time_new, /* tp_new */
- _PyObject_Del, /* tp_free */
-};
-
-/*
* PyDateTime_TZInfo implementation.
*/
/* This is a pure abstract base class, so doesn't do anything beyond
* raising NotImplemented exceptions. Real tzinfo classes need
* to derive from this. This is mostly for clarity, and for efficiency in
- * datetimetz and timetz constructors (their tzinfo arguments need to
+ * datetimetz and time constructors (their tzinfo arguments need to
* be subclasses of this tzinfo class, which is easy and quick to check).
*
* Note: For reasons having to do with pickling of subclasses, we have
@@ -3938,22 +3584,51 @@
};
/*
- * PyDateTime_TimeTZ implementation.
+ * PyDateTime_Time implementation.
*/
-/* Accessor properties. Properties for hour, minute, second and microsecond
- * are inherited from time.
+/* Accessor properties.
*/
static PyObject *
-timetz_tzinfo(PyDateTime_TimeTZ *self, void *unused)
+time_hour(PyDateTime_Time *self, void *unused)
{
- Py_INCREF(self->tzinfo);
- return self->tzinfo;
+ return PyInt_FromLong(TIME_GET_HOUR(self));
}
-static PyGetSetDef timetz_getset[] = {
- {"tzinfo", (getter)timetz_tzinfo},
+static PyObject *
+time_minute(PyDateTime_Time *self, void *unused)
+{
+ return PyInt_FromLong(TIME_GET_MINUTE(self));
+}
+
+/* The name time_second conflicted with some platform header file. */
+static PyObject *
+py_time_second(PyDateTime_Time *self, void *unused)
+{
+ return PyInt_FromLong(TIME_GET_SECOND(self));
+}
+
+static PyObject *
+time_microsecond(PyDateTime_Time *self, void *unused)
+{
+ return PyInt_FromLong(TIME_GET_MICROSECOND(self));
+}
+
+static PyObject *
+time_tzinfo(PyDateTime_Time *self, void *unused)
+{
+ PyObject *result = self->hastzinfo ? self->tzinfo : Py_None;
+ Py_INCREF(result);
+ return result;
+}
+
+static PyGetSetDef time_getset[] = {
+ {"hour", (getter)time_hour},
+ {"minute", (getter)time_minute},
+ {"second", (getter)py_time_second},
+ {"microsecond", (getter)time_microsecond},
+ {"tzinfo", (getter)time_tzinfo},
{NULL}
};
@@ -3961,11 +3636,11 @@
* Constructors.
*/
-static char *timetz_kws[] = {"hour", "minute", "second", "microsecond",
- "tzinfo", NULL};
+static char *time_kws[] = {"hour", "minute", "second", "microsecond",
+ "tzinfo", NULL};
static PyObject *
-timetz_new(PyTypeObject *type, PyObject *args, PyObject *kw)
+time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *self = NULL;
int hour = 0;
@@ -3974,14 +3649,14 @@
int usecond = 0;
PyObject *tzinfo = Py_None;
- if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO", timetz_kws,
+ if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO", time_kws,
&hour, &minute, &second, &usecond,
&tzinfo)) {
if (check_time_args(hour, minute, second, usecond) < 0)
return NULL;
if (check_tzinfo_subclass(tzinfo) < 0)
return NULL;
- self = new_timetz(hour, minute, second, usecond, tzinfo);
+ self = new_time(hour, minute, second, usecond, tzinfo);
}
return self;
}
@@ -3991,9 +3666,10 @@
*/
static void
-timetz_dealloc(PyDateTime_TimeTZ *self)
+time_dealloc(PyDateTime_Time *self)
{
- Py_XDECREF(self->tzinfo);
+ if (self->hastzinfo)
+ Py_XDECREF(self->tzinfo);
self->ob_type->tp_free((PyObject *)self);
}
@@ -4003,43 +3679,76 @@
/* 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(self->tzinfo, "utcoffset", Py_None);
+time_utcoffset(PyDateTime_Time *self, PyObject *unused) {
+ return offset_as_timedelta(self->hastzinfo ? self->tzinfo : Py_None,
+ "utcoffset", Py_None);
}
static PyObject *
-timetz_dst(PyDateTime_TimeTZ *self, PyObject *unused) {
- return offset_as_timedelta(self->tzinfo, "dst", Py_None);
+time_dst(PyDateTime_Time *self, PyObject *unused) {
+ return offset_as_timedelta(self->hastzinfo ? self->tzinfo : Py_None,
+ "dst", Py_None);
}
static PyObject *
-timetz_tzname(PyDateTime_TimeTZ *self, PyObject *unused) {
- return call_tzname(self->tzinfo, Py_None);
+time_tzname(PyDateTime_Time *self, PyObject *unused) {
+ return call_tzname(self->hastzinfo ? self->tzinfo : Py_None,
+ Py_None);
}
/*
- * Various ways to turn a timetz into a string.
+ * Various ways to turn a time into a string.
*/
static PyObject *
-timetz_repr(PyDateTime_TimeTZ *self)
+time_repr(PyDateTime_Time *self)
{
- PyObject *baserepr = time_repr((PyDateTime_Time *)self);
+ char buffer[100];
+ char *typename = self->ob_type->tp_name;
+ int h = TIME_GET_HOUR(self);
+ int m = TIME_GET_MINUTE(self);
+ int s = TIME_GET_SECOND(self);
+ int us = TIME_GET_MICROSECOND(self);
+ PyObject *result = NULL;
- if (baserepr == NULL)
- return NULL;
- return append_keyword_tzinfo(baserepr, self->tzinfo);
+ if (us)
+ PyOS_snprintf(buffer, sizeof(buffer),
+ "%s(%d, %d, %d, %d)", typename, h, m, s, us);
+ else if (s)
+ PyOS_snprintf(buffer, sizeof(buffer),
+ "%s(%d, %d, %d)", typename, h, m, s);
+ else
+ PyOS_snprintf(buffer, sizeof(buffer),
+ "%s(%d, %d)", typename, h, m);
+ result = PyString_FromString(buffer);
+ if (result != NULL && self->hastzinfo)
+ result = append_keyword_tzinfo(result, self->tzinfo);
+ return result;
}
-/* Note: tp_str is inherited from time. */
+static PyObject *
+time_str(PyDateTime_Time *self)
+{
+ return PyObject_CallMethod((PyObject *)self, "isoformat", "()");
+}
static PyObject *
-timetz_isoformat(PyDateTime_TimeTZ *self)
+time_isoformat(PyDateTime_Time *self)
{
char buf[100];
- PyObject *result = time_isoformat((PyDateTime_Time *)self);
+ PyObject *result;
+ /* Reuse the time format code from the datetime type. */
+ PyDateTime_DateTime datetime;
+ PyDateTime_DateTime *pdatetime = &datetime;
- if (result == NULL || self->tzinfo == Py_None)
+ /* Copy over just the time bytes. */
+ memcpy(pdatetime->data + _PyDateTime_DATE_DATASIZE,
+ self->data,
+ _PyDateTime_TIME_DATASIZE);
+
+ isoformat_time(pdatetime, buf, sizeof(buf));
+ result = PyString_FromString(buf);
+ if (result == NULL || ! self->hastzinfo || self->tzinfo == Py_None)
return result;
/* We need to append the UTC offset. */
@@ -4052,16 +3761,146 @@
return result;
}
-/* Note: strftime() is inherited from time. */
+static PyObject *
+time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
+{
+ PyObject *result;
+ PyObject *format;
+ PyObject *tuple;
+ static char *keywords[] = {"format", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
+ &PyString_Type, &format))
+ return NULL;
+
+ /* Python's strftime does insane things with the year part of the
+ * timetuple. The year is forced to (the otherwise nonsensical)
+ * 1900 to worm around that.
+ */
+ tuple = Py_BuildValue("iiiiiiiii",
+ 1900, 0, 0, /* year, month, day */
+ TIME_GET_HOUR(self),
+ TIME_GET_MINUTE(self),
+ TIME_GET_SECOND(self),
+ 0, 0, -1); /* weekday, daynum, dst */
+ if (tuple == NULL)
+ return NULL;
+ assert(PyTuple_Size(tuple) == 9);
+ result = wrap_strftime((PyObject *)self, format, tuple, Py_None);
+ Py_DECREF(tuple);
+ return result;
+}
/*
* Miscellaneous methods.
*/
-/* Note: tp_richcompare and tp_hash are inherited from time. */
+/* This is more natural as a tp_compare, but doesn't work then: for whatever
+ * reason, Python's try_3way_compare ignores tp_compare unless
+ * PyInstance_Check returns true, but these aren't old-style classes.
+ */
+static PyObject *
+time_richcompare(PyDateTime_Time *self, PyObject *other, int op)
+{
+ int diff;
+ naivety n1, n2;
+ int offset1, offset2;
+
+ if (! PyTime_Check(other)) {
+ /* Stop this from falling back to address comparison. */
+ PyErr_Format(PyExc_TypeError,
+ "can't compare '%s' to '%s'",
+ self->ob_type->tp_name,
+ other->ob_type->tp_name);
+ return NULL;
+ }
+ if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, Py_None,
+ other, &offset2, &n2, Py_None) < 0)
+ return NULL;
+ assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN);
+ /* If they're both naive, or both aware and have the same offsets,
+ * we get off cheap. Note that if they're both naive, offset1 ==
+ * offset2 == 0 at this point.
+ */
+ if (n1 == n2 && offset1 == offset2) {
+ diff = memcmp(self->data, ((PyDateTime_Time *)other)->data,
+ _PyDateTime_TIME_DATASIZE);
+ return diff_to_bool(diff, op);
+ }
+
+ if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) {
+ assert(offset1 != offset2); /* else last "if" handled it */
+ /* Convert everything except microseconds to seconds. These
+ * can't overflow (no more than the # of seconds in 2 days).
+ */
+ offset1 = TIME_GET_HOUR(self) * 3600 +
+ (TIME_GET_MINUTE(self) - offset1) * 60 +
+ TIME_GET_SECOND(self);
+ offset2 = TIME_GET_HOUR(other) * 3600 +
+ (TIME_GET_MINUTE(other) - offset2) * 60 +
+ TIME_GET_SECOND(other);
+ diff = offset1 - offset2;
+ if (diff == 0)
+ diff = TIME_GET_MICROSECOND(self) -
+ TIME_GET_MICROSECOND(other);
+ return diff_to_bool(diff, op);
+ }
+
+ assert(n1 != n2);
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare offset-naive and "
+ "offset-aware times");
+ return NULL;
+}
+
+static long
+time_hash(PyDateTime_Time *self)
+{
+ if (self->hashcode == -1) {
+ naivety n;
+ int offset;
+ PyObject *temp;
+
+ n = classify_utcoffset((PyObject *)self, Py_None, &offset);
+ assert(n != OFFSET_UNKNOWN);
+ if (n == OFFSET_ERROR)
+ return -1;
+
+ /* Reduce this to a hash of another object. */
+ if (offset == 0)
+ temp = PyString_FromStringAndSize((char *)self->data,
+ _PyDateTime_TIME_DATASIZE);
+ else {
+ int hour;
+ int minute;
+
+ assert(n == OFFSET_AWARE);
+ assert(self->hastzinfo);
+ hour = divmod(TIME_GET_HOUR(self) * 60 +
+ TIME_GET_MINUTE(self) - offset,
+ 60,
+ &minute);
+ if (0 <= hour && hour < 24)
+ temp = new_time(hour, minute,
+ TIME_GET_SECOND(self),
+ TIME_GET_MICROSECOND(self),
+ Py_None);
+ else
+ temp = Py_BuildValue("iiii",
+ hour, minute,
+ TIME_GET_SECOND(self),
+ TIME_GET_MICROSECOND(self));
+ }
+ if (temp != NULL) {
+ self->hashcode = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+ }
+ return self->hashcode;
+}
static PyObject *
-timetz_replace(PyDateTime_TimeTZ *self, PyObject *args, PyObject *kw)
+time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw)
{
PyObject *clone;
PyObject *tuple;
@@ -4069,22 +3908,22 @@
int mm = TIME_GET_MINUTE(self);
int ss = TIME_GET_SECOND(self);
int us = TIME_GET_MICROSECOND(self);
- PyObject *tzinfo = self->tzinfo;
+ PyObject *tzinfo = self->hastzinfo ? self->tzinfo : Py_None;
if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO:replace",
- timetz_kws,
+ time_kws,
&hh, &mm, &ss, &us, &tzinfo))
return NULL;
tuple = Py_BuildValue("iiiiO", hh, mm, ss, us, tzinfo);
if (tuple == NULL)
return NULL;
- clone = timetz_new(self->ob_type, tuple, NULL);
+ clone = time_new(self->ob_type, tuple, NULL);
Py_DECREF(tuple);
return clone;
}
static int
-timetz_nonzero(PyDateTime_TimeTZ *self)
+time_nonzero(PyDateTime_Time *self)
{
int offset;
int none;
@@ -4096,7 +3935,7 @@
return 1;
}
offset = 0;
- if (self->tzinfo != Py_None) {
+ if (self->hastzinfo && self->tzinfo != Py_None) {
offset = call_utcoffset(self->tzinfo, Py_None, &none);
if (offset == -1 && PyErr_Occurred())
return -1;
@@ -4113,7 +3952,7 @@
* So it's a tuple in any (non-error) case.
*/
static PyObject *
-timetz_getstate(PyDateTime_TimeTZ *self)
+time_getstate(PyDateTime_Time *self)
{
PyObject *basestate;
PyObject *result = NULL;
@@ -4121,7 +3960,7 @@
basestate = PyString_FromStringAndSize((char *)self->data,
_PyDateTime_TIME_DATASIZE);
if (basestate != NULL) {
- if (self->tzinfo == Py_None)
+ if (! self->hastzinfo || self->tzinfo == Py_None)
result = Py_BuildValue("(O)", basestate);
else
result = Py_BuildValue("OO", basestate, self->tzinfo);
@@ -4131,7 +3970,7 @@
}
static PyObject *
-timetz_setstate(PyDateTime_TimeTZ *self, PyObject *state)
+time_setstate(PyDateTime_Time *self, PyObject *state)
{
PyObject *basestate;
PyObject *tzinfo = Py_None;
@@ -4146,33 +3985,41 @@
"bad argument to time.__setstate__");
return NULL;
}
+ if (tzinfo != Py_None && ! self->hastzinfo) {
+ PyErr_SetString(PyExc_ValueError, "time.__setstate__ can't "
+ "add a non-None tzinfo to a time object that "
+ "doesn't have one already");
+ return NULL;
+ }
memcpy((char *)self->data,
PyString_AsString(basestate),
_PyDateTime_TIME_DATASIZE);
self->hashcode = -1;
- Py_INCREF(tzinfo);
- Py_XDECREF(self->tzinfo);
- self->tzinfo = tzinfo;
+ if (self->hastzinfo) {
+ Py_INCREF(tzinfo);
+ Py_XDECREF(self->tzinfo);
+ self->tzinfo = tzinfo;
+ }
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
-timetz_pickler(PyObject *module, PyDateTime_TimeTZ *timetz)
+time_pickler(PyObject *module, PyDateTime_Time *time)
{
PyObject *state;
PyObject *result = NULL;
- if (! PyTimeTZ_CheckExact(timetz)) {
+ if (! PyTime_CheckExact(time)) {
PyErr_Format(PyExc_TypeError,
- "bad type passed to timetz pickler: %s",
- timetz->ob_type->tp_name);
+ "bad type passed to time pickler: %s",
+ time->ob_type->tp_name);
return NULL;
}
- state = timetz_getstate(timetz);
+ state = time_getstate(time);
if (state) {
result = Py_BuildValue("O(O)",
- timetz_unpickler_object,
+ time_unpickler_object,
state);
Py_DECREF(state);
}
@@ -4180,55 +4027,70 @@
}
static PyObject *
-timetz_unpickler(PyObject *module, PyObject *arg)
+time_unpickler(PyObject *module, PyObject *arg)
{
- PyDateTime_TimeTZ *self;
+ PyDateTime_Time *self;
+ PyObject *tzinfo = Py_None;
- self = PyObject_New(PyDateTime_TimeTZ, &PyDateTime_TimeTZType);
+ /* We don't want to allocate space for tzinfo if it's not needed.
+ * Figuring that out in advance is irritating, so for now we
+ * realloc later.
+ */
+ self = PyObject_New(PyDateTime_Time, &PyDateTime_TimeType);
if (self != NULL) {
PyObject *res;
- self->tzinfo = NULL;
- res = timetz_setstate(self, arg);
+ self->tzinfo = Py_None;
+ Py_INCREF(self->tzinfo);
+ self->hastzinfo = (char)1; /* true */
+ res = time_setstate(self, arg);
if (res == NULL) {
Py_DECREF(self);
return NULL;
}
Py_DECREF(res);
+ if (self->tzinfo == Py_None) {
+ /* shrinking; can't fail */
+ Py_DECREF(self->tzinfo);
+ PyObject_Realloc(self, sizeof(_PyDateTime_BaseTime));
+ self->hastzinfo = (char)0;
+ }
}
return (PyObject *)self;
}
-static PyMethodDef timetz_methods[] = {
- {"isoformat", (PyCFunction)timetz_isoformat, METH_KEYWORDS,
+static PyMethodDef time_methods[] = {
+ {"isoformat", (PyCFunction)time_isoformat, METH_KEYWORDS,
PyDoc_STR("Return string in ISO 8601 format, HH:MM:SS[.mmmmmm]"
"[+HH:MM].")},
- {"utcoffset", (PyCFunction)timetz_utcoffset, METH_NOARGS,
+ {"strftime", (PyCFunction)time_strftime, METH_KEYWORDS,
+ PyDoc_STR("format -> strftime() style string.")},
+
+ {"utcoffset", (PyCFunction)time_utcoffset, METH_NOARGS,
PyDoc_STR("Return self.tzinfo.utcoffset(self).")},
- {"tzname", (PyCFunction)timetz_tzname, METH_NOARGS,
+ {"tzname", (PyCFunction)time_tzname, METH_NOARGS,
PyDoc_STR("Return self.tzinfo.tzname(self).")},
- {"dst", (PyCFunction)timetz_dst, METH_NOARGS,
+ {"dst", (PyCFunction)time_dst, METH_NOARGS,
PyDoc_STR("Return self.tzinfo.dst(self).")},
- {"replace", (PyCFunction)timetz_replace, METH_KEYWORDS,
- PyDoc_STR("Return timetz with new specified fields.")},
+ {"replace", (PyCFunction)time_replace, METH_KEYWORDS,
+ PyDoc_STR("Return time with new specified fields.")},
- {"__setstate__", (PyCFunction)timetz_setstate, METH_O,
+ {"__setstate__", (PyCFunction)time_setstate, METH_O,
PyDoc_STR("__setstate__(state)")},
- {"__getstate__", (PyCFunction)timetz_getstate, METH_NOARGS,
+ {"__getstate__", (PyCFunction)time_getstate, METH_NOARGS,
PyDoc_STR("__getstate__() -> state")},
{NULL, NULL}
-
};
-static char timetz_doc[] =
+static char time_doc[] =
PyDoc_STR("Time type.");
-static PyNumberMethods timetz_as_number = {
+static PyNumberMethods time_as_number = {
0, /* nb_add */
0, /* nb_subtract */
0, /* nb_multiply */
@@ -4239,50 +4101,50 @@
0, /* nb_negative */
0, /* nb_positive */
0, /* nb_absolute */
- (inquiry)timetz_nonzero, /* nb_nonzero */
+ (inquiry)time_nonzero, /* nb_nonzero */
};
-statichere PyTypeObject PyDateTime_TimeTZType = {
+statichere PyTypeObject PyDateTime_TimeType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"datetime.time", /* tp_name */
- sizeof(PyDateTime_TimeTZ), /* tp_basicsize */
+ sizeof(PyDateTime_Time), /* tp_basicsize */
0, /* tp_itemsize */
- (destructor)timetz_dealloc, /* tp_dealloc */
+ (destructor)time_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
- (reprfunc)timetz_repr, /* tp_repr */
- &timetz_as_number, /* tp_as_number */
+ (reprfunc)time_repr, /* tp_repr */
+ &time_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
- 0, /* tp_hash */
+ (hashfunc)time_hash, /* tp_hash */
0, /* tp_call */
- 0, /* tp_str */
+ (reprfunc)time_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
Py_TPFLAGS_BASETYPE, /* tp_flags */
- timetz_doc, /* tp_doc */
+ time_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
- 0, /* tp_richcompare */
+ (richcmpfunc)time_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
- timetz_methods, /* tp_methods */
+ time_methods, /* tp_methods */
0, /* tp_members */
- timetz_getset, /* tp_getset */
- &PyDateTime_TimeType, /* tp_base */
+ time_getset, /* tp_getset */
+ 0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
- timetz_new, /* tp_new */
+ time_new, /* tp_new */
_PyObject_Del, /* tp_free */
};
@@ -4789,11 +4651,11 @@
static PyObject *
datetimetz_gettimetz(PyDateTime_DateTimeTZ *self)
{
- return new_timetz(DATE_GET_HOUR(self),
- DATE_GET_MINUTE(self),
- DATE_GET_SECOND(self),
- DATE_GET_MICROSECOND(self),
- self->tzinfo);
+ return new_time(DATE_GET_HOUR(self),
+ DATE_GET_MINUTE(self),
+ DATE_GET_SECOND(self),
+ DATE_GET_MICROSECOND(self),
+ self->tzinfo);
}
/*
@@ -5019,8 +4881,8 @@
{"_date_unpickler", (PyCFunction)date_unpickler, METH_O, NULL},
{"_datetimetz_pickler", (PyCFunction)datetimetz_pickler,METH_O, NULL},
{"_datetimetz_unpickler",(PyCFunction)datetimetz_unpickler,METH_O, NULL},
- {"_timetz_pickler", (PyCFunction)timetz_pickler, METH_O, NULL},
- {"_timetz_unpickler", (PyCFunction)timetz_unpickler, METH_O, NULL},
+ {"_time_pickler", (PyCFunction)time_pickler, METH_O, NULL},
+ {"_time_unpickler", (PyCFunction)time_unpickler, METH_O, NULL},
{"_tzinfo_pickler", (PyCFunction)tzinfo_pickler, METH_O, NULL},
{"_tzinfo_unpickler", (PyCFunction)tzinfo_unpickler, METH_NOARGS,
NULL},
@@ -5054,8 +4916,6 @@
return;
if (PyType_Ready(&PyDateTime_TZInfoType) < 0)
return;
- if (PyType_Ready(&PyDateTime_TimeTZType) < 0)
- return;
if (PyType_Ready(&PyDateTime_DateTimeTZType) < 0)
return;
@@ -5079,15 +4939,15 @@
Py_DECREF(x);
Py_DECREF(pickler);
- pickler = PyObject_GetAttrString(m, "_timetz_pickler");
+ pickler = PyObject_GetAttrString(m, "_time_pickler");
if (pickler == NULL) return;
- timetz_unpickler_object = PyObject_GetAttrString(m,
- "_timetz_unpickler");
- if (timetz_unpickler_object == NULL) return;
+ time_unpickler_object = PyObject_GetAttrString(m,
+ "_time_unpickler");
+ if (time_unpickler_object == NULL) return;
x = PyObject_CallMethod(copyreg, "pickle", "OOO",
- &PyDateTime_TimeTZType,
+ &PyDateTime_TimeType,
pickler,
- timetz_unpickler_object);
+ time_unpickler_object);
if (x == NULL) return;
Py_DECREF(x);
Py_DECREF(pickler);
@@ -5160,15 +5020,15 @@
return;
Py_DECREF(x);
- /* timetz values */
- d = PyDateTime_TimeTZType.tp_dict;
+ /* time values */
+ d = PyDateTime_TimeType.tp_dict;
- x = new_timetz(0, 0, 0, 0, Py_None);
+ x = new_time(0, 0, 0, 0, Py_None);
if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
return;
Py_DECREF(x);
- x = new_timetz(23, 59, 59, 999999, Py_None);
+ x = new_time(23, 59, 59, 999999, Py_None);
if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
return;
Py_DECREF(x);
@@ -5211,8 +5071,8 @@
Py_INCREF(&PyDateTime_TZInfoType);
PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType);
- Py_INCREF(&PyDateTime_TimeTZType);
- PyModule_AddObject(m, "time", (PyObject *) &PyDateTime_TimeTZType);
+ Py_INCREF(&PyDateTime_TimeType);
+ PyModule_AddObject(m, "time", (PyObject *) &PyDateTime_TimeType);
Py_INCREF(&PyDateTime_DateTimeTZType);
PyModule_AddObject(m, "datetime",