[Python-checkins] python/nondist/sandbox/datetime obj_datetimetz.c,NONE,1.1 datetime.c,1.62,1.63 datetime.h,1.15,1.16 obj_date.c,1.48,1.49 obj_datetime.c,1.46,1.47

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Thu, 12 Dec 2002 17:13:49 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv26152

Modified Files:
	datetime.c datetime.h obj_date.c obj_datetime.c 
Added Files:
	obj_datetimetz.c 
Log Message:
Added a non-functional C datetimetz implementation.  It compiles and
the tests pass, but the new C type isn't really being used yet.


--- NEW FILE: obj_datetimetz.c ---
/* XXX This isn't functional yet, it merely compiles.  It's a copy and
 * XXX very light edit of obj_datetime.c.
 */
/*
 * PyDateTime_DateTimeTZ implementation.
 */

/* Accessor properties. */

static PyObject *
datetimetz_hour(PyDateTime_DateTimeTZ *self, void *unused)
{
	return PyInt_FromLong(DATE_GET_HOUR(self));
}

static PyObject *
datetimetz_minute(PyDateTime_DateTimeTZ *self, void *unused)
{
	return PyInt_FromLong(DATE_GET_MINUTE(self));
}

static PyObject *
datetimetz_second(PyDateTime_DateTimeTZ *self, void *unused)
{
	return PyInt_FromLong(DATE_GET_SECOND(self));
}

static PyObject *
datetimetz_microsecond(PyDateTime_DateTimeTZ *self, void *unused)
{
	return PyInt_FromLong(DATE_GET_MICROSECOND(self));
}

static PyGetSetDef datetimetz_getset[] = {
	{"hour",        (getter)datetimetz_hour},
	{"minute",      (getter)datetimetz_minute},
	{"second",      (getter)datetimetz_second},
	{"microsecond", (getter)datetimetz_microsecond},
	{NULL}
};

/* Constructors. */

static PyObject *
datetimetz_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
	PyObject *self = NULL;
	long year;
	long month;
	long day;
	long hour = 0;
	long minute = 0;
	long second = 0;
	long usecond = 0;

	static char *keywords[] = {
		"year", "month", "day", "hour", "minute", "second",
		"microsecond", NULL
	};

	if (PyArg_ParseTupleAndKeywords(args, kw, "lll|llll", keywords,
					&year, &month, &day, &hour, &minute,
					&second, &usecond)) {
		if (year < MINYEAR || year > MAXYEAR) {
			PyErr_SetString(PyExc_ValueError,
					"year is out of range");
			return NULL;
		}
		if (month < 1 || month > 12) {
			PyErr_SetString(PyExc_ValueError,
					"month must be in 1..12");
			return NULL;
		}
		if (day < 1 || day > days_in_month(year, month)) {
			PyErr_SetString(PyExc_ValueError,
					"day is out of range for month");
			return NULL;
		}
		if (hour < 0 || hour > 23) {
			PyErr_SetString(PyExc_ValueError,
					"hour must be in 0..23");
			return NULL;
		}
		if (minute < 0 || minute > 59) {
			PyErr_SetString(PyExc_ValueError,
					"minute must be in 0..59");
			return NULL;
		}
		if (second < 0 || second > 59) {
			PyErr_SetString(PyExc_ValueError,
					"second must be in 0..59");
			return NULL;
		}
		if (usecond < 0 || usecond > 999999) {
			PyErr_SetString(PyExc_ValueError,
					"microsecond must be in 0..999999");
			return NULL;
		}
		self = new_datetime(year, month, day, hour, minute, second,
				     usecond);
	}
	return self;
}


/* TM_FUNC is the shared type of localtime() and gmtime(). */
typedef struct tm *(*TM_FUNC)(const time_t *timer);

/* Internal helper.
 * Build datetime from a time_t and a distinct count of microseconds.
 * Pass localtime or gmtime for f, to control the interpretation of timet.
 */
static PyObject *
datetimetz_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, long us)
{
	struct tm *tm;
	PyObject *result = NULL;

	tm = f(&timet);
	if (tm)
		result = PyObject_CallFunction(cls, "iiiiiil",
					       tm->tm_year + 1900,
					       tm->tm_mon + 1,
					       tm->tm_mday,
					       tm->tm_hour,
					       tm->tm_min,
					       tm->tm_sec,
					       us);
	else
		PyErr_SetString(PyExc_ValueError,
				"timestamp out of range for "
				"platform localtime()/gmtime() function");
	return result;
}

/* Internal helper.
 * Build datetime from a Python timestamp.  Pass localtime or gmtime for f,
 * to control the interpretation of the timestamp.  Since a double doesn't
 * have enough bits to cover a datetime's full range of precision, it's
 * better to call datetimetz_from_timet_and_us provided you have a way
 * to get that much precision (e.g., C time() isn't good enough).
 */
static PyObject *
datetimetz_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp)
{
	time_t timet = (time_t)timestamp;
	long us = (long)((timestamp - (double)timet) * 1e6);

	return datetimetz_from_timet_and_us(cls, f, timet, us);
}

/* Internal helper.
 * Build most accurate possible datetime for current time.  Pass localtime or
 * gmtime for f as appropriate.
 */
