[Python-checkins] python/nondist/sandbox/datetime doc.txt,1.16,1.17 obj_datetime.c,1.21,1.22 test_both.py,1.40,1.41

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Thu, 05 Dec 2002 18:17:44 -0800


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

Modified Files:
	doc.txt obj_datetime.c test_both.py 
Log Message:
Implemented datetime.utcfromtimestamp, and added a test.
Reworked the test for datetime.fromtimestamp to match.
Reworked the internals so that the timestamp constructors could share more
tedious code.
At considerable cost, made datetime.today() work the way it's documented
to work.  Exporting time.time in a more reasonable way (for access from
C code) would eliminate the artificial expense.
Reworked the today() test to try to be robust against several legitimate
failure modes (having to do with systems resetting their clocks,
time.time() having extremely fine resolution, and time.time() having poor
resolution).


Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -d -r1.16 -r1.17
*** doc.txt	6 Dec 2002 00:55:28 -0000	1.16
--- doc.txt	6 Dec 2002 02:17:42 -0000	1.17
***************
*** 346,349 ****
--- 346,350 ----
      Return the current local datetime.  This is equivalent to
      datetime.fromtimestamp(time.time()).
+     See also now().
  
    - now()
***************
*** 352,364 ****
      possible, supplies more precision than can be gotten from going
      through a time.time() timestamp.
      XXX It currently doesn't.
  
    - fromtimestamp(timestamp)
  
!     Return the local datetime corresponding to the POSIX timestamp, such as
!     is returned by time.time().  This may raise ValueError, if the
      timestamp is out of the range of values supported by the platform C
      localtime() function.  It's common for this to be restricted to
      years in 1970 through 2038.
  
    - fromordinal(ordinal)
--- 353,375 ----
      possible, supplies more precision than can be gotten from going
      through a time.time() timestamp.
+     See also today().
      XXX It currently doesn't.
  
    - fromtimestamp(timestamp)
  
!     Return the local datetime corresponding to the POSIX timestamp, such
!     as is returned by time.time().  This may raise ValueError, if the
      timestamp is out of the range of values supported by the platform C
      localtime() function.  It's common for this to be restricted to
      years in 1970 through 2038.
+     See also utcfromtimestamp().
+ 
+   - utcfromtimestamp(timestamp)
+ 
+     Return the UTC datetime corresponding to the POSIX timestamp.
+     This may raise ValueError, if the timestamp is out of the range of
+     values supported by the platform C gmtime() function.  It's common
+     for this to be restricted to years in 1970 through 2038.
+     See also fromtimestamp().
  
    - fromordinal(ordinal)

Index: obj_datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_datetime.c,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -d -r1.21 -r1.22
*** obj_datetime.c	6 Dec 2002 00:55:28 -0000	1.21
--- obj_datetime.c	6 Dec 2002 02:17:42 -0000	1.22
***************
*** 100,112 ****
  }
  
  static PyObject *
! datetime_local_from_time_t(PyObject *cls, time_t t)
  {
  	struct tm *tm;
  	PyObject *result = NULL;
  
! 	tm = localtime(&t);
  	if (tm)
! 		result = PyObject_CallFunction(cls, "llllll",
  					       tm->tm_year + 1900,
  					       tm->tm_mon + 1,
--- 100,118 ----
  }
  
+ 
+ /* 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,
***************
*** 114,126 ****
  					       tm->tm_hour,
  					       tm->tm_min,
! 					       tm->tm_sec);
  	else
  		PyErr_SetString(PyExc_ValueError,
  				"timestamp out of range for "
! 				"platform localtime() function");
  	return result;
  }
  
! /* Return new datetime from given timestamp (Python timestamp -- a double). */
  static PyObject *
  datetime_fromtimestamp(PyObject *self, PyObject *args)
--- 120,133 ----
  					       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)
***************
*** 131,135 ****
  
  	if (PyArg_ParseTuple(args, "Od:fromtimestamp", &cls, &timestamp))
! 		result = datetime_local_from_time_t(cls, (time_t)timestamp);
  	return result;
  }
--- 138,155 ----
  
  	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;
  }
***************
*** 178,194 ****
  }
  
  static PyObject *
  datetime_today(PyObject *self, PyObject *cls)
  {
! 	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);
  }
  
--- 198,233 ----
  }
  
