[Python-checkins] python/nondist/sandbox/datetime datetime.c,1.46,1.47 datetime.h,1.11,1.12 datetime.py,1.78,1.79 doc.txt,1.23,1.24 obj_date.c,1.36,1.37 obj_datetime.c,1.31,1.32 obj_time.c,1.1,1.2 test_both.py,1.48,1.49 test_datetime.py,1.55,1.56

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Sat, 07 Dec 2002 10:34:41 -0800


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

Modified Files:
	datetime.c datetime.h datetime.py doc.txt obj_date.c 
	obj_datetime.c obj_time.c test_both.py test_datetime.py 
Log Message:
Mounds of changes to implement the time type in C.  Moved all the time
tests from test_datetime.py into test_both.py.  More tests are needed
(e.g., for pickling of time objects -- this is implemented but not yet
tested).

The Python implementation had a fancy "pretty" __str__ for time and timetz.
I don't know whether or not that's a good idea, as people argue endlessly
over what "pretty" means.  Since all the other types in this module with
an isoformat() method made __str__ a synonym, that's what I did here too,
and changed the Python implementation to match.  I'm not married to this,
I just don't want to endure arguments about this now.

Pretty str() for all types or pretty str() for none is what I want to see,
the latter is quickest to implement, and if pretty is desired that's not
critical to get done before tha alpha release.


Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -C2 -d -r1.46 -r1.47
*** datetime.c	7 Dec 2002 16:36:16 -0000	1.46
--- datetime.c	7 Dec 2002 18:34:39 -0000	1.47
***************
*** 46,56 ****
  
  /* Time accessors for time. */
  #define TIME_SET_HOUR(o, v)	(PyDateTime_TIME_GET_HOUR(o) = (v))
  #define TIME_SET_MINUTE(o, v)	(PyDateTime_TIME_GET_MINUTE(o) = (v))
  #define TIME_SET_SECOND(o, v)	(PyDateTime_TIME_GET_SECOND(o) = (v))
  #define TIME_SET_MICROSECOND(o, v)	\
! 	(((o)->data[7] = ((v) & 0xff0000) >> 16), \
!          ((o)->data[8] = ((v) & 0x00ff00) >> 8), \
!          ((o)->data[9] = ((v) & 0x0000ff)))
  
  /* Delta accessors for timedelta. */
--- 46,60 ----
  
  /* Time accessors for time. */
+ #define TIME_GET_HOUR		PyDateTime_TIME_GET_HOUR
+ #define TIME_GET_MINUTE		PyDateTime_TIME_GET_MINUTE
+ #define TIME_GET_SECOND		PyDateTime_TIME_GET_SECOND
+ #define TIME_GET_MICROSECOND	PyDateTime_TIME_GET_MICROSECOND
  #define TIME_SET_HOUR(o, v)	(PyDateTime_TIME_GET_HOUR(o) = (v))
  #define TIME_SET_MINUTE(o, v)	(PyDateTime_TIME_GET_MINUTE(o) = (v))
  #define TIME_SET_SECOND(o, v)	(PyDateTime_TIME_GET_SECOND(o) = (v))
  #define TIME_SET_MICROSECOND(o, v)	\
! 	(((o)->data[3] = ((v) & 0xff0000) >> 16), \
!          ((o)->data[4] = ((v) & 0x00ff00) >> 8), \
!          ((o)->data[5] = ((v) & 0x0000ff)))
  
  /* Delta accessors for timedelta. */
***************
*** 67,70 ****
--- 71,75 ----
  static PyTypeObject PyDateTime_DateTimeType;
  static PyTypeObject PyDateTime_DeltaType;
+ static PyTypeObject PyDateTime_TimeType;
  
  /*
***************
*** 538,541 ****
--- 543,563 ----
  }
  
+ /* Create a time instance with no range checking. */
+ static PyObject *
+ new_time(int hour, int minute, int second, int usecond)
+ {
+ 	PyDateTime_Time *self;
+ 
+ 	self = PyObject_New(PyDateTime_Time, &PyDateTime_TimeType);
+ 	if (self != NULL) {
+ 		self->hashcode = -1;
+ 		TIME_SET_HOUR(self, hour);
+ 		TIME_SET_MINUTE(self, minute);
+ 		TIME_SET_SECOND(self, second);
+ 		TIME_SET_MICROSECOND(self, usecond);
+ 	}
+ 	return (PyObject *) self;
+ }
+ 
  /* Create a timedelta instance.  Normalize the members iff normalize is
   * true.  Passing false is a speed optimization, if you know for sure
***************
*** 569,572 ****
--- 591,595 ----
  static PyObject *date_unpickler_object = NULL;
  static PyObject *datetime_unpickler_object = NULL;
+ static PyObject *time_unpickler_object = NULL;
  
  /* For obscure reasons, we need to use tp_richcompare instead of tp_compare.
***************
*** 598,601 ****
--- 621,625 ----
  #include "obj_date.c"
  #include "obj_datetime.c"
+ #include "obj_time.c"
  
  
***************
*** 608,611 ****
--- 632,637 ----
  	{"_datetime_pickler",	(PyCFunction)datetime_pickler,	METH_O, NULL},
  	{"_datetime_unpickler",	(PyCFunction)datetime_unpickler,METH_O, NULL},
+ 	{"_time_pickler",	(PyCFunction)time_pickler,	METH_O, NULL},
+ 	{"_time_unpickler",	(PyCFunction)time_unpickler,	METH_O, NULL},
  	{NULL, NULL}
  };
***************
*** 630,633 ****
--- 656,661 ----
  	if (PyType_Ready(&PyDateTime_DeltaType) < 0)
  		return;
+ 	if (PyType_Ready(&PyDateTime_TimeType) < 0)
+ 		return;
  
  	/* timedelta values */
***************
*** 688,691 ****
--- 716,737 ----
  	Py_DECREF(dt);
  
+ 	/* time values */
+ 	d = PyDateTime_TimeType.tp_dict;
+ 
+ 	dt = new_time(0, 0, 0, 0);
+ 	if (dt == NULL || PyDict_SetItemString(d, "min", dt) < 0)
+ 		return;
+ 	Py_DECREF(dt);
+ 
+ 	dt = new_time(23, 59, 59, 999999);
+ 	if (dt == NULL || PyDict_SetItemString(d, "max", dt) < 0)
+ 		return;
+ 	Py_DECREF(dt);
+ 
+ 	dt = new_delta(0, 0, 1, 0);
+ 	if (dt == NULL || PyDict_SetItemString(d, "resolution", dt) < 0)
+ 		return;
+ 	Py_DECREF(dt);
+ 
  	Py_DECREF(safepickle);
  