static PyObject *
datetimetz_best_possible(PyObject *cls, TM_FUNC f)
{
#ifdef HAVE_GETTIMEOFDAY
	struct timeval t;

#ifdef GETTIMEOFDAY_NO_TZ
	gettimeofday(&t);
#else
	gettimeofday(&t, (struct timezone *)NULL);
#endif
	return datetimetz_from_timet_and_us(cls, f, t.tv_sec, t.tv_usec);

#else	/* ! HAVE_GETTIMEOFDAY */
	/* No flavor of gettimeofday exists on this platform.  Python's
	 * time.time() does a lot of other platform tricks to get the
	 * best time it can on the platform, and we're not going to do
	 * better than that (if we could, the better code would belong
	 * in time.time()!)  We're limited by the precision of a double,
	 * though.
	 */
	PyObject *time;
	double dtime;

	time = time_time();
    	if (time == NULL)
    		return NULL;
	dtime = PyFloat_AsDouble(time);
	Py_DECREF(time);
	if (dtime == -1.0 && PyErr_Occurred())
		return NULL;
	return datetimetz_from_timestamp(cls, f, dtime);
#endif	/* ! HAVE_GETTIMEOFDAY */
}

/* Return new local datetime from timestamp (Python timestamp -- a double). */
static PyObject *
datetimetz_fromtimestamp(PyObject *cls, PyObject *args)
{
	double timestamp;
	PyObject *result = NULL;

	if (PyArg_ParseTuple(args, "d:fromtimestamp", &timestamp))
		result = datetimetz_from_timestamp(cls, localtime, timestamp);
	return result;
}

/* Return new UTC datetime from timestamp (Python timestamp -- a double). */
static PyObject *
datetimetz_utcfromtimestamp(PyObject *cls, PyObject *args)
{
	double timestamp;
	PyObject *result = NULL;

	if (PyArg_ParseTuple(args, "d:utcfromtimestamp", &timestamp))
		result = datetimetz_from_timestamp(cls, gmtime, timestamp);
	return result;
}

/* Return best possible local time -- this isn't constrained by the
 * precision of a timestamp.
 */
static PyObject *
datetimetz_now(PyObject *cls, PyObject *dummy)
{
	return datetimetz_best_possible(cls, localtime);
}

/* Return best possible UTC time -- this isn't constrained by the
 * precision of a timestamp.
 */
static PyObject *
datetimetz_utcnow(PyObject *cls, PyObject *dummy)
{
	return datetimetz_best_possible(cls, gmtime);
}

/* Return new datetime from date and time arguments. */
static PyObject *
datetimetz_combine(PyObject *cls, PyObject *args, PyObject *kw)
{
 	static char *keywords[] = {"date", "time", NULL};
	PyObject *date;
	PyObject *time;
	PyObject *result = NULL;

	if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!:combine", keywords,
					&PyDateTime_DateType, &date,
					&PyDateTime_TimeType, &time))
		result = PyObject_CallFunction(cls, "iiiiiii",
						GET_YEAR(date),
				    		GET_MONTH(date),
						GET_DAY(date),
				    		TIME_GET_HOUR(time),
				    		TIME_GET_MINUTE(time),
				    		TIME_GET_SECOND(time),
				    		TIME_GET_MICROSECOND(time));
	return result;
}

/* datetime arithmetic. */

static PyObject *
add_datetimetz_timedelta(PyDateTime_DateTimeTZ *date, PyDateTime_Delta *delta)
{
	/* Note that the C-level additions can't overflow, because of
	 * invariant bounds on the member values.
	 */
	long year = GET_YEAR(date);
	long month = GET_MONTH(date);
	long day = GET_DAY(date) + GET_TD_DAYS(delta);
	long hour = DATE_GET_HOUR(date);
	long minute = DATE_GET_MINUTE(date);
	long second = DATE_GET_SECOND(date) + GET_TD_SECONDS(delta);
	long microsecond = DATE_GET_MICROSECOND(date) +
			   GET_TD_MICROSECONDS(delta);

	if (normalize_datetime(&year, &month, &day,
			       &hour, &minute, &second, &microsecond) < 0)
		return NULL;
	else
		return new_datetime(year, month, day,
				    hour, minute, second, microsecond);
}

static PyObject *
sub_datetimetz_timedelta(PyDateTime_DateTimeTZ *date, PyDateTime_Delta *delta)
{
	/* Note that the C-level subtractions can't overflow, because of
	 * invariant bounds on the member values.
	 */
	long year = GET_YEAR(date);
	long month = GET_MONTH(date);
	long day = GET_DAY(date) - GET_TD_DAYS(delta);
	long hour = DATE_GET_HOUR(date);
	long minute = DATE_GET_MINUTE(date);
	long second = DATE_GET_SECOND(date) - GET_TD_SECONDS(delta);
	long microsecond = DATE_GET_MICROSECOND(date) -
			   GET_TD_MICROSECONDS(delta);

	if (normalize_datetime(&year, &month, &day,
			       &hour, &minute, &second, &microsecond) < 0)
		return NULL;
	else
		return new_datetime(year, month, day,
				    hour, minute, second, microsecond);
}

