[Python-checkins] python/nondist/sandbox/datetime doc.txt,1.12,1.13 obj_datetime.c,1.19,1.20 test_both.py,1.38,1.39

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Thu, 05 Dec 2002 15:16:01 -0800


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

Modified Files:
	doc.txt obj_datetime.c test_both.py 
Log Message:
Brute-forced datetime.today() and datetime.fromtimestamp() to work as
intended.  I hate this.  I think what I really want is to figure out how,
at the C level, to say "OK, I've got a type object.  Now figure out what
*it* calls the 'fromtimestamp' class method, and call that".  Else,
like now, I get goofy code duplication and a lack of extensibility.

Also got a start on serious docs for datetime.


Index: doc.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/doc.txt,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** doc.txt	5 Dec 2002 20:28:41 -0000	1.12
--- doc.txt	5 Dec 2002 23:15:59 -0000	1.13
***************
*** 317,320 ****
--- 317,495 ----
  class datetime
  ==============
+ A datetime object is a single object containing all the information from
+ a date object and a time object.  Like a date object, datetime assumes
+ the current Gregorian calendar extended in both directions; like a time
+ object, datetime assumes there are exact 3600*24 seconds in every day.
+ 
+ Constructor:
+ 
+     datetime(year, month, day,
+              hour=0, minute=0, second=0, microsecond=0)
+ 
+     The year, month and day arguments are required.  Arguments may be ints
+     or longs, in the following ranges:
+ 
+         MINYEAR <= year <= MAXYEAR
+         1 <= month <= 12
+         1 <= day <= number of days in the given month and year
+         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):
+ 
+   - today()
+ 
+     Return the current local datetime.  This is equivalent to
+     datetime.fromtimestamp(time.time()).
+ 
+   - now()
+ 
+     Return the current local datetime.  This is like today(), but, if
+     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)
+ 
+     Return the date corresponding to the proleptic Gregorian ordinal,
+     where January 1 of year 1 has ordinal 1.  ValueError is raised
+     unless 1 <= ordinal <= datetime.max.toordinal().
+ 
+   XXX THERE ARE A SLEW OF OTHER CONSTRUCTORS IN THE PYTHON IMPLEMENTATION
+   XXX THAT DON'T EXIST YET IN  THE C IMPLEMENTATION.
+ 
+ Class attributes:
+ 
+     .min
+         The earliest representable datetime,
+         datetime(MINYEAR, 1, 1).
+ 
+     .max
+         The latest representable datetime,
+         datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999).
+ 
+     .resolution
+         The smallest possible difference between non-equal datetime
+         objects, timedelta(microseconds=1).
+ 
+ Instance attributes (read-only):
+ 
+     .year           between MINYEAR and MAXYEAR inclusive
+     .month          between 1 and 12 inclusive
+     .day            between 1 and the number of days in the given month
+                     of the given year
+     .hour           in range(24)
+     .minute         in range(60)
+     .second         in range(60)
+     .microsecond    in range(1000000)
+ 
+ Supported operations:
+ 
+     - datetime1 + timedelta -> datetime2
+       timedelta + datetime1 -> datetime2
+       datetime2 is a duration of timedelta removed from datetime1, moving
+       forward in time if timedelta.days > 0, or backward if
+       timedelta.days < 0.  datetime2 - datetime1 == timedelta after.
+       OverflowError is raised if date2.year would be smaller than MINYEAR
+       or larger than MAXYEAR.
+ 
+ XXX NOTHING BELOW THIS POINT HAS BEEN CHECKED, AND MUCH IS CERTAINLY
+ XXX WRONG (CUT & PASTE ERRORS MOSTLY) AND/OR MISIMPLEMENTED.
+ 
+     - date1 - timedelta -> date2
+       Computes the date2 such that date2 + timedelta == date1.  This
+       isn't quite equivalent to date1 + (-timedelta), because -timedelta
+       in isolation can overflow in cases where date1 - timedelta does
+       not.  timedelta.seconds and timedelta.microseconds are ignored.
+ 
+     - date1 - date2 -> timedelta
+       This is exact, and cannot overflow.  timedelta.seconds and
+       timedelta.microseconds are 0, and date2 + timedelta == date1
+       after.
+ 
+     - comparison of date to date, where date1 is considered less than
+       date2 when date1 precedes date2 in time.  In other words,
+       date1 < date2 if and only if date1.toordinal() < date2.toordinal().
+ 
+     - hash, use as dict key
+ 
+     - pickling
+ 
+ Instance methods:
+ 
+   - timetuple()
+     Return a 9-element tuple of the form returned by time.localtime().
+     The hours, minutes and seconds are 0, and the DST flag is -1.
+     d.timetuple() is equivalent to
+         (d.year, d.month, d.day,
+          0, 0, 0,  # h, m, s
+          d.weekday(),  # 0 is Monday
+          d.toordinal() - date(d.year, 1, 1).toordinal() + 1, # day of year
+          -1)
+ 
+   - toordinal()
+     Return the proleptic Gregorian ordinal of the date, where January 1
+     of year 1 has ordinal 1.  For any date object d,
+     date.fromordinal(d.toordinal()) == d.
+ 
+   - weekday()
+     Return the day of the week as an integer, where Monday is 0 and
+     Sunday is 6.  For example, date(2002, 12, 4).weekday() == 2, a
+     Wednesday.
+     See also isoweekday().
+ 
+   - isoweekday()
+     Return the day of the week as an integer, where Monday is 1 and
+     Sunday is 7.  For example, date(2002, 12, 4).isoweekday() == 3, a
+     Wednesday.
+     See also weekday() and isocalendar().
+ 
+   - isocalendar()
+     Return a 3-tuple, (ISO year, ISO week number, ISO weekday).
+ 
+     The ISO calendar is a widely used variant of the Gregorian calendar.
+     See <http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm>
+     for a good explanation.
+ 
+     The ISO year consists of 52 or 53 full weeks, and where a week starts
+     on a Monday and ends on a Sunday.  The first week of an ISO year is
+     the first (Gregorian) calendar week of a year containing a Thursday.
+     This is called week number 1, and the ISO year of that Thursday is
+     the same as its Gregorian year.
+ 
+     For example, 2004 begins on a Thursday, so the first week of ISO
+     year 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan
+     2004, so that
+ 
+     date(2003, 12, 29).isocalendar() == (2004, 1, 1)
+     date(2004, 1, 4).isocalendar() == (2004, 1, 7)
+ 
+   - isoformat()
+     Return a string representing the date in ISO 8601 format,
+     'YYYY-MM-DD'.  For example,
+     date(2002, 12, 4).isoformat() == '2002-12-04'.
+     str(d) is equivalent to d.isoformat().
+ 
+   - ctime()
+     Return a string representing the date, for example
+     date(2002, 12, 4).ctime() == 'Wed Dec  4 00:00:00 2002'.
+     d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())).
+ 
+   - strftime(format)
+     Return a string representing the date, controlled by an explicit
+     format string.  d.strftime(f) is the same as
+     time.strftime(f, d.timetuple()).
  
  