***************
*** 695,707 ****
  	PyModule_AddIntConstant(m, "MINYEAR", MINYEAR);
  	PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR);
  	Py_INCREF(&PyDateTime_DateType);
! 	PyModule_AddObject(m, "date",
! 		           (PyObject *) &PyDateTime_DateType);
  	Py_INCREF(&PyDateTime_DateTimeType);
  	PyModule_AddObject(m, "datetime",
  			   (PyObject *) &PyDateTime_DateTimeType);
  	Py_INCREF(&PyDateTime_DeltaType);
! 	PyModule_AddObject(m, "timedelta",
! 			   (PyObject *) &PyDateTime_DeltaType);
  
  
--- 741,757 ----
  	PyModule_AddIntConstant(m, "MINYEAR", MINYEAR);
  	PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR);
+ 
  	Py_INCREF(&PyDateTime_DateType);
! 	PyModule_AddObject(m, "date", (PyObject *) &PyDateTime_DateType);
! 
  	Py_INCREF(&PyDateTime_DateTimeType);
  	PyModule_AddObject(m, "datetime",
  			   (PyObject *) &PyDateTime_DateTimeType);
+ 
  	Py_INCREF(&PyDateTime_DeltaType);
! 	PyModule_AddObject(m, "timedelta", (PyObject *) &PyDateTime_DeltaType);
! 
! 	Py_INCREF(&PyDateTime_TimeType);
! 	PyModule_AddObject(m, "time", (PyObject *) &PyDateTime_TimeType);
  
  
***************
*** 767,770 ****
--- 817,833 ----
  	    				   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);

Index: datetime.h
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -d -r1.11 -r1.12
*** datetime.h	7 Dec 2002 16:36:16 -0000	1.11
--- datetime.h	7 Dec 2002 18:34:39 -0000	1.12
***************
*** 21,31 ****
  
  /* # of bytes for year, month, and day. */
! #define _PyDateTime_DATE_DATA_SIZE 4
! 
! /* # of bytes for year, month, day, hour, minute, second, and usecond. */
! #define _PyDateTime_DATETIME_DATA_SIZE 10
  
  /* # of bytes for hour, minute, second, and usecond. */
! #define _PyDateTime_TIME_DATA_SIZE 6
  
  typedef struct
--- 21,31 ----
  
  /* # of bytes for year, month, and day. */
! #define _PyDateTime_DATE_DATASIZE 4
  
  /* # of bytes for hour, minute, second, and usecond. */
! #define _PyDateTime_TIME_DATASIZE 6
! 
! /* # of bytes for year, month, day, hour, minute, second, and usecond. */
! #define _PyDateTime_DATETIME_DATASIZE 10
  
  typedef struct
***************
*** 33,38 ****
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_DATE_DATA_SIZE];
! }  PyDateTime_Date;
  
  typedef struct
--- 33,38 ----
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_DATE_DATASIZE];
! } PyDateTime_Date;
  
  typedef struct
***************
*** 40,45 ****
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_DATETIME_DATA_SIZE];
! }  PyDateTime_DateTime;
  
  typedef struct
--- 40,45 ----
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_DATETIME_DATASIZE];
! } PyDateTime_DateTime;
  
  typedef struct
***************
*** 47,52 ****
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_TIME_DATA_SIZE];
! }  PyDateTime_Time;
  
  typedef struct
--- 47,52 ----
  	PyObject_HEAD
  	long hashcode;
! 	unsigned char data[_PyDateTime_TIME_DATASIZE];
! } PyDateTime_Time;
  
  typedef struct

Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.78
retrieving revision 1.79
diff -C2 -d -r1.78 -r1.79
*** datetime.py	5 Dec 2002 19:45:03 -0000	1.78
--- datetime.py	7 Dec 2002 18:34:39 -0000	1.79
***************
*** 789,792 ****
--- 789,805 ----
                                   self.__hour, self.__minute, s)
  
+     def isoformat(self):
+         """Return the time formatted according to ISO.
+ 
+         This is 'HH:MM:SS.mmmmmm'.
+         """
+         return "%02d:%02d:%02d.%06d" % (
+             self.__hour, self.__minute, self.__second,
+             self.__microsecond)
+ 
+     # XXX All other types in this module that define isoformat make __str__
+     # XXX a synonym, so for now I'm overriding this "pretty string" oddball
+     # XXX to do likewise.  The last thing I need right now is to spend days
+     # XXX arguing about what "pretty" means in 6 distinct types <0.5 wink>.
      def __str__(self):
          """Convert to pretty string, for str()."""
***************
*** 805,816 ****
          return pretty
  
!     def isoformat(self):
!         """Return the time formatted according to ISO.
! 
!         This is 'HH:MM:SS.mmmmmm'.
!         """
!         return "%02d:%02d:%02d.%06d" % (
!             self.__hour, self.__minute, self.__second,
!             self.__microsecond)
  
      def strftime(self, fmt):
--- 818,822 ----
          return pretty
  
!     __str__ = isoformat
  
      def strftime(self, fmt):
***************
*** 937,947 ****
          return s
  