static PyObject *
sub_datetimetz_datetime(PyDateTime_DateTimeTZ *left, PyDateTime_DateTimeTZ *right)
{
	long days1 = ymd_to_ord(GET_YEAR(left),
				GET_MONTH(left),
				GET_DAY(left));
	long days2 = ymd_to_ord(GET_YEAR(right),
				GET_MONTH(right),
				GET_DAY(right));
	/* These can't overflow, since the values are normalized.  At most
	 * this gives the number of seconds in one day.
	 */
	long delta_s = (DATE_GET_HOUR(left) - DATE_GET_HOUR(right)) * 3600 +
	               (DATE_GET_MINUTE(left) - DATE_GET_MINUTE(right)) * 60 +
		       DATE_GET_SECOND(left) - DATE_GET_SECOND(right);
	long delta_us = DATE_GET_MICROSECOND(left) -
			DATE_GET_MICROSECOND(right);

	return new_delta(days1 - days2, delta_s, delta_us, 1);
}

static PyObject *
datetimetz_add(PyObject *left, PyObject *right)
{
	PyTypeObject *left_type = left->ob_type;
	PyTypeObject *right_type = right->ob_type;

	if (PyType_IsSubtype(left_type, &PyDateTime_DateTimeType)) {
		/* datetime + ??? */
		if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType))
			/* datetime + delta */
			return add_datetimetz_timedelta(
					(PyDateTime_DateTimeTZ *)left,
					(PyDateTime_Delta *)right);
	}
	else if (PyType_IsSubtype(left_type, &PyDateTime_DeltaType)) {
		/* delta + datetime */
		return add_datetimetz_timedelta((PyDateTime_DateTimeTZ *) right,
					      (PyDateTime_Delta *) left);
	}
	Py_INCREF(Py_NotImplemented);
	return Py_NotImplemented;
}

static PyObject *
datetimetz_subtract(PyObject *left, PyObject *right)
{
	PyTypeObject *left_type = left->ob_type;
	PyTypeObject *right_type = right->ob_type;
	PyObject *result = Py_NotImplemented;

	if (PyType_IsSubtype(left_type, &PyDateTime_DateTimeType)) {
		/* datetime - ??? */
		if (PyType_IsSubtype(right_type, &PyDateTime_DateTimeType)) {
			/* datetime - datetime */
			result = sub_datetimetz_datetime(
					(PyDateTime_DateTimeTZ *)left,
					(PyDateTime_DateTimeTZ *)right);
		}
		else if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
			/* datetime - delta */
			result = sub_datetimetz_timedelta(
					(PyDateTime_DateTimeTZ *)left,
					(PyDateTime_Delta *)right);
		}
	}

	if (result == Py_NotImplemented)
		Py_INCREF(result);
	return result;
}

/* Various ways to turn a datetime into a string. */

static PyObject *
datetimetz_repr(PyDateTime_DateTimeTZ *self)
{
	char buffer[1000];
	char *typename = self->ob_type->tp_name;

	if (DATE_GET_MICROSECOND(self)) {
		PyOS_snprintf(buffer, sizeof(buffer),
			      "%s(%d, %d, %d, %d, %d, %d, %d)",
			      typename,
			      GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
			      DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
			      DATE_GET_SECOND(self),
			      DATE_GET_MICROSECOND(self));
	}
	else if (DATE_GET_SECOND(self)) {
		PyOS_snprintf(buffer, sizeof(buffer),
			      "%s(%d, %d, %d, %d, %d, %d)",
			      typename,
			      GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
			      DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
			      DATE_GET_SECOND(self));
	}
	else {
		PyOS_snprintf(buffer, sizeof(buffer),
			      "%s(%d, %d, %d, %d, %d)",
			      typename,
			      GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
			      DATE_GET_HOUR(self), DATE_GET_MINUTE(self));
	}
	return PyString_FromString(buffer);
}

static PyObject *
datetimetz_str(PyDateTime_DateTimeTZ *self)
{
	return PyObject_CallMethod((PyObject *)self, "isoformat", "(s)", " ");
}

static PyObject *
datetimetz_isoformat(PyDateTime_DateTimeTZ *self,
                   PyObject *args, PyObject *kw)
{
	char sep = 'T';
	static char *keywords[] = {"sep", NULL};
	char buffer[100];
	char *cp;

	if (!PyArg_ParseTupleAndKeywords(args, kw, "|c:isoformat", keywords,
					 &sep))
		return NULL;
	cp = isoformat_date((PyDateTime_Date *)self, buffer, sizeof(buffer));
	assert(cp != NULL);
	*cp++ = sep;
	isoformat_time((PyDateTime_DateTime *)self,
		       cp,
		       sizeof(buffer) - (cp - buffer));
	return PyString_FromString(buffer);
}

static PyObject *
datetimetz_ctime(PyDateTime_DateTimeTZ *self)
{
	return format_ctime((PyDateTime_Date *)self,
			    DATE_GET_HOUR(self),
			    DATE_GET_MINUTE(self),
			    DATE_GET_SECOND(self));
}

/* 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.
 */
static PyObject *
datetimetz_richcompare(PyDateTime_DateTimeTZ *self, PyObject *other, int op)
{
	long diff;

	if (!PyType_IsSubtype(other->ob_type, &PyDateTime_DateTimeTZType)) {
		PyErr_Format(PyExc_TypeError,
			     "can't compare datetime to %s instance",
			     other->ob_type->tp_name);
		return NULL;
	}
	diff = memcmp(self->data, ((PyDateTime_DateTimeTZ *)other)->data,
		      _PyDateTime_DATETIME_DATASIZE);
	return diff_to_bool(diff, op);
}