Index: obj_datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_datetime.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -C2 -d -r1.19 -r1.20
*** obj_datetime.c	5 Dec 2002 21:29:36 -0000	1.19
--- obj_datetime.c	5 Dec 2002 23:15:59 -0000	1.20
***************
*** 241,244 ****
--- 241,337 ----
  
  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,
+ 					       tm->tm_mday,
+ 					       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)
+ {
+ 	PyObject *cls;
+ 	double timestamp;
+ 	PyObject *result = NULL;
+ 
+ 	if (PyArg_ParseTuple(args, "Od:fromtimestamp", &cls, &timestamp))
+ 		result = datetime_local_from_time_t(cls, (time_t)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_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);
+ }
+ 
+ static PyObject *
  datetime_hour(PyDateTime_DateTime *self, void *unused)
  {
***************
*** 345,406 ****
  }
  
- 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, "iiiiiil",
- 				     tm->tm_year + 1900, tm->tm_mon + 1,
- 				     tm->tm_mday, tm->tm_hour, tm->tm_min,
- 				     tm->tm_sec, 0);
- #endif
- }
- 
- static PyObject *
- datetime_today(PyObject *self, PyObject *cls)
- {
- 	/* 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, "iiiiiil",
- 				     tm->tm_year + 1900, tm->tm_mon + 1,
- 				     tm->tm_mday, 0, 0, 0, 0);
- }
- 
  /* Pickle support.  Quite a maze! */
  static PyObject *
--- 438,441 ----
***************
*** 463,478 ****
--- 498,522 ----
  	{"now",         (PyCFunction)datetime_now,	METH_O | METH_CLASS,
  	 "Return a new datetime that represents the current time."},
+ 
  	{"today",         (PyCFunction)datetime_today,	METH_O | METH_CLASS,
  	 "Return a new datetime that represents the current date."},
  
+ 	{"fromtimestamp", (PyCFunction)datetime_fromtimestamp, 	METH_VARARGS |
+ 							   	METH_CLASS,
+ 	 "timestamp -> local datetime from a POSIX timestamp "
+ 	 "(like time.time())."},
+ 
  	/* Instance methods: */
  	{"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")},

Index: test_both.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_both.py,v
retrieving revision 1.38
retrieving revision 1.39
diff -C2 -d -r1.38 -r1.39
*** test_both.py	5 Dec 2002 20:28:41 -0000	1.38
--- test_both.py	5 Dec 2002 23:15:59 -0000	1.39
***************
*** 563,577 ****
          self.assertEqual(d.day, day)
  
!         # Try today.
!         today = self.theclass.today()
!         ts = time.time()
!         todayagain = self.theclass.fromtimestamp(ts)
!         if today != todayagain:
!             # It's possible that equality fails if we're running at a
!             # midnight boundary, so wait a little and try again.
!             time.sleep(5)
              today = self.theclass.today()
              ts = time.time()
              todayagain = self.theclass.fromtimestamp(ts)
          self.assertEqual(today, todayagain)
  
--- 563,583 ----
          self.assertEqual(d.day, day)
  
!     def test_today(self):
!         import time
! 
!         count = 0
!         while count < 3:
!             count += 1
              today = self.theclass.today()
              ts = time.time()
              todayagain = self.theclass.fromtimestamp(ts)
+             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)
  
***************
*** 1014,1017 ****
--- 1020,1037 ----
              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():