-     def __str__(self):
-         """Convert to pretty string, for str()."""
-         s = super(timetz, self).__str__()
-         tz = self._tzstr()
-         if tz: s = "%s %s" % (s, tz)
-         return s
- 
      def isoformat(self):
          """Return the time formatted according to ISO.
--- 943,946 ----
***************
*** 953,956 ****
--- 952,965 ----
          if tz: s += tz
          return s
+ 
+     # XXX As for time, I'm making __str__ a synonym for isoformat for now.
+     def __str__(self):
+         """Convert to pretty string, for str()."""
+         s = super(timetz, self).__str__()
+         tz = self._tzstr()
+         if tz: s = "%s %s" % (s, tz)
+         return s
+ 
+     __str__ = isoformat
  
      def strftime(self, fmt):

Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.23
retrieving revision 1.24
diff -C2 -d -r1.23 -r1.24
*** doc.txt	7 Dec 2002 05:33:44 -0000	1.23
--- doc.txt	7 Dec 2002 18:34:39 -0000	1.24
***************
*** 503,506 ****
--- 503,567 ----
  class time
  ==========
+ A time object represents an idealized time of day, independent of day
+ and timezone.  
+ 
+ Constructor:
+ 
+     time(hour=0, minute=0, second=0, microsecond=0)
+ 
+     All arguments are optional.  The may be ints or longs, in the
+     following ranges:
+ 
+         0 <= hour < 24
+         0 <= minute < 60
+         0 <= second < 60
+         0 <= microsecond < 1000000
+ 
+     If an argument outside those ranges is given, ValueError is raised.
+ 
+ Other constructors (class methods):
+ 
+     None
+ 
+ Class attributes:
+ 
+     .min
+         The earliest representable time, time(0, 0, 0).
+ 
+     .max
+         The latest representable time, time(23, 59, 59).
+ 
+     .resolution
+         The smallest possible difference between non-equal time
+         objects, timedelta(microseconds=1).
+ 
+ Instance attributes (read-only):
+ 
+     .hour           in range(24)
+     .minute         in range(60)
+     .second         in range(60)
+     .microsecond    in range(1000000)
+ 
+ Supported operations:
+ 
+     - comparison of time to time, where time1 is considered
+       less than time2 when time1 precedes time2 in time.
+ 
+     - hash, use as dict key
+ 
+     - pickling
+ 
+ Instance methods:
+ 
+   - isoformat()
+     Return a string representing the time in ISO 8601 format,
+         HH:MM:SS.mmmmmm
+     str(t) is equivalent to t.isoformat().
+ 
+   - strftime(format)
+     Return a string representing the time, controlled by an explicit
+     format string.  Format codes for any fields other than hour, minute
+     and second should not be used, since a time object has meaningful
+     values only for those fields.
  
  

Index: obj_date.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_date.c,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** obj_date.c	7 Dec 2002 03:12:57 -0000	1.36
--- obj_date.c	7 Dec 2002 18:34:39 -0000	1.37
***************
*** 365,369 ****
  	}
  	diff = memcmp(self->data, ((PyDateTime_Date *)other)->data,
! 		      _PyDateTime_DATE_DATA_SIZE);
  	return diff_to_bool(diff, op);
  }
--- 365,369 ----
  	}
  	diff = memcmp(self->data, ((PyDateTime_Date *)other)->data,
! 		      _PyDateTime_DATE_DATASIZE);
  	return diff_to_bool(diff, op);
  }
***************
*** 426,430 ****
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_DATE_DATA_SIZE);
  }
  
--- 426,430 ----
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_DATE_DATASIZE);
  }
  
***************
*** 436,445 ****
  
  	if (! PyString_Check(state) ||
! 	    len != _PyDateTime_DATE_DATA_SIZE) {
  		PyErr_SetString(PyExc_TypeError,
  				"bad argument to date.__setstate__");
  		return NULL;
  	}
! 	memcpy(self->data, pdata, _PyDateTime_DATE_DATA_SIZE);
  	self->hashcode = -1;
  
--- 436,445 ----
  
  	if (! PyString_Check(state) ||
! 	    len != _PyDateTime_DATE_DATASIZE) {
  		PyErr_SetString(PyExc_TypeError,
  				"bad argument to date.__setstate__");
  		return NULL;
  	}
! 	memcpy(self->data, pdata, _PyDateTime_DATE_DATASIZE);
  	self->hashcode = -1;
  

Index: obj_datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_datetime.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -C2 -d -r1.31 -r1.32
*** obj_datetime.c	7 Dec 2002 16:36:16 -0000	1.31
--- obj_datetime.c	7 Dec 2002 18:34:39 -0000	1.32
***************
*** 446,450 ****
  	}
  	diff = memcmp(self->data, ((PyDateTime_DateTime *)other)->data,
! 		      _PyDateTime_DATETIME_DATA_SIZE);
  	return diff_to_bool(diff, op);
  }
--- 446,450 ----
  	}
  	diff = memcmp(self->data, ((PyDateTime_DateTime *)other)->data,
! 		      _PyDateTime_DATETIME_DATASIZE);
  	return diff_to_bool(diff, op);
  }
***************
*** 499,503 ****
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_DATETIME_DATA_SIZE);
  }
  
--- 499,503 ----
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_DATETIME_DATASIZE);
  }
  
***************
*** 509,518 ****
  
  	if (! PyString_Check(state) ||
! 	    len != _PyDateTime_DATETIME_DATA_SIZE) {
  		PyErr_SetString(PyExc_TypeError,
  				"bad argument to datetime.__setstate__");
  		return NULL;
  	}
! 	memcpy(self->data, pdata, _PyDateTime_DATETIME_DATA_SIZE);
  	self->hashcode = -1;
  
--- 509,518 ----
  
  	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;
  

Index: obj_time.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_time.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** obj_time.c	7 Dec 2002 16:36:16 -0000	1.1
--- obj_time.c	7 Dec 2002 18:34:39 -0000	1.2
***************
*** 1,5 ****
  /*
   * PyDateTime_Time implementation.
-  * XXX This is currently a copy of obj_datetime.c, and unused.
   */
  
--- 1,4 ----
***************
*** 7,38 ****
  
  static PyObject *
! datetime_hour(PyDateTime_DateTime *self, void *unused)
  {
! 	return PyInt_FromLong(DATE_GET_HOUR(self));
  }
  
  static PyObject *
! datetime_minute(PyDateTime_DateTime *self, void *unused)
  {
! 	return PyInt_FromLong(DATE_GET_MINUTE(self));
  }
  
  static PyObject *
! datetime_second(PyDateTime_DateTime *self, void *unused)
  {
! 	return PyInt_FromLong(DATE_GET_SECOND(self));
  }
  
  static PyObject *
! datetime_microsecond(PyDateTime_DateTime *self, void *unused)
  {
! 	return PyInt_FromLong(DATE_GET_MICROSECOND(self));
  }
  
! static PyGetSetDef datetime_getset[] = {
! 	{"hour",        (getter)datetime_hour},
! 	{"minute",      (getter)datetime_minute},
! 	{"second",      (getter)datetime_second},
! 	{"microsecond", (getter)datetime_microsecond},
  	{NULL}
  };
--- 6,37 ----
  
  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 *
! 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)time_second},
! 	{"microsecond", (getter)time_microsecond},
  	{NULL}
  };
***************
*** 41,50 ****
  
  static PyObject *
! datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
  {
  	PyObject *self = NULL;
- 	long year;
- 	long month;
- 	long day;
  	long hour = 0;
  	long minute = 0;
--- 40,46 ----
  
  static PyObject *
! time_new(PyTypeObject *type, PyObject *args, PyObject *kw)
  {
  	PyObject *self = NULL;
  	long hour = 0;
  	long minute = 0;
***************
*** 53,78 ****
  
  	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,
--- 49,57 ----
  
  	static char *keywords[] = {
! 		"hour", "minute", "second", "microsecond", NULL
  	};
  
! 	if (PyArg_ParseTupleAndKeywords(args, kw, "|llll", keywords,
! 					&hour, &minute, &second, &usecond)) {
  		if (hour < 0 || hour > 23) {
  			PyErr_SetString(PyExc_ValueError,
***************
*** 95,430 ****
  			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);
! 
! static PyObject *
! datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp)
! {
! 	struct tm *tm;
! 	time_t timet = (time_t)timestamp;
! 	long us = (long)((timestamp - (double)timet) * 1e6);
! 	PyObject *result = NULL;
! 
! 	tm = f(&timet);
! 	if (tm)
! 		result = PyObject_CallFunction(cls, "lllllll",
! 					       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;
! }
! 
! /* Return new local datetime from timestamp (Python timestamp -- a double). */
! static PyObject *
! datetime_fromtimestamp(PyObject *self, PyObject *args)
! {
! 	PyObject *cls;
! 	double timestamp;
! 	PyObject *result = NULL;
! 
! 	if (PyArg_ParseTuple(args, "Od:fromtimestamp", &cls, &timestamp))
! 		result = datetime_from_timestamp(cls, localtime, timestamp);
! 	return result;
! }
! 
! /* Return new UTC datetime from timestamp (Python timestamp -- a double). */
! static PyObject *
! datetime_utcfromtimestamp(PyObject *self, PyObject *args)
! {
! 	PyObject *cls;
! 	double timestamp;
! 	PyObject *result = NULL;
! 
! 	if (PyArg_ParseTuple(args, "Od:utcfromtimestamp", &cls, &timestamp))
! 		result = datetime_from_timestamp(cls, gmtime, timestamp);
! 	return result;
! }
! 
! static PyObject *
! datetime_now(PyObject *self, PyObject *cls)
! {
! 	/* XXX MAJOR:  This needs to get some notion of current time
! 	 * XXX to better than 1-second resolution.  Doing this in a x-
! 	 * XXX platform way is mondo painful.  Maybe floattime() from
! 	 * XXX timemodule.c is good enough?  We're running out of bits
! 	 * XXX in a typical time_t, though, and gettimeofday() should do
! 	 * XXX better than floattime() -- but gettimeofday isn't portable.
! 	 */
! #if 0
! 	/* XXX need to create the instance by calling cls(y,mon,d,h,min,s,u) */
! 	struct timeval t;
! 	struct tm *tm;
! 	time_t timet;
! 
! #ifdef GETTIMEOFDAY_NO_TZ
! 	gettimeofday(&t);
! #else /* !GETTIMEOFDAY_NO_TZ */
! 	gettimeofday(&t, (struct timezone *)NULL);
! #endif /* !GETTIMEOFDAY_NO_TZ */
! 	timet = t.tv_sec;
! 	tm = localtime(&timet);
! 
! 	return PyObject_CallFunction(cls, "iiiiiil",
! 				     tm->tm_year + 1900, tm->tm_mon + 1,
! 				     tm->tm_mday, tm->tm_hour, tm->tm_min,
! 				     tm->tm_sec, t.tv_usec);
! #else
! 	/* XXX need to create the instance by calling cls(y,mon,d,h,min,s,u) */
! 	struct tm *tm;
! 	time_t timet;
! 
!  	time(&timet);
! 	tm = localtime(&timet);
! 
! 	return PyObject_CallFunction(cls, "llllll",
! 				     tm->tm_year + 1900, tm->tm_mon + 1,
! 				     tm->tm_mday, tm->tm_hour, tm->tm_min,
! 				     tm->tm_sec);
! #endif
! }
! 
! static PyObject *
! datetime_utcnow(PyObject *self, PyObject *cls)
! {
! 	/* XXX Like datetime_now, this would like to do better than
! 	 * XXX 1-second resolution.
! 	 */
! 	struct tm *tm;
! 	time_t timet;
! 
!  	time(&timet);
! 	tm = gmtime(&timet);
! 
! 	return PyObject_CallFunction(cls, "llllll",
! 				     tm->tm_year + 1900, tm->tm_mon + 1,
! 				     tm->tm_mday, tm->tm_hour, tm->tm_min,
! 				     tm->tm_sec);
! }
! 
! /* 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)
! {
! 	/* 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_datetime_timedelta(PyDateTime_DateTime *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_datetime_datetime(PyDateTime_DateTime *left, PyDateTime_DateTime *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 *
! datetime_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_datetime_timedelta(
! 					(PyDateTime_DateTime *)left,
! 					(PyDateTime_Delta *)right);
! 	}
! 	else if (PyType_IsSubtype(left_type, &PyDateTime_DeltaType)) {
! 		/* delta + datetime */
! 		return add_datetime_timedelta((PyDateTime_DateTime *) right,
! 					      (PyDateTime_Delta *) left);
! 	}
! 	Py_INCREF(Py_NotImplemented);
! 	return Py_NotImplemented;
! }
! 
! static PyObject *
! datetime_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_datetime_datetime(
! 					(PyDateTime_DateTime *)left,
! 					(PyDateTime_DateTime *)right);
! 		}
! 		else if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
! 			/* datetime - delta */
! 			result = sub_datetime_timedelta(
! 					(PyDateTime_DateTime *)left,
! 					(PyDateTime_Delta *)right);
! 		}
! 	}
! 
! 	if (result == Py_NotImplemented)
! 		Py_INCREF(result);
! 	return result;
! }
! 
! /* Various ways to turn a datetime into a string. */
  
  static PyObject *
! datetime_repr(PyDateTime_DateTime *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 *
! datetime_isoformat_helper(PyDateTime_DateTime *self, char sep)
  {
  	char buffer[100];
! 	char *cp;
  
! 	cp = isoformat_date((PyDateTime_Date *)self, buffer, sizeof(buffer));
! 	assert(cp != NULL);
! 	*cp++ = sep;
! 	isoformat_time(self, cp, sizeof(buffer) - (cp - buffer));
  
  	return PyString_FromString(buffer);
  }
  
  static PyObject *
! datetime_str(PyDateTime_DateTime *self)
! {
! 	return datetime_isoformat_helper(self, ' ');
! }
! 
! static PyObject *
! datetime_isoformat(PyDateTime_DateTime *self,
!                    PyObject *args, PyObject *kw)
  {
! 	char sep = 'T';
! 	static char *keywords[] = {"sep", NULL};
! 
! 	if (!PyArg_ParseTupleAndKeywords(args, kw, "|c:isoformat", keywords,
! 					 &sep))
  		return NULL;
! 	return datetime_isoformat_helper(self, sep);
! }
! 
! static PyObject *
! datetime_ctime(PyDateTime_DateTime *self)
! {
! 	return format_ctime((PyDateTime_Date *)self,
! 			    DATE_GET_HOUR(self),
! 			    DATE_GET_MINUTE(self),
! 			    DATE_GET_SECOND(self));
  }
  
--- 74,140 ----
  			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);
  }
  
+ /* time_isoformat is also tp_str */
  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 *format)
  {
! 	PyObject *result;
! 	PyObject *tuple = Py_BuildValue("iiiiiiiii",
! 				        0, 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 = format_strftime(format, tuple);
! 	Py_DECREF(tuple);
! 	return result;
  }
  
***************
*** 436,464 ****
   */
  static PyObject *
! datetime_richcompare(PyDateTime_DateTime *self, PyObject *other, int op)
  {
  	long diff;
  
! 	if (!PyType_IsSubtype(other->ob_type, &PyDateTime_DateTimeType)) {
  		PyErr_Format(PyExc_TypeError,
! 			     "can't compare datetime to %s instance",
  			     other->ob_type->tp_name);
  		return NULL;
  	}
! 	diff = memcmp(self->data, ((PyDateTime_DateTime *)other)->data,
! 		      _PyDateTime_DATETIME_DATA_SIZE);
  	return diff_to_bool(diff, op);
  }
  
  static long
! datetime_hash(PyDateTime_DateTime *self)
  {
  	if (self->hashcode == -1) {
  		PyObject *temp;
! 		temp = Py_BuildValue("lllllll", 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));
  		if (temp != NULL) {
  			self->hashcode = PyObject_Hash(temp);
--- 146,174 ----
   */
  static PyObject *
! time_richcompare(PyDateTime_Time *self, PyObject *other, int op)
  {
  	long diff;
  
! 	if (!PyType_IsSubtype(other->ob_type, &PyDateTime_TimeType)) {
  		PyErr_Format(PyExc_TypeError,
! 			     "can't compare time to %s instance",
  			     other->ob_type->tp_name);
  		return NULL;
  	}
! 	diff = memcmp(self->data, ((PyDateTime_Time *)other)->data,
! 		      _PyDateTime_TIME_DATASIZE);
  	return diff_to_bool(diff, op);
  }
  
  static long
! time_hash(PyDateTime_Time *self)
  {
  	if (self->hashcode == -1) {
  		PyObject *temp;
! 		temp = Py_BuildValue("llll",
! 				      TIME_GET_HOUR(self),
! 				      TIME_GET_MINUTE(self),
! 				      TIME_GET_SECOND(self),
! 				      TIME_GET_MICROSECOND(self));
  		if (temp != NULL) {
  			self->hashcode = PyObject_Hash(temp);
***************
*** 469,495 ****
  }
  
! static PyObject *
! datetime_timetuple(PyDateTime_DateTime *self)
! {
! 	const int year = GET_YEAR(self);
! 	const int month = GET_MONTH(self);
! 	const int day = GET_DAY(self);
! 
! 	return Py_BuildValue("iiiiiiiii",
! 			     year, month, day,
! 			     DATE_GET_HOUR(self),
! 			     DATE_GET_MINUTE(self),
! 			     DATE_GET_SECOND(self),
! 			     weekday(year, month, day),
! 			     days_before_month(year, month) + day,
! 			     -1);
! }
! 
! static PyObject *
! datetime_getdate(PyDateTime_DateTime *self)
  {
! 	return new_date((int)GET_YEAR(self),
! 			(int)GET_MONTH(self),
! 			(int)GET_DAY(self));
  }
  
--- 179,189 ----
  }
  
! static int
! time_nonzero(PyDateTime_Time *self)
  {
! 	return TIME_GET_HOUR(self) ||
! 	       TIME_GET_MINUTE(self) ||
! 	       TIME_GET_SECOND(self) ||
! 	       TIME_GET_MICROSECOND(self);
  }
  
***************
*** 497,508 ****
  
  static PyObject *
! datetime_getstate(PyDateTime_DateTime *self)
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_DATETIME_DATA_SIZE);
  }
  
  static PyObject *
! datetime_setstate(PyDateTime_DateTime *self, PyObject *state)
  {
  	const int len = PyString_Size(state);
--- 191,202 ----
  
  static PyObject *
! time_getstate(PyDateTime_Time *self)
  {
  	return PyString_FromStringAndSize(self->data,
! 					  _PyDateTime_TIME_DATASIZE);
  }
  
  static PyObject *
! time_setstate(PyDateTime_Time *self, PyObject *state)
  {
  	const int len = PyString_Size(state);
***************
*** 510,519 ****
  
  	if (! PyString_Check(state) ||
! 	    len != _PyDateTime_DATETIME_DATA_SIZE) {
  		PyErr_SetString(PyExc_TypeError,
! 				"bad argument to datetime.__setstate__");
  		return NULL;
  	}
! 	memcpy(self->data, pdata, _PyDateTime_DATETIME_DATA_SIZE);
  	self->hashcode = -1;
  
--- 204,213 ----
  
  	if (! PyString_Check(state) ||
! 	    len != _PyDateTime_TIME_DATASIZE) {
  		PyErr_SetString(PyExc_TypeError,
! 				"bad argument to time.__setstate__");
  		return NULL;
  	}
! 	memcpy(self->data, pdata, _PyDateTime_TIME_DATASIZE);
  	self->hashcode = -1;
  
***************
*** 524,542 ****
  /* XXX This seems a ridiculously inefficient way to pickle a short string. */
  static PyObject *
! datetime_pickler(PyObject *module, PyDateTime_DateTime *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 = datetime_getstate(datetime);
  	if (state) {
  		result = Py_BuildValue("O(O)",
! 				       datetime_unpickler_object,
  				       state);
  		Py_DECREF(state);
--- 218,236 ----
  /* XXX This seems a ridiculously inefficient way to pickle a short string. */
  static PyObject *
! time_pickler(PyObject *module, PyDateTime_Time *time)
  {
  	PyObject *state;
  	PyObject *result = NULL;
  
! 	if (time->ob_type != &PyDateTime_TimeType) {
  		PyErr_Format(PyExc_TypeError,
! 			     "bad type passed to time pickler: %s",
! 			     time->ob_type->tp_name);
  		return NULL;
  	}
! 	state = time_getstate(time);
  	if (state) {
  		result = Py_BuildValue("O(O)",
! 				       time_unpickler_object,
  				       state);
  		Py_DECREF(state);
***************
*** 546,562 ****
  
  static PyObject *
! datetime_unpickler(PyObject *module, PyObject *arg)
  {
! 	PyDateTime_DateTime *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_DateTime, &PyDateTime_DateTimeType);
  	if (self != NULL) {
! 		PyObject *res = datetime_setstate(self, arg);
  		Py_XDECREF(res);
  	}
--- 240,256 ----
  
  static PyObject *
! time_unpickler(PyObject *module, PyObject *arg)
  {
! 	PyDateTime_Time *self;
  
  	if (! PyString_CheckExact(arg)) {
  		PyErr_Format(PyExc_TypeError,
! 			     "bad type passed to time unpickler: %s",
  			     arg->ob_type->tp_name);
  		return NULL;
  	}
! 	self = PyObject_New(PyDateTime_Time, &PyDateTime_TimeType);
  	if (self != NULL) {
! 		PyObject *res = time_setstate(self, arg);
  		Py_XDECREF(res);
  	}
***************
*** 564,621 ****
  }
  
! static PyMethodDef datetime_methods[] = {
! 	/* Class methods: */
! 	/* XXX METH_CLASS is implemented incorrectly:
! 	 * XXX http://www.python.org/sf/548651
! 	 * XXX The signatures of these methods will need to change (for
! 	 * XXX the better) when that's fixed.
! 	 */
! 	{"now",         (PyCFunction)datetime_now,
! 	 METH_O | METH_CLASS,
! 	 "Return a new datetime representing local day and time."},
! 
! 	{"utcnow",         (PyCFunction)datetime_utcnow,
! 	 METH_O | METH_CLASS,
! 	 "Return a new datetime representing UTC day and time."},
! 
! 	{"fromtimestamp", (PyCFunction)datetime_fromtimestamp,
! 	 METH_VARARGS | METH_CLASS,
! 	 "timestamp -> local datetime from a POSIX timestamp "
! 	 "(like time.time())."},
! 
! 	{"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp,
! 	 METH_VARARGS | METH_CLASS,
! 	 "timestamp -> UTC datetime from a POSIX timestamp "
! 	 "(like time.time())."},
! 
! 	/* Instance methods: */
! 	{"timetuple",   (PyCFunction)datetime_timetuple, METH_NOARGS,
!          "Return time tuple, compatible with time.localtime()."},
! 
! 	{"date",   (PyCFunction)datetime_getdate, METH_NOARGS,
!          "Return date object with same year, month and day."},
! 
! 	{"ctime",       (PyCFunction)datetime_ctime,	METH_NOARGS,
! 	 "Return ctime() style string."},
  
! 	{"isoformat",   (PyCFunction)datetime_isoformat, METH_KEYWORDS,
! 	 "[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)datetime_setstate, METH_O,
  	 	PyDoc_STR("__setstate__(state)")},
  
! 	{"__getstate__", (PyCFunction)datetime_getstate, METH_NOARGS,
  	 	PyDoc_STR("__getstate__() -> state")},
  	{NULL,	NULL}
  };
  
! static char datetime_doc[] =
! "Basic date/time type.";
  
! static PyNumberMethods datetime_as_number = {
! 	datetime_add,				/* nb_add */
! 	datetime_subtract,			/* nb_subtract */
  	0,					/* nb_multiply */
  	0,					/* nb_divide */
--- 258,282 ----
  }
  
! static PyMethodDef time_methods[] = {
! 	{"isoformat",   (PyCFunction)time_isoformat,	METH_KEYWORDS,
! 	 "Return string in ISO 8601 format, HH:MM:SS.mmmmmm."},
  
! 	{"strftime",   	(PyCFunction)time_strftime,	METH_O,
! 	 "format -> strftime() style string."},
  
! 	{"__setstate__", (PyCFunction)time_setstate,	METH_O,
  	 	PyDoc_STR("__setstate__(state)")},
  
! 	{"__getstate__", (PyCFunction)time_getstate,	METH_NOARGS,
  	 	PyDoc_STR("__getstate__() -> state")},
  	{NULL,	NULL}
  };
  
! static char time_doc[] =
! "Basic time type.";
  
! static PyNumberMethods time_as_number = {
! 	0,					/* nb_add */
! 	0,					/* nb_subtract */
  	0,					/* nb_multiply */
  	0,					/* nb_divide */
***************
*** 626,638 ****
  	0,					/* nb_positive */
  	0,					/* nb_absolute */
! 	(inquiry)date_nonzero,			/* nb_nonzero */
  };
  
! statichere PyTypeObject PyDateTime_DateTimeType = {
  	PyObject_HEAD_INIT(NULL)
  	0,					/* ob_size */
  	/* XXX When this module is renamed to datetime, change tp_name. */
! 	"_datetime.datetime",			/* tp_name */
! 	sizeof(PyDateTime_DateTime),		/* tp_basicsize */
  	0,					/* tp_itemsize */
  	(destructor)PyObject_Del,		/* tp_dealloc */
--- 287,299 ----
  	0,					/* nb_positive */
  	0,					/* nb_absolute */
! 	(inquiry)time_nonzero,			/* nb_nonzero */
  };
  
! statichere PyTypeObject PyDateTime_TimeType = {
  	PyObject_HEAD_INIT(NULL)
  	0,					/* ob_size */
  	/* XXX When this module is renamed to datetime, change tp_name. */
! 	"_datetime.time",			/* tp_name */
! 	sizeof(PyDateTime_Time),		/* tp_basicsize */
  	0,					/* tp_itemsize */
  	(destructor)PyObject_Del,		/* tp_dealloc */
***************
*** 641,651 ****
  	0,					/* tp_setattr */
  	0,					/* tp_compare */
! 	(reprfunc)datetime_repr,		/* tp_repr */
! 	&datetime_as_number,			/* tp_as_number */
  	0,					/* tp_as_sequence */
  	0,					/* tp_as_mapping */
! 	(hashfunc)datetime_hash,		/* tp_hash */
  	0,              			/* tp_call */
! 	(reprfunc)datetime_str,			/* tp_str */
  	PyObject_GenericGetAttr,		/* tp_getattro */
  	0,					/* tp_setattro */
--- 302,312 ----
  	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_isoformat,		/* tp_str */
  	PyObject_GenericGetAttr,		/* tp_getattro */
  	0,					/* tp_setattro */
***************
*** 653,667 ****
  	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
          Py_TPFLAGS_BASETYPE,			/* tp_flags */
! 	datetime_doc,				/* tp_doc */
  	0,					/* tp_traverse */
  	0,					/* tp_clear */
! 	(richcmpfunc)datetime_richcompare,	/* tp_richcompare */
  	0,					/* tp_weaklistoffset */
  	0,					/* tp_iter */
  	0,					/* tp_iternext */
! 	datetime_methods,			/* tp_methods */
  	0,					/* tp_members */
! 	datetime_getset,			/* tp_getset */
! 	&PyDateTime_DateType,			/* tp_base */
  	0,					/* tp_dict */
  	0,					/* tp_descr_get */
--- 314,328 ----
  	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 */
***************
*** 670,675 ****
  	0,					/* tp_init */
  	0,					/* tp_alloc */
! 	datetime_new,				/* tp_new */
  	_PyObject_Del,				/* tp_free */
  };
- 
--- 331,335 ----
  	0,					/* tp_init */
  	0,					/* tp_alloc */
! 	time_new,				/* tp_new */
  	_PyObject_Del,				/* tp_free */
  };

Index: test_both.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_both.py,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** test_both.py	7 Dec 2002 05:33:45 -0000	1.48
--- test_both.py	7 Dec 2002 18:34:39 -0000	1.49
***************
*** 40,43 ****
--- 40,44 ----
  date = datetime.date
  timedelta = datetime.timedelta
+ time = datetime.time
  MINYEAR = datetime.MINYEAR
  MAXYEAR = datetime.MAXYEAR
***************
*** 1119,1122 ****
--- 1120,1244 ----
          # self.assertEqual(dt.time(), time(18, 45, 3, 1234))
  
+ class TestTime(unittest.TestCase):
+ 
+     theclass = time
+ 
+     def test_basic_attributes(self):
+         t = self.theclass(12, 0)
+         self.assertEqual(t.hour, 12)
+         self.assertEqual(t.minute, 0)
+         self.assertEqual(t.second, 0)
+         self.assertEqual(t.microsecond, 0)
+ 
+     def test_basic_attributes_nonzero(self):
+         # Make sure all attributes are non-zero so bugs in
+         # bit-shifting access show up.
+         t = self.theclass(12, 59, 59, 8000)
+         self.assertEqual(t.hour, 12)
+         self.assertEqual(t.minute, 59)
+         self.assertEqual(t.second, 59)
+         self.assertEqual(t.microsecond, 8000)
+ 
+     def test_roundtrip(self):
+         for t in (self.theclass(1, 2, 3, 4),):
+             # Verify t -> string -> time identity.
+             s = repr(t)
+             t2 = eval(s)
+             self.assertEqual(t, t2)
+ 
+             # Verify identity via reconstructing from pieces.
+             t2 = self.theclass(t.hour, t.minute, t.second,
+                                t.microsecond)
+             self.assertEqual(t, t2)
+ 
+     def test_comparing(self):
+         t1 = self.theclass(9, 0, 0)
+         t2 = self.theclass(10, 0, 0)
+         t3 = self.theclass(9, 0, 0)
+         self.assertEqual(t1, t3)
+         self.assert_(t2 > t3)
+ 
+     def test_bad_constructor_arguments(self):
+         # bad hours
+         self.theclass(0, 0)    # no exception
+         self.theclass(23, 0)   # no exception
+         self.assertRaises(ValueError, self.theclass, -1, 0)
+         self.assertRaises(ValueError, self.theclass, 24, 0)
+         # bad minutes
+         self.theclass(23, 0)    # no exception
+         self.theclass(23, 59)   # no exception
+         self.assertRaises(ValueError, self.theclass, 23, -1)
+         self.assertRaises(ValueError, self.theclass, 23, 60)
+         # bad seconds
+         self.theclass(23, 59, 0)    # no exception
+         self.theclass(23, 59, 59)   # no exception
+         self.assertRaises(ValueError, self.theclass, 23, 59, -1)
+         self.assertRaises(ValueError, self.theclass, 23, 59, 60)
+         # bad microseconds
+         self.theclass(23, 59, 59, 0)        # no exception
+         self.theclass(23, 59, 59, 999999)   # no exception
+         self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
+         self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
+ 
+     def test_hash_equality(self):
+         d = self.theclass(23, 30, 17)
+         e = self.theclass(23, 30, 17)
+         self.assertEqual(d, e)
+         self.assertEqual(hash(d), hash(e))
+ 
+         dic = {d: 1}
+         dic[e] = 2
+         self.assertEqual(len(dic), 1)
+         self.assertEqual(dic[d], 2)
+         self.assertEqual(dic[e], 2)
+ 
+         d = self.theclass(0,  5, 17)
+         e = self.theclass(0,  5, 17)
+         self.assertEqual(d, e)
+         self.assertEqual(hash(d), hash(e))
+ 
+         dic = {d: 1}
+         dic[e] = 2
+         self.assertEqual(len(dic), 1)
+         self.assertEqual(dic[d], 2)
+         self.assertEqual(dic[e], 2)
+ 
+     def test_isoformat(self):
+         t = self.theclass(4, 5, 1, 123)
+         self.assertEqual(t.isoformat(), "04:05:01.000123")
+ 
+     def test_strftime(self):
+         t = self.theclass(1, 2, 3, 4)
+         self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
+ 
+     def test_str(self):
+         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
+         self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
+         self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
+         self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03.000000")
+         self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00.000000")
+ 
+     def test_repr(self):
+         name = self.theclass.__name__
+         if TESTING_C:
+             name = '_datetime.' + name
+ 
+         self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
+                          "%s(1, 2, 3, 4)" % name)
+         self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
+                          "%s(10, 2, 3, 4000)" % name)
+         self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
+                          "%s(0, 2, 3, 400000)" % name)
+         self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
+                          "%s(12, 2, 3)" % name)
+         self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
+                          "%s(23, 15)" % name)
+ 
+     def test_resolution_info(self):
+         self.assert_(isinstance(self.theclass.min, self.theclass))
+         self.assert_(isinstance(self.theclass.max, self.theclass))
+         self.assert_(isinstance(self.theclass.resolution, timedelta))
+         self.assert_(self.theclass.max > self.theclass.min)
+ 
  def test_suite():
      allsuites = [unittest.makeSuite(klass, 'test')
***************
*** 1126,1129 ****
--- 1248,1252 ----
                                 TestDate,
                                 TestDateTime,
+                                TestTime,
                                )
                  ]

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.55
retrieving revision 1.56
diff -C2 -d -r1.55 -r1.56
*** test_datetime.py	7 Dec 2002 05:33:45 -0000	1.55
--- test_datetime.py	7 Dec 2002 18:34:39 -0000	1.56
***************
*** 4,7 ****
--- 4,13 ----
  """
  