static PyObject *datetimetz_getstate(PyDateTime_DateTimeTZ *self);

static long
datetimetz_hash(PyDateTime_DateTimeTZ *self)
{
	if (self->hashcode == -1) {
		PyObject *temp = datetimetz_getstate(self);
		if (temp != NULL) {
			self->hashcode = PyObject_Hash(temp);
			Py_DECREF(temp);
		}
	}
	return self->hashcode;
}

static PyObject *
datetimetz_timetuple(PyDateTime_DateTimeTZ *self)
{
	return build_struct_time(GET_YEAR(self),
				 GET_MONTH(self),
				 GET_DAY(self),
				 DATE_GET_HOUR(self),
				 DATE_GET_MINUTE(self),
				 DATE_GET_SECOND(self));
}

static PyObject *
datetimetz_getdate(PyDateTime_DateTimeTZ *self)
{
	return new_date(GET_YEAR(self),
			GET_MONTH(self),
			GET_DAY(self));
}

static PyObject *
datetimetz_gettime(PyDateTime_DateTimeTZ *self)
{
	return new_time(DATE_GET_HOUR(self),
			DATE_GET_MINUTE(self),
			DATE_GET_SECOND(self),
			DATE_GET_MICROSECOND(self));
}

/* Pickle support.  Quite a maze! */

static PyObject *
datetimetz_getstate(PyDateTime_DateTimeTZ *self)
{
	return PyString_FromStringAndSize(self->data,
					  _PyDateTime_DATETIME_DATASIZE);
}

static PyObject *
datetimetz_setstate(PyDateTime_DateTimeTZ *self, PyObject *state)
{
	const int len = PyString_Size(state);
	unsigned char *pdata = (unsigned char*)PyString_AsString(state);

	if (! PyString_Check(state) ||
	    len != _PyDateTime_DATETIME_DATASIZE) {
		PyErr_SetString(PyExc_TypeError,
				"bad argument to datetime.__setstate__");
		return NULL;
	}
	memcpy(self->data, pdata, _PyDateTime_DATETIME_DATASIZE);
	self->hashcode = -1;

	Py_INCREF(Py_None);
	return Py_None;
}

/* XXX This seems a ridiculously inefficient way to pickle a short string. */
static PyObject *
datetimetz_pickler(PyObject *module, PyDateTime_DateTimeTZ *datetime)
{
	PyObject *state;
	PyObject *result = NULL;

	if (datetime->ob_type != &PyDateTime_DateTimeType) {
		PyErr_Format(PyExc_TypeError,
			     "bad type passed to datetime pickler: %s",
			     datetime->ob_type->tp_name);
		return NULL;
	}
	state = datetimetz_getstate(datetime);
	if (state) {
		result = Py_BuildValue("O(O)",
				       datetimetz_unpickler_object,
				       state);
		Py_DECREF(state);
	}
	return result;
}

static PyObject *
datetimetz_unpickler(PyObject *module, PyObject *arg)
{
	PyDateTime_DateTimeTZ *self;

	if (! PyString_CheckExact(arg)) {
		PyErr_Format(PyExc_TypeError,
			     "bad type passed to datetime unpickler: %s",
			     arg->ob_type->tp_name);
		return NULL;
	}
	self = PyObject_New(PyDateTime_DateTimeTZ, &PyDateTime_DateTimeType);
	if (self != NULL) {
		PyObject *res = datetimetz_setstate(self, arg);
		if (res == NULL) {
			Py_DECREF(self);
			return NULL;
		}
		Py_DECREF(res);
	}
	return (PyObject *)self;
}

static PyMethodDef datetimetz_methods[] = {
	/* Class methods: */
	{"now",         (PyCFunction)datetimetz_now,
	 METH_NOARGS | METH_CLASS,
	 PyDoc_STR("Return a new datetime representing local day and time.")},

	{"utcnow",         (PyCFunction)datetimetz_utcnow,
	 METH_NOARGS | METH_CLASS,
	 PyDoc_STR("Return a new datetime representing UTC day and time.")},

	{"fromtimestamp", (PyCFunction)datetimetz_fromtimestamp,
	 METH_VARARGS | METH_CLASS,
	 PyDoc_STR("timestamp -> local datetime from a POSIX timestamp "
	 	   "(like time.time()).")},

	{"utcfromtimestamp", (PyCFunction)datetimetz_utcfromtimestamp,
	 METH_VARARGS | METH_CLASS,
	 PyDoc_STR("timestamp -> UTC datetime from a POSIX timestamp "
	 	   "(like time.time()).")},

	{"combine", (PyCFunction)datetimetz_combine,
	 METH_VARARGS | METH_KEYWORDS | METH_CLASS,
	 PyDoc_STR("date, time -> datetime with same date and time fields")},

	/* Instance methods: */
	{"timetuple",   (PyCFunction)datetimetz_timetuple, METH_NOARGS,
         PyDoc_STR("Return time tuple, compatible with time.localtime().")},

	{"date",   (PyCFunction)datetimetz_getdate, METH_NOARGS,
         PyDoc_STR("Return date object with same year, month and day.")},

	{"time",   (PyCFunction)datetimetz_gettime, METH_NOARGS,
         PyDoc_STR("Return time object with same hour, minute, second and "
         	   "microsecond.")},

	{"ctime",       (PyCFunction)datetimetz_ctime,	METH_NOARGS,
	 PyDoc_STR("Return ctime() style string.")},

	{"isoformat",   (PyCFunction)datetimetz_isoformat, METH_KEYWORDS,
	 PyDoc_STR("[sep] -> string in ISO 8601 format, "
	 	   "YYYY-MM-DDTHH:MM:SS[.mmmmmm].\n\n"
	 	   "sep is used to separate the year from the time, and "
	 	   "defaults\n"
	 	   "to 'T'.")},

	{"__setstate__", (PyCFunction)datetimetz_setstate, METH_O,
	 PyDoc_STR("__setstate__(state)")},

	{"__getstate__", (PyCFunction)datetimetz_getstate, METH_NOARGS,
	 PyDoc_STR("__getstate__() -> state")},
	{NULL,	NULL}
};