+ /* Well, we say this is equivalent to fromtimestamp(time.time()), and the
+  * only way to be sure of that is to *call* time.time().  That's not
+  * generally the same as calling C's time.
+  * XXX Expose the time module's C code in a saner way.
+  */
  static PyObject *
  datetime_today(PyObject *self, PyObject *cls)
  {
! 	PyObject *time;
! 	PyObject *time_time;
! 	PyObject *result;
! 	double timestamp;
  
! 	time = PyImport_ImportModule("time");
! 	if (time == NULL)
! 		return NULL;
  
! 	time_time = PyObject_GetAttrString(time, "time");
! 	Py_DECREF(time);
! 	if (time_time == NULL)
! 		return NULL;
! 
!    	time = PyObject_CallObject(time_time, NULL);
!     	Py_DECREF(time_time);
!     	if (time == NULL)
!     		return NULL;
! 
! 	timestamp = PyFloat_AsDouble(time);
! 	Py_DECREF(time);
! 	if (timestamp == -1.0 && PyErr_Occurred())
! 		return NULL;
!     	return datetime_from_timestamp(cls, localtime, timestamp);
  }
  
***************
*** 444,447 ****
--- 483,487 ----
  
  /* Pickle support.  Quite a maze! */
+ 
  static PyObject *
  datetime_getstate(PyDateTime_DateTime *self)
***************
*** 510,513 ****
--- 550,558 ----
  							   	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())."},
  

Index: test_both.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_both.py,v
retrieving revision 1.40
retrieving revision 1.41
diff -C2 -d -r1.40 -r1.41
*** test_both.py	6 Dec 2002 00:15:28 -0000	1.40
--- test_both.py	6 Dec 2002 02:17:42 -0000	1.41
***************
*** 574,580 ****
          import time
  
!         count = 0
!         while count < 3:
!             count += 1
              today = self.theclass.today()
              ts = time.time()
--- 574,580 ----
          import time
  
!         # We claim that today() is like fromtimestamp(time.time()), so
!         # prove it.
!         for dummy in range(3):
              today = self.theclass.today()
              ts = time.time()
***************
*** 582,592 ****
              if today == todayagain:
                  break
!             # It's possible that equality fails if we're running at a
!             # midnight boundary, and also possible that it fails if we're
!             # testing datetime and it takes more than a second between
!             # operations above.  Wait a little and try again.
!             time.sleep(1)
  
!         self.assertEqual(today, todayagain)
  
      def test_weekday(self):
--- 582,601 ----
              if today == todayagain:
                  break
!             # There are several legit reasons that could fail:
!             # 1. It recently became midnight, between the today() and the
!             #    time() calls.
!             # 2. The platform time() has such fine resolution that we'll
!             #    never get the same value twice.
!             # 3. The platform time() has poor resolution, and we just
!             #    happened to call today() right before a resolution quantum
!             #    boundary.
!             # 4. The system clock got fiddled between calls.
!             # In any case, wait a little while and try again.
!             time.sleep(0.1)
  
!         # It worked or it didn't.  If it didn't, assume it's reason #2, and
!         # let the test pass if they're within half a second of each other.
!         self.failUnless(today == todayagain or
!                         abs(todayagain - today) < timedelta(seconds=0.5))
  
      def test_weekday(self):
***************
*** 1028,1045 ****
              self.assertEqual(cmp(t2, t1), 1)
  
      def test_fromtimestamp(self):
          import time
  
!         # Try an arbitrary fixed value.
!         year, month, day = 1999, 9, 19
!         hour, minute, second = 22, 17, 36
!         ts = time.mktime((year, month, day, hour, minute, second, 0, 0, -1))
!         d = self.theclass.fromtimestamp(ts)
!         self.assertEqual(d.year, year)
!         self.assertEqual(d.month, month)
!         self.assertEqual(d.day, day)
!         self.assertEqual(d.hour, hour)
!         self.assertEqual(d.minute, minute)
!         self.assertEqual(d.second, second)
  
  def test_suite():
--- 1037,1066 ----
              self.assertEqual(cmp(t2, t1), 1)
  
+ 
+     # A helper for timestamp constructor tests.
+     def verify_field_equality(self, expected, got):
+         self.assertEqual(expected.tm_year, got.year)
+         self.assertEqual(expected.tm_mon, got.month)
+         self.assertEqual(expected.tm_mday, got.day)
+         self.assertEqual(expected.tm_hour, got.hour)
+         self.assertEqual(expected.tm_min, got.minute)
+         self.assertEqual(expected.tm_sec, got.second)
+ 
      def test_fromtimestamp(self):
          import time
  
!         ts = time.time()
!         expected = time.localtime(ts)
!         got = self.theclass.fromtimestamp(ts)
!         self.verify_field_equality(expected, got)
! 
!     def test_utcfromtimestamp(self):
!         import time
! 
!         ts = time.time()
!         expected = time.gmtime(ts)
!         got = self.theclass.utcfromtimestamp(ts)
!         self.verify_field_equality(expected, got)
! 
  
  def test_suite():