+ # XXX When a test passes in both the Python and C implementations, it
+ # XXX gets moved from this file into test_both.py.
+ # XXX Eventually (when the C implementation is complete), this file will
+ # XXX be empty, and at that time test_both.py will be renamed to
+ # XXX test_datetime.py.
+ 
  import sys
  import unittest
***************
*** 10,132 ****
                       MINYEAR, MAXYEAR
  
! class TestTime(unittest.TestCase):
! 
!     theclass = time
! 
!     def test_basic_attributes(self):
!         t = self.theclass(12, 0)
!         self.assertEqual(t.hour, 12)
!         self.assertEqual(t.minute, 0)
!         self.assertEqual(t.second, 0)
!         self.assertEqual(t.microsecond, 0)
! 
!     def test_basic_attributes_nonzero(self):
!         # Make sure all attributes are non-zero so bugs in
!         # bit-shifting access show up.
!         t = self.theclass(12, 59, 59, 8000)
!         self.assertEqual(t.hour, 12)
!         self.assertEqual(t.minute, 59)
!         self.assertEqual(t.second, 59)
!         self.assertEqual(t.microsecond, 8000)
! 
!     def test_roundtrip(self):
!         for t in (self.theclass(1, 2, 3, 4),):
!             # Verify t -> string -> time identity.
!             s = repr(t)
!             t2 = eval(s)
!             self.assertEqual(t, t2)
! 
!             # Verify identity via reconstructing from pieces.
!             t2 = self.theclass(t.hour, t.minute, t.second,
!                                t.microsecond)
!             self.assertEqual(t, t2)
! 
!     def test_comparing(self):
!         t1 = self.theclass(9, 0, 0)
!         t2 = self.theclass(10, 0, 0)
!         t3 = self.theclass(9, 0, 0)
!         self.assertEqual(t1, t3)
!         self.assert_(t2 > t3)
! 
!     def test_bad_constructor_arguments(self):
!         # bad hours
!         self.theclass(0, 0)    # no exception
!         self.theclass(23, 0)   # no exception
!         self.assertRaises(ValueError, self.theclass, -1, 0)
!         self.assertRaises(ValueError, self.theclass, 24, 0)
!         # bad minutes
!         self.theclass(23, 0)    # no exception
!         self.theclass(23, 59)   # no exception
!         self.assertRaises(ValueError, self.theclass, 23, -1)
!         self.assertRaises(ValueError, self.theclass, 23, 60)
!         # bad seconds
!         self.theclass(23, 59, 0)    # no exception
!         self.theclass(23, 59, 59)   # no exception
!         self.assertRaises(ValueError, self.theclass, 23, 59, -1)
!         self.assertRaises(ValueError, self.theclass, 23, 59, 60)
!         # bad microseconds
!         self.theclass(23, 59, 59, 0)        # no exception
!         self.theclass(23, 59, 59, 999999)   # no exception
!         self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
!         self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
! 
!     def test_hash_equality(self):
!         d = self.theclass(23, 30, 17)
!         e = self.theclass(23, 30, 17)
!         self.assertEqual(d, e)
!         self.assertEqual(hash(d), hash(e))
! 
!         dic = {d: 1}
!         dic[e] = 2
!         self.assertEqual(len(dic), 1)
!         self.assertEqual(dic[d], 2)
!         self.assertEqual(dic[e], 2)
! 
!         d = self.theclass(0,  5, 17)
!         e = self.theclass(0,  5, 17)
!         self.assertEqual(d, e)
!         self.assertEqual(hash(d), hash(e))
! 
!         dic = {d: 1}
!         dic[e] = 2
!         self.assertEqual(len(dic), 1)
!         self.assertEqual(dic[d], 2)
!         self.assertEqual(dic[e], 2)
! 
!     def test_isoformat(self):
!         t = self.theclass(4, 5, 1, 123)
!         self.assertEqual(t.isoformat(), "04:05:01.000123")
! 
!     def test_strftime(self):
!         t = self.theclass(1, 2, 3, 4)
!         self.assertEqual(t.strftime('%H %M %S'), "01 02 03")
! 
!     def test_str(self):
!         self.assertEqual(str(self.theclass(1, 2, 3, 4)), "1:02:03.000004")
!         self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004")
!         self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "0:02:03.4")
!         self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
!         self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15")
! 
!     def test_repr(self):
!         self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
!                          "%s(1, 2, 3, 4)" % self.theclass.__name__)
!         self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
!                          "%s(10, 2, 3, 4000)" % self.theclass.__name__)
!         self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
!                          "%s(0, 2, 3, 400000)" % self.theclass.__name__)
!         self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
!                          "%s(12, 2, 3)" % self.theclass.__name__)
!         self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
!                          "%s(23, 15)" % self.theclass.__name__)
! 
!     def test_resolution_info(self):
!         self.assert_(isinstance(self.theclass.min, self.theclass))
!         self.assert_(isinstance(self.theclass.max, self.theclass))
!         self.assert_(isinstance(self.theclass.resolution, timedelta))
!         self.assert_(self.theclass.max > self.theclass.min)
! 
! 
! class TestTimeTZ(TestTime):
  
      theclass = timetz