static char datetimetz_doc[] =
PyDoc_STR("date/time type.");

static PyNumberMethods datetimetz_as_number = {
	datetimetz_add,				/* nb_add */
	datetimetz_subtract,			/* 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 */
	0,					/* nb_nonzero */
};

statichere PyTypeObject PyDateTime_DateTimeTZType = {
	PyObject_HEAD_INIT(NULL)
	0,					/* ob_size */
	/* XXX When this module is renamed to datetime, change tp_name. */
	"_datetime.datetimetz",			/* tp_name */
	sizeof(PyDateTime_DateTimeTZ),		/* tp_basicsize */
	0,					/* tp_itemsize */
	(destructor)PyObject_Del,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	(reprfunc)datetimetz_repr,		/* tp_repr */
	&datetimetz_as_number,			/* tp_as_number */
	0,					/* tp_as_sequence */
	0,					/* tp_as_mapping */
	(hashfunc)datetimetz_hash,		/* tp_hash */
	0,              			/* tp_call */
	(reprfunc)datetimetz_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 */
	datetimetz_doc,				/* tp_doc */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	(richcmpfunc)datetimetz_richcompare,	/* tp_richcompare */
	0,					/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	datetimetz_methods,			/* tp_methods */
	0,					/* tp_members */
	datetimetz_getset,			/* tp_getset */
	&PyDateTime_DateTimeType,		/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	0,					/* tp_init */
	0,					/* tp_alloc */
	datetimetz_new,				/* tp_new */
	_PyObject_Del,				/* tp_free */
};

Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.62
retrieving revision 1.63
diff -C2 -d -r1.62 -r1.63
*** datetime.c	12 Dec 2002 22:08:22 -0000	1.62
--- datetime.c	13 Dec 2002 01:13:46 -0000	1.63
***************
*** 70,73 ****
--- 70,74 ----
  static PyTypeObject PyDateTime_DateType;
  static PyTypeObject PyDateTime_DateTimeType;
+ static PyTypeObject PyDateTime_DateTimeTZType;
  static PyTypeObject PyDateTime_DeltaType;
  static PyTypeObject PyDateTime_TimeType;
***************
*** 404,434 ****
  }
  
- /* Fiddle days (d), seconds (s), and microseconds (us) so that
-  * 	0 <= *s < 24*3600
-  * 	0 <= *us < 1000000
-  * The input values must be such that the internals don't overflow.
-  * The way this routine is used, we don't get close.
-  */
- static void
- normalize_d_s_us(long *d, long *s, long *us)
- {
- 	if (*us < 0 || *us >= 1000000) {
- 		normalize_pair(s, us, 1000000);
- 		/* |s| can't be bigger than about
- 		 * |original s| + |original us|/1000000 now.
- 		 */
- 
- 	}
- 	if (*s < 0 || *s >= 24*3600) {
- 		normalize_pair(d, s, 24*3600);
- 		/* |d| can't be bigger than about
- 		 * |original d| +
- 		 * (|original s| + |original us|/1000000) / (24*3600) now.
- 		 */
- 	}
- 	assert(0 <= *s && *s < 24*3600);
- 	assert(0 <= *us && *us < 1000000);
- }
- 
  /* Fiddle years (y), months (m), and days (d) so that
   * 	1 <= *m <= 12
--- 405,408 ----
***************
*** 499,502 ****
--- 473,538 ----
  }
  
+ /* Fiddle out-of-bounds months and days so that the result makes some kind
+  * of sense.  The parameters are both inputs and outputs.  Returns < 0 on
+  * failure, where failure means the adjusted year is out of bounds.
+  */
+ static int
+ normalize_date(long *year, long *month, long *day)
+ {
+ 	int result;
+ 
+ 	normalize_y_m_d(year, month, day);
+ 	if (MINYEAR <= *year && *year <= MAXYEAR)
+ 		result = 0;
+ 	else {
+ 		PyErr_SetString(PyExc_OverflowError,
+ 				"date value out of range");
+ 		result = -1;
+ 	}
+ 	return result;
+ }
+ 
+ /* Force all the datetime fields into range.  The parameters are both
+  * inputs and outputs.  Returns < 0 on error.
+  */
+ static int
+ normalize_datetime(long *year, long *month, long *day,
+                    long *hour, long *minute, long *second,
+                    long *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);
+ }
+ 
+ /* Fiddle days (d), seconds (s), and microseconds (us) so that
+  * 	0 <= *s < 24*3600
+  * 	0 <= *us < 1000000
+  * The input values must be such that the internals don't overflow.
+  * The way this routine is used, we don't get close.
+  */
+ static void
+ normalize_d_s_us(long *d, long *s, long *us)
+ {
+ 	if (*us < 0 || *us >= 1000000) {
+ 		normalize_pair(s, us, 1000000);
+ 		/* |s| can't be bigger than about
+ 		 * |original s| + |original us|/1000000 now.
+ 		 */
+ 
+ 	}
+ 	if (*s < 0 || *s >= 24*3600) {
+ 		normalize_pair(d, s, 24*3600);
+ 		/* |d| can't be bigger than about
+ 		 * |original d| +
+ 		 * (|original s| + |original us|/1000000) / (24*3600) now.
+ 		 */
+ 	}
+ 	assert(0 <= *s && *s < 24*3600);
+ 	assert(0 <= *us && *us < 1000000);
+ }
+ 
  /* Wrap functions from the time module.  These aren't directly available
   * from C.  Perhaps they should be.
***************
*** 579,582 ****
--- 615,636 ----
  }
  
+ static void
+ set_date_fields(PyDateTime_Date *self, int y, int m, int d)
+ {
+ 	self->hashcode = -1;
+ 	SET_YEAR(self, y);
+ 	SET_MONTH(self, m);
+ 	SET_DAY(self, d);
+ }
+ 
+ static void
+ set_datetime_time_fields(PyDateTime_Date *self, int h, int m, int s, int us)
+ {
+ 	DATE_SET_HOUR(self, h);
+ 	DATE_SET_MINUTE(self, m);
+ 	DATE_SET_SECOND(self, s);
+ 	DATE_SET_MICROSECOND(self, us);
+ }
+ 
  /* Create a date instance with no range checking. */
  static PyObject *
***************
*** 586,595 ****
  
  	self = PyObject_New(PyDateTime_Date, &PyDateTime_DateType);
! 	if (self != NULL) {
! 		self->hashcode = -1;
! 		SET_YEAR(self, year);
! 		SET_MONTH(self, month);
! 		SET_DAY(self, day);
! 	}
  	return (PyObject *) self;
  }
--- 640,645 ----
  
  	self = PyObject_New(PyDateTime_Date, &PyDateTime_DateType);
! 	if (self != NULL)
! 		set_date_fields(self, year, month, day);
  	return (PyObject *) self;
  }
***************
*** 604,615 ****
  	self = PyObject_New(PyDateTime_DateTime, &PyDateTime_DateTimeType);
  	if (self != NULL) {
! 		self->hashcode = -1;
! 		SET_YEAR(self, year);
! 		SET_MONTH(self, month);
! 		SET_DAY(self, day);
! 		DATE_SET_HOUR(self, hour);
! 		DATE_SET_MINUTE(self, minute);
! 		DATE_SET_SECOND(self, second);
! 		DATE_SET_MICROSECOND(self, usecond);
  	}
  	return (PyObject *) self;
--- 654,678 ----
  	self = PyObject_New(PyDateTime_DateTime, &PyDateTime_DateTimeType);
  	if (self != NULL) {
! 		set_date_fields((PyDateTime_Date *)self, year, month, day);
! 		set_datetime_time_fields((PyDateTime_Date *)self,
! 					 hour, minute, second, usecond);
! 	}
! 	return (PyObject *) self;
! }
! 
! /* Create a datetimetz instance with no range checking. */
! static PyObject *
! new_datetimetz(int year, int month, int day, int hour, int minute,
!              int second, int usecond, PyObject *tzinfo)
! {
! 	PyDateTime_DateTimeTZ *self;
! 
! 	self = PyObject_New(PyDateTime_DateTimeTZ, &PyDateTime_DateTimeTZType);
! 	if (self != NULL) {
! 		set_date_fields((PyDateTime_Date *)self, year, month, day);
! 		set_datetime_time_fields((PyDateTime_Date *)self,
! 					 hour, minute, second, usecond);
! 		Py_INCREF(tzinfo);
! 		self->tzinfo = tzinfo;
  	}
  	return (PyObject *) self;
***************
*** 791,797 ****
  static PyObject *date_unpickler_object = NULL;
  static PyObject *datetime_unpickler_object = NULL;
  static PyObject *time_unpickler_object = NULL;
  static PyObject *timetz_unpickler_object = NULL;
- static PyObject *tzinfo_unpickler_object = NULL;
  
  #include "obj_delta.c"
--- 854,861 ----
  static PyObject *date_unpickler_object = NULL;
  static PyObject *datetime_unpickler_object = NULL;
+ static PyObject *datetimetz_unpickler_object = NULL;
+ static PyObject *tzinfo_unpickler_object = NULL;
  static PyObject *time_unpickler_object = NULL;
  static PyObject *timetz_unpickler_object = NULL;
  
  #include "obj_delta.c"
***************
*** 801,804 ****
--- 865,869 ----
  #include "obj_tzinfo.c"
  #include "obj_timetz.c"
+ #include "obj_datetimetz.c"
  
  
***************
*** 811,814 ****
--- 876,881 ----
  	{"_datetime_pickler",	(PyCFunction)datetime_pickler,	METH_O, NULL},
  	{"_datetime_unpickler",	(PyCFunction)datetime_unpickler,METH_O, NULL},
+ 	{"_datetimetz_pickler",	(PyCFunction)datetimetz_pickler,METH_O, NULL},
+ 	{"_datetimetz_unpickler",(PyCFunction)datetimetz_unpickler,METH_O, NULL},
  	{"_time_pickler",	(PyCFunction)time_pickler,	METH_O, NULL},
  	{"_time_unpickler",	(PyCFunction)time_unpickler,	METH_O, NULL},
***************
*** 850,853 ****
--- 917,922 ----
  	if (PyType_Ready(&PyDateTime_TimeTZType) < 0)
  		return;
+ 	if (PyType_Ready(&PyDateTime_DateTimeTZType) < 0)
+ 		return;
  
  	/* Pickling support, via registering functions with copy_reg. */
***************
*** 857,924 ****
  		PyObject *copyreg = PyImport_ImportModule("copy_reg");
  
! 		assert(copyreg);
  
  		pickler = PyObject_GetAttrString(m, "_date_pickler");
! 		assert(pickler);
  		date_unpickler_object = PyObject_GetAttrString(m,
  						"_date_unpickler");
! 		assert(date_unpickler_object);
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_DateType,
  	    				   pickler,
  		                           date_unpickler_object);