--- 16,20 ----
                       MINYEAR, MAXYEAR
  
! class TestTimeTZ(unittest.TestCase):
  
      theclass = timetz
***************
*** 154,160 ****
          self.assertEqual(t1, t3)
          self.assertEqual(t2, t3)
!         self.assertEqual(str(t1), "7:47 -05:00")
!         self.assertEqual(str(t2), "12:47 +00:00")
!         self.assertEqual(str(t3), "13:47 +01:00")
          self.assertEqual(repr(t1), "timetz(7, 47, tzinfo=est)")
          self.assertEqual(repr(t2), "timetz(12, 47, tzinfo=utc)")
--- 42,48 ----
          self.assertEqual(t1, t3)
          self.assertEqual(t2, t3)
!         self.assertEqual(str(t1), "07:47:00.000000-05:00")
!         self.assertEqual(str(t2), "12:47:00.000000+00:00")
!         self.assertEqual(str(t3), "13:47:00.000000+01:00")
          self.assertEqual(repr(t1), "timetz(7, 47, tzinfo=est)")
          self.assertEqual(repr(t2), "timetz(12, 47, tzinfo=utc)")
***************
*** 266,274 ****
  
  def test_suite():
-     s3 = unittest.makeSuite(TestTime, 'test')
      s4 = unittest.makeSuite(TestTimeTZ, 'test')
      s5 = unittest.makeSuite(TestDateTime, 'test')
      s6 = unittest.makeSuite(TestDateTimeTZ, 'test')
!     return unittest.TestSuite([s3, s4, s5, s6])
  
  def test_main():
--- 154,161 ----
  
  def test_suite():
      s4 = unittest.makeSuite(TestTimeTZ, 'test')
      s5 = unittest.makeSuite(TestDateTime, 'test')
      s6 = unittest.makeSuite(TestDateTimeTZ, 'test')
!     return unittest.TestSuite([s4, s5, s6])
  
  def test_main():