! 		assert(temp);
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_datetime_pickler");
! 		assert(pickler);
  		datetime_unpickler_object = PyObject_GetAttrString(m,
  						"_datetime_unpickler");
! 		assert(datetime_unpickler_object);
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_DateTimeType,
  	    				   pickler,
  		                           datetime_unpickler_object);
! 		assert(temp);
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_time_pickler");
! 		assert(pickler);
  		time_unpickler_object = PyObject_GetAttrString(m,
  						"_time_unpickler");
! 		assert(time_unpickler_object);
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TimeType,
  	    				   pickler,
  		                           time_unpickler_object);
! 		assert(temp);
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_timetz_pickler");
! 		assert(pickler);
  		timetz_unpickler_object = PyObject_GetAttrString(m,
  						"_timetz_unpickler");
! 		assert(timetz_unpickler_object);
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TimeTZType,
  	    				   pickler,
  		                           timetz_unpickler_object);
! 		assert(temp);
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_tzinfo_pickler");
! 		assert(pickler);
  		tzinfo_unpickler_object = PyObject_GetAttrString(m,
  							"_tzinfo_unpickler");
! 		assert(tzinfo_unpickler_object);
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TZInfoType,
  	    				   pickler,
  		                           tzinfo_unpickler_object);
! 		assert(temp);
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
--- 926,1006 ----
  		PyObject *copyreg = PyImport_ImportModule("copy_reg");
  
! 		if (copyreg == NULL) return;
  
  		pickler = PyObject_GetAttrString(m, "_date_pickler");
! 		if (pickler == NULL) return;
  		date_unpickler_object = PyObject_GetAttrString(m,
  						"_date_unpickler");
! 		if (date_unpickler_object == NULL) return;
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_DateType,
  	    				   pickler,
  		                           date_unpickler_object);
! 		if (temp == NULL) return;
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_datetime_pickler");
! 		if (pickler == NULL) return;
  		datetime_unpickler_object = PyObject_GetAttrString(m,
  						"_datetime_unpickler");
! 		if (datetime_unpickler_object == NULL) return;
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_DateTimeType,
  	    				   pickler,
  		                           datetime_unpickler_object);
! 		if (temp == NULL) return;
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_time_pickler");
! 		if (pickler == NULL) return;
  		time_unpickler_object = PyObject_GetAttrString(m,
  						"_time_unpickler");
! 		if (time_unpickler_object == NULL) return;
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TimeType,
  	    				   pickler,
  		                           time_unpickler_object);
! 		if (temp == NULL) return;
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_timetz_pickler");
! 		if (pickler == NULL) return;
  		timetz_unpickler_object = PyObject_GetAttrString(m,
  						"_timetz_unpickler");
! 		if (timetz_unpickler_object == NULL) return;
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TimeTZType,
  	    				   pickler,
  		                           timetz_unpickler_object);
! 		if (temp == NULL) return;
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
  
  		pickler = PyObject_GetAttrString(m, "_tzinfo_pickler");
! 		if (pickler == NULL) return;
  		tzinfo_unpickler_object = PyObject_GetAttrString(m,
  							"_tzinfo_unpickler");
! 		if (tzinfo_unpickler_object == NULL) return;
  	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
  	    				   &PyDateTime_TZInfoType,
  	    				   pickler,
  		                           tzinfo_unpickler_object);
! 		if (temp == NULL) return;
! 		Py_DECREF(temp);
! 		Py_DECREF(pickler);
! 
! 		pickler = PyObject_GetAttrString(m, "_datetimetz_pickler");
! 		if (pickler == NULL) return;
! 		datetimetz_unpickler_object = PyObject_GetAttrString(m,
! 						 "_datetimetz_unpickler");
! 		if (datetimetz_unpickler_object == NULL) return;
! 	    	temp = PyObject_CallMethod(copyreg, "pickle", "OOO",
! 	    				   &PyDateTime_DateTimeTZType,
! 	    				   pickler,
! 		                           datetimetz_unpickler_object);
! 		if (temp == NULL) return;
  		Py_DECREF(temp);
  		Py_DECREF(pickler);
***************
*** 1011,1014 ****
--- 1093,1114 ----
  
  	x = new_timetz(23, 59, 59, 999999, Py_None);
+ 	if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
+ 		return;
+ 	Py_DECREF(x);
+ 
+ 	x = new_delta(0, 0, 1, 0);
+ 	if (x == NULL || PyDict_SetItemString(d, "resolution", x) < 0)
+ 		return;
+ 	Py_DECREF(x);
+ 
+ 	/* datetimetz values */
+ 	d = PyDateTime_DateTimeTZType.tp_dict;
+ 
+ 	x = new_datetimetz(1, 1, 1, 0, 0, 0, 0, Py_None);
+ 	if (x == NULL || PyDict_SetItemString(d, "min", x) < 0)
+ 		return;
+ 	Py_DECREF(x);
+ 
+ 	x = new_datetimetz(MAXYEAR, 12, 31, 23, 59, 59, 999999, Py_None);
  	if (x == NULL || PyDict_SetItemString(d, "max", x) < 0)
  		return;

Index: datetime.h
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.h,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** datetime.h	11 Dec 2002 18:54:10 -0000	1.15
--- datetime.h	13 Dec 2002 01:13:46 -0000	1.16
***************
*** 16,21 ****
   *  6 		second   1 byte, 0-59
   *  7 		usecond  3 bytes, 0-999999
!  * 10  		tzoffset 2 bytes, signed, in minutes, -1439 .. 1439
!  * 12
   */
  
--- 16,20 ----
   *  6 		second   1 byte, 0-59
   *  7 		usecond  3 bytes, 0-999999
!  * 10
   */
  
***************
*** 47,50 ****
--- 46,57 ----
  	PyObject_HEAD
  	long hashcode;
+ 	unsigned char data[_PyDateTime_DATETIME_DATASIZE];
+ 	PyObject *tzinfo;
+ } PyDateTime_DateTimeTZ;
+ 
+ typedef struct
+ {
+ 	PyObject_HEAD
+ 	long hashcode;
  	unsigned char data[_PyDateTime_TIME_DATASIZE];
  } PyDateTime_Time;
***************
*** 72,76 ****
  } PyDateTime_TZInfo;
  
! /* Apply for date instances. */
  #define PyDateTime_GET_YEAR(o)     ((((PyDateTime_Date*)o)->data[0] << 8) | \
                                       ((PyDateTime_Date*)o)->data[1])
--- 79,83 ----
  } PyDateTime_TZInfo;
  
! /* Apply for date, datetime, and datetimetz instances. */
  #define PyDateTime_GET_YEAR(o)     ((((PyDateTime_Date*)o)->data[0] << 8) | \
                                       ((PyDateTime_Date*)o)->data[1])
***************
*** 78,82 ****
  #define PyDateTime_GET_DAY(o)      (((PyDateTime_Date*)o)->data[3])
  
- /* Apply for datetime and date instances. */
  #define PyDateTime_DATE_GET_HOUR(o)        (((PyDateTime_DateTime*)o)->data[4])
  #define PyDateTime_DATE_GET_MINUTE(o)      (((PyDateTime_DateTime*)o)->data[5])
--- 85,88 ----

Index: obj_date.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_date.c,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** obj_date.c	12 Dec 2002 18:48:01 -0000	1.48
--- obj_date.c	13 Dec 2002 01:13:46 -0000	1.49
***************
*** 149,173 ****
  }
  
! /* date arithmetic. */
! 
! /* Fiddle out-of-bounds months and days so that the result makes some kind
!  * of sense.  The parameters are both inputs and outputs.  Returns < 0 on
!  * failure, where failure means the adjusted year is out of bounds.
   */
- static int
- normalize_date(long *year, long *month, long *day)
- {
- 	int result;
- 
- 	normalize_y_m_d(year, month, day);
- 	if (MINYEAR <= *year && *year <= MAXYEAR)
- 		result = 0;
- 	else {
- 		PyErr_SetString(PyExc_OverflowError,
- 				"date value out of range");
- 		result = -1;
- 	}
- 	return result;
- }
  
  /* date + timedelta -> date.  If arg negate is true, subtract the timedelta
--- 149,155 ----
  }
  
! /*
!  * Date arithmetic.
   */
  
  /* date + timedelta -> date.  If arg negate is true, subtract the timedelta

Index: obj_datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_datetime.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -C2 -d -r1.46 -r1.47
*** obj_datetime.c	12 Dec 2002 18:48:01 -0000	1.46
--- obj_datetime.c	13 Dec 2002 01:13:46 -0000	1.47
***************
*** 253,271 ****
  /* datetime arithmetic. */
  
- /* Force all the datetime fields into range.  The parameters are both
-  * inputs and outputs.  Returns < 0 on error.
-  */
- static int
- normalize_datetime(long *year, long *month, long *day,
-                    long *hour, long *minute, long *second,
-                    long *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);
- }
- 
  static PyObject *
  add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta)
--- 253,256 ----