[Python-checkins] python/nondist/sandbox/datetime datetime.c,1.28,1.29 datetime.py,1.66,1.67 obj_date.c,1.10,1.11 obj_datetime.c,1.5,1.6 obj_delta.c,1.7,1.8 test_both.py,1.10,1.11 test_datetime.py,1.52,1.53 test_cdatetime.py,1.13,NONE
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Sun, 01 Dec 2002 11:37:31 -0800
Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory sc8-pr-cvs1:/tmp/cvs-serv3635
Modified Files:
datetime.c datetime.py obj_date.c obj_datetime.c obj_delta.c
test_both.py test_datetime.py
Removed Files:
test_cdatetime.py
Log Message:
A huge # of changes, mostly implementing missing methods and getting the
arithmetic to work. All test_both.py tests pass now, under both the C
and Python implementations. No unique tests remained in test_cdatetime.py,
so that file was removed from the project.
Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.28
retrieving revision 1.29
diff -C2 -d -r1.28 -r1.29
*** datetime.c 26 Nov 2002 23:02:52 -0000 1.28
--- datetime.c 1 Dec 2002 19:37:28 -0000 1.29
***************
*** 18,22 ****
PyObject_HEAD
long hashcode;
! long days; /* this may be negative */
long seconds; /* 0 <= seconds < 24*3600 is invariant */
long microseconds; /* 0 <= microseconds < 1000000 is invariant */
--- 18,22 ----
PyObject_HEAD
long hashcode;
! long days; /* in -TOOBIG_INPUT .. TOOBIG_INPUT */
long seconds; /* 0 <= seconds < 24*3600 is invariant */
long microseconds; /* 0 <= microseconds < 1000000 is invariant */
***************
*** 96,99 ****
--- 96,126 ----
}
+ /* All input fields (year, month, day, hour, minute, second, millisecond,
+ * microsecond) are constrained to lie within -TOOBIG_INPUT .. TOOBIG_INPUT
+ * exclusive of endpoints. You can't make this larger without studying the
+ * code very carefully, as various "can't overflow" assurances follow
+ * from the specific value used here.
+ */
+ #define TOOBIG_INPUT 1000000000 /* a billion; 1e9 */
+
+ /* Check that x is in the range given above. If so, return 0. If not,
+ * raise the given exception and return -1.
+ */
+ static int
+ check_range(long x, const char* tag, PyObject *exception)
+ {
+ char buf[200];
+
+ if (-TOOBIG_INPUT < x && x < TOOBIG_INPUT)
+ return 0;
+ /* PyErr_Format() ignores the "l" in "%ld", which isn't correct
+ * on boxes where sizeof(long) > sizeof(int). So roll our own.
+ */
+ PyOS_snprintf(buf, sizeof(buf), "%s=%ld; must have magnitude < %ld",
+ tag, x, TOOBIG_INPUT);
+ PyErr_SetString(exception, buf);
+ return -1;
+ }
+
/* For each month ordinal in 1..12, the number of days in that month,
* and the number of days before that month in the same year. These
***************
*** 327,460 ****
! /* Create a date instance with no range checking. */
! static PyObject *
! new_date(int year, int month, int day)
{
! PyDateTime_Date *self;
!
! 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;
}
! /* Create a datetime instance with no range checking. */
! static PyObject *
! new_datetime(int year, int month, int day, int hour, int minute,
! int second, int usecond)
{
! PyDateTime_DateTime *self;
- 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);
- SET_HOUR(self, hour);
- SET_MINUTE(self, minute);
- SET_SECOND(self, second);
- SET_MICROSECOND(self, usecond);
}
! return (PyObject *) self;
}
! /* An internal struct used for miserable normalization tasks. */
! typedef struct s_tmxxx {
! long year; /* may be negative on output */
! long month; /* in 1..12 on output */
! long day; /* in 1 .. days_in_month(year, month) on output */
! long hour; /* in 0..23 on output */
! long minute; /* in 0..59 on output */
! long second; /* in 0 .. 59 on output */
! long microsecond; /* in 0..999999 on output */
! } tmxxx;
!
! #define TMXXX_CLEAR(PTR_TO_TMXXX) \
! memset(PTR_TO_TMXXX, 0, sizeof(struct s_tmxxx))
!
! /* One step of mixed-radix conversion. *lower is the lower unit and *higher
! * the higher unit, such that 1 higher unit is equal to bound lower units.
! * Normalize *lower to lie in range(bound), adding carries (if needed) to
! * higher.
! * If a carry is needed, adding into *higher may overflow. In that case,
! * retuns a non-zero value. If everything is OK, returns 0.
*/
! static int
! norm1(long *higher, long *lower, long bound)
{
! assert(bound > 0);
! if (*lower < 0 || *lower >= bound) {
! long carry;
! long new_higher;
- carry = divmod(*lower, bound, lower);
- assert(0 <= *lower && *lower < bound);
- new_higher = *higher + carry;
- if (SIGNED_ADD_OVERFLOWED(new_higher, *higher, carry))
- return 1;
- *higher = new_higher;
}
! return 0;
}
! static int
! tmxxx_normalize(tmxxx *p)
{
! int dim; /* days in month */
! char *msg;
! if (norm1(&p->second, &p->microsecond, 1000000)) {
! msg = "second component too large";
! goto Overflow;
}
! assert(0 <= p->microsecond && p->microsecond < 1000000);
! if (norm1(&p->minute, &p->second, 60)) {
! msg = "minute component too large";
! goto Overflow;
}
! assert(0 <= p->second && p->second < 60);
! if (norm1(&p->hour, &p->minute, 60)) {
! msg = "hour component too large";
! goto Overflow;
}
! assert(0 <= p->minute && p->minute < 60);
! if (norm1(&p->day, &p->hour, 60)) {
! msg = "hour component too large";
! goto Overflow;
! }
! assert(0 <= p->hour && p->hour < 24);
! /* That was easy. Now it gets muddy: the proper range for day
! * can't be determined without knowing the correct month and year,
! * but if day is, e.g., plus or minus a million, the current month
! * and year values make no sense (and may also be out of bounds
! * themselves).
* Saying 12 months == 1 year should be non-controversial.
*/
! if (p->month < 1 || p->month > 12) {
! --p->month;
! if (norm1(&p->year, &p->month, 12)) {
! msg = "year component too large";
! goto Overflow;
! }
! ++p->month;
! assert (1 <= p->month && p->month <= 12);
}
- /* The other routines don't deal correctly with years < 0, so cut
- * that off now.
- */
- if (p->year < 0) {
- msg = "year component is negative";
- goto Overflow;
- }
/* Now only day can be out of bounds (year may also be out of bounds
* for a datetime object, but we don't care about that here).
--- 354,519 ----
! /* One step of a mixed-radix conversion. A "hi" unit is equivalent to
! * factor "lo" units. factor must be > 0. If *lo is less than 0, or
! * at least factor, enough of *lo is converted into "hi" units so that
! * 0 <= *lo < factor. The input values must be such that long overflow
! * is impossible.
! */
! static void
! normalize_pair(long *hi, long *lo, long factor)
{
! assert(factor > 0);
! assert(lo != hi);
! if (*lo < 0 || *lo >= factor) {
! const long num_hi = divmod(*lo, factor, lo);
! const long new_hi = *hi + num_hi;
! assert(! SIGNED_ADD_OVERFLOWED(new_hi, *hi, num_hi));
! *hi = new_hi;
}
! assert(0 <= *lo && *lo < factor);
}
! /* Fiddle days (d), hours (h), and minutes (m) so that
! * 0 <= *h < 24
! * 0 <= *m < 60
! * 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_h_m(long *d, long *h, long *m)
{
! if (*m < 0 || *m >= 60) {
! normalize_pair(h, m, 60);
! /* |h| can't be bigger than about
! * |original h| + |original m|/60 now.
! */
}
! if (*h < 0 || *h >= 24) {
! normalize_pair(d, h, 24);
! /* |d| can't be bigger than about
! * |original d| +
! * (|original h| + |original m|/60) / 24 now.
! */
! }
! assert(0 <= *h && *h < 24);
! assert(0 <= *m && *m < 60);
}
! /* 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 days (d), seconds (s), and microseconds (us) so that the output
! * duration is the same as the input duration, but with hours (h),
! * minutes (m), and milliseconds (ms) all 0.
! * The input values must be such that the internals don't overflow. The
! * way this routine is used, we don't get close.
! * The output d, s, and us are intended to be passed to normalize_d_s_us
! * to get them into their proper ranges.
! */
! static void
! squash_h_m_ms_out_of_d_h_m_s_ms_us(long *d, long h, long m,
! long *s, long ms, long *us)
{
! if (h) {
! long new_minutes;
! normalize_pair(d, &h, 24);
! /* |d| can't be bigger than about
! * |original d| + |original h|/24 now.
! * h can't bigger than 23.
! */
! new_minutes = m + h*60;
! assert(! SIGNED_ADD_OVERFLOWED(new_minutes, m, h*60));
! m = new_minutes;
! /* |m| can't be bigger than about
! * |original m| + 23*60 now.
! */
}
! if (m) {
! long new_seconds;
! normalize_pair(d, &m, 24*60);
! /* |d| can't be bigger than about
! * |original d| + |original h|/24 +
! * (|original m| + 23*60)/(24*60) now.
! * m can't bigger than 24*60-1.
! */
! new_seconds = *s + m*60;
! assert(! SIGNED_ADD_OVERFLOWED(new_seconds, *s, m*60));
! *s = new_seconds;
! /* |s| can't be bigger than about
! * |original s| + (24*60-1) * 60 now.
! */
}
! if (ms) {
! long new_us;
! normalize_pair(s, &ms, 1000);
! /* |s| can't be bigger than about
! * |original s| + (24*60-1) * 60 + |original ms|/1000 now.
! * ms can't be bigger than 999.
! */
! new_us = *us + ms * 1000;
! assert(! SIGNED_ADD_OVERFLOWED(new_us, *us, ms*1000));
! *us = new_us;
! /* |us| can't be bigger than about
! * |original us| + 999 * 1000.
! */
}
! }
! /* Fiddle years (y), months (m), and days (d) so that
! * 1 <= *m <= 12
! * 1 <= *d <= days_in_month(*y, *m)
! * 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_y_m_d(long *y, long *m, long *d)
! {
! int dim; /* # of days in month */
! /* This gets muddy: the proper range for day can't be determined
! * without knowing the correct month and year, but if day is, e.g.,
! * plus or minus a million, the current month and year values make
! * no sense (and may also be out of bounds themselves).
* Saying 12 months == 1 year should be non-controversial.
*/
! if (*m < 1 || *m > 12) {
! --*m;
! normalize_pair(y, m, 12);
! ++*m;
! /* |y| can't be bigger than about
! * |original y| + |original m|/12 now.
! */
}
+ assert(1 <= *m && *m <= 12);
+ assert(INT_MIN < *y && *y < INT_MAX); /* so cast to int is safe */
/* Now only day can be out of bounds (year may also be out of bounds
* for a datetime object, but we don't care about that here).
***************
*** 462,503 ****
* method here is principled and explainable.
*/
! dim = days_in_month(p->year, p->month);
! if (p->day < 1 || p->day > dim) {
/* Move day-1 days from the first of the month. First try to
* get off cheap if we're only one day out of range
* (adjustments for timezone alone can't be worse than that).
*/
! if (p->day == 0) {
! --p->month;
! if (p->month > 0)
! p->day = days_in_month(p->year, p->month);
else {
! --p->year;
! p->month = 12;
! p->day = 31;
}
}
! else if (p->day == dim + 1) {
/* move forward a day */
! ++p->month;
! p->day = 1;
! if (p->month > 12) {
! p->month = 1;
! ++p->year;
}
}
else {
! long ordinal = ymd_to_ord(p->year, p->month, 1) +
! p->day - 1;
! ord_to_ymd(ordinal, &p->year, &p->month, &p->day);
}
}
! assert(p->month > 0);
! assert(p->day > 0);
! return 1;
! Overflow:
! PyErr_SetString(PyExc_OverflowError, msg);
! return 0;
}
--- 521,603 ----
* method here is principled and explainable.
*/
! dim = days_in_month((int)*y, (int)*m);
! if (*d < 1 || *d > dim) {
/* Move day-1 days from the first of the month. First try to
* get off cheap if we're only one day out of range
* (adjustments for timezone alone can't be worse than that).
*/
! if (*d == 0) {
! --*m;
! if (*m > 0)
! *d = days_in_month((int)*y, (int)*m);
else {
! --*y;
! *m = 12;
! *d = 31;
}
}
! else if (*d == dim + 1) {
/* move forward a day */
! ++*m;
! *d = 1;
! if (*m > 12) {
! *m = 1;
! ++*y;
}
}
else {
! long ordinal = ymd_to_ord((int)*y, (int)*m, 1) +
! *d - 1;
! ord_to_ymd(ordinal, y, m, d);
}
}
! assert(*m > 0);
! assert(*d > 0);
! }
! static PyObject *us_per_ms = NULL;
! static PyObject *us_per_second = NULL;
! static PyObject *us_per_minute = NULL;
! static PyObject *us_per_hour = NULL;
! static PyObject *us_per_day = NULL;
! static PyObject *us_per_week = NULL;
!
! static PyObject *seconds_per_day = NULL;
!
! /* Create a date instance with no range checking. */
! static PyObject *
! new_date(int year, int month, int day)
! {
! PyDateTime_Date *self;
!
! 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;
! }
!
! /* Create a datetime instance with no range checking. */
! static PyObject *
! new_datetime(int year, int month, int day, int hour, int minute,
! int second, int usecond)
! {
! PyDateTime_DateTime *self;
!
! 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);
! SET_HOUR(self, hour);
! SET_MINUTE(self, minute);
! SET_SECOND(self, second);
! SET_MICROSECOND(self, usecond);
! }
! return (PyObject *) self;
}
***************
*** 505,539 ****
new_delta(long days, long seconds, long microseconds)
{
! PyDateTime_Delta *self = NULL;
! if (microseconds < 0 || microseconds >= 1000000) {
! long whole_seconds;
! long new_seconds;
! whole_seconds = divmod(microseconds, 1000000, µseconds);
! assert(microseconds >= 0);
! new_seconds = seconds + whole_seconds;
! if (SIGNED_ADD_OVERFLOWED(new_seconds, seconds,
! whole_seconds)) {
! PyErr_SetString(PyExc_OverflowError, "timedelta "
! "seconds component too large");
! goto done;
! }
! seconds = new_seconds;
! }
! if (seconds < 0 || seconds >= 24*3600) {
! long whole_days;
! long new_days;
- whole_days = divmod(seconds, 24*3600, &seconds);
- assert(seconds >= 0);
- new_days = days + whole_days;
- if (SIGNED_ADD_OVERFLOWED(new_days, days, whole_days)) {
- PyErr_SetString(PyExc_OverflowError, "timedelta "
- "days component too large");
- goto done;
- }
- days = new_days;
- }
self = PyObject_New(PyDateTime_Delta, &PyDateTime_DeltaType);
if (self != NULL) {
--- 605,615 ----
new_delta(long days, long seconds, long microseconds)
{
! PyDateTime_Delta *self;
! normalize_d_s_us(&days, &seconds, µseconds);
! if (check_range(days, "timedelta days", PyExc_OverflowError) < 0)
! return NULL;
self = PyObject_New(PyDateTime_Delta, &PyDateTime_DeltaType);
if (self != NULL) {
***************
*** 543,547 ****
SET_TD_MICROSECONDS(self, microseconds);
}
- done:
return (PyObject *) self;
}
--- 619,622 ----
***************
*** 627,630 ****
--- 702,717 ----
assert(DI100Y == 25 * DI4Y - 1);
assert(DI100Y == days_before_year(100+1));
+
+ us_per_ms = PyInt_FromLong(1000);
+ us_per_second = PyInt_FromLong(1000000);
+ us_per_minute = PyInt_FromLong(60000000);
+ seconds_per_day = PyInt_FromLong(24 * 3600);
+
+ /* The rest are too big for 32-bit ints, but even
+ * us_per_week fits in 40 bits, so doubles should be exact.
+ */
+ us_per_hour = PyLong_FromDouble(3600000000.0);
+ us_per_day = PyLong_FromDouble(86400000000.0);
+ us_per_week = PyLong_FromDouble(604800000000.0);
}
Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.66
retrieving revision 1.67
diff -C2 -d -r1.66 -r1.67
*** datetime.py 26 Nov 2002 18:03:19 -0000 1.66
--- datetime.py 1 Dec 2002 19:37:28 -0000 1.67
***************
*** 466,469 ****
--- 466,471 ----
return 1
+ def __hash__(self):
+ return hash((self.__days, self.__seconds, self.__microseconds))
class date(object):
Index: obj_date.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_date.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** obj_date.c 26 Nov 2002 22:51:04 -0000 1.10
--- obj_date.c 1 Dec 2002 19:37:28 -0000 1.11
***************
*** 5,34 ****
/* 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 and > 0 for success. Failure means the adjusted year is out of
! * bounds.
*/
static int
normalize_date(long *year, long *month, long *day)
{
- tmxxx t;
int result;
! TMXXX_CLEAR(&t);
! t.year = *year;
! t.month = *month;
! t.day = *day;
! result = tmxxx_normalize(&t);
! if (result > 0) {
! /* looks good */
! *year = t.year;
! *month = t.month;
! *day = t.day;
!
! if (*year < MINYEAR || *year > MAXYEAR) {
! PyErr_SetString(PyExc_OverflowError,
! "date value out of range");
! result = 0;
! }
}
return result;
--- 5,23 ----
/* 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;
***************
*** 64,71 ****
long int month = GET_MONTH(date);
long int day = GET_DAY(date) + GET_TD_DAYS(delta);
! if (normalize_date(&year, &month, &day))
! result = new_date(year, month, day);
! else
result = NULL;
}
else {
--- 53,60 ----
long int month = GET_MONTH(date);
long int day = GET_DAY(date) + GET_TD_DAYS(delta);
! if (normalize_date(&year, &month, &day) < 0)
result = NULL;
+ else
+ result = new_date(year, month, day);
}
else {
***************
*** 145,149 ****
}
! static int
date_hash(PyDateTime_Date *self)
{
--- 134,138 ----
}
! static long
date_hash(PyDateTime_Date *self)
{
Index: obj_datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_datetime.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** obj_datetime.c 21 Nov 2002 19:47:34 -0000 1.5
--- obj_datetime.c 1 Dec 2002 19:37:28 -0000 1.6
***************
*** 4,26 ****
*/
- static void
- normalize_pair(long int *parent, long int *child, int size)
- {
- if (*child < 0) {
- long int borrow = (*child / size) + 1;
- *parent -= borrow;
- *child += (borrow * size);
- }
- else if (*child >= size) {
- long int carry = *child / size;
- *parent += carry;
- *child -= (carry * size);
- }
- }
-
static int
! normalize_datetime(long int *year, long int *month, long int *day,
! long int *hour, long int *minute, long int *second,
! long int *microsecond)
{
normalize_pair(second, microsecond, 1000000);
--- 4,11 ----
*/
static int
! normalize_datetime(long *year, long *month, long *day,
! long *hour, long *minute, long *second,
! long *microsecond)
{
normalize_pair(second, microsecond, 1000000);
***************
*** 28,32 ****
normalize_pair(hour, minute, 60);
normalize_pair(day, hour, 24);
-
return normalize_date(year, month, day);
}
--- 13,16 ----
***************
*** 35,52 ****
add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta)
{
! long int year = GET_YEAR(date);
! long int month = GET_MONTH(date);
! long int day = GET_DAY(date) + GET_TD_DAYS(delta);
! long int hour = GET_HOUR(date);
! long int minute = GET_MINUTE(date);
! long int second = GET_SECOND(date) + GET_TD_SECONDS(delta);
! long int microsecond = GET_MICROSECOND(date) + GET_TD_MICROSECONDS(delta);
if (normalize_datetime(&year, &month, &day,
! &hour, &minute, &second, µsecond))
return new_datetime(year, month, day,
hour, minute, second, microsecond);
! else
return NULL;
}
--- 19,78 ----
add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta)
{
! long year = GET_YEAR(date);
! long month = GET_MONTH(date);
! long day = GET_DAY(date) + GET_TD_DAYS(delta);
! long hour = GET_HOUR(date);
! long minute = GET_MINUTE(date);
! long second = GET_SECOND(date) + GET_TD_SECONDS(delta);
! long microsecond = GET_MICROSECOND(date) + GET_TD_MICROSECONDS(delta);
if (normalize_datetime(&year, &month, &day,
! &hour, &minute, &second, µsecond) < 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)
! {
! long year = GET_YEAR(date);
! long month = GET_MONTH(date);
! long day = GET_DAY(date) - GET_TD_DAYS(delta);
! long hour = GET_HOUR(date);
! long minute = GET_MINUTE(date);
! long second = GET_SECOND(date) - GET_TD_SECONDS(delta);
! long microsecond = GET_MICROSECOND(date) - GET_TD_MICROSECONDS(delta);
!
! if (normalize_datetime(&year, &month, &day,
! &hour, &minute, &second, µsecond) < 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 seconds1 = (GET_HOUR(left) * 60 + GET_MINUTE(left)) * 60 +
+ GET_SECOND(left);
+ long seconds2 = (GET_HOUR(right) * 60 + GET_MINUTE(right)) * 60 +
+ GET_SECOND(right);
+
+ long delta_us = GET_MICROSECOND(left) - GET_MICROSECOND(right);
+
+ return new_delta(days1 - days2, seconds1 - seconds2, delta_us);
}
***************
*** 72,75 ****
--- 98,129 ----
}
+ 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 - timedelta */
+ result = sub_datetime_timedelta(
+ (PyDateTime_DateTime *)left,
+ (PyDateTime_Delta *)right);
+ }
+ }
+
+ if (result == Py_NotImplemented)
+ Py_INCREF(result);
+ return result;
+ }
+
static int
datetime_compare(PyDateTime_DateTime *self, PyObject *other)
***************
*** 91,120 ****
}
! static int
datetime_hash(PyDateTime_DateTime *self)
{
if (self->hashcode == -1) {
PyObject *temp;
! if (GET_MICROSECOND(self) != 0)
! temp = Py_BuildValue("lllllll", GET_YEAR(self),
! GET_MONTH(self), GET_DAY(self),
! GET_HOUR(self), GET_MINUTE(self),
! GET_SECOND(self), GET_MICROSECOND(self));
! else if (GET_SECOND(self) != 0)
! temp = Py_BuildValue("llllll", GET_YEAR(self),
! GET_MONTH(self), GET_DAY(self),
! GET_HOUR(self), GET_MINUTE(self),
! GET_SECOND(self));
! else if (GET_MINUTE(self) != 0)
! temp = Py_BuildValue("lllll", GET_YEAR(self),
! GET_MONTH(self), GET_DAY(self),
! GET_HOUR(self), GET_MINUTE(self));
! else if (GET_HOUR(self) != 0)
! temp = Py_BuildValue("llll", GET_YEAR(self),
! GET_MONTH(self), GET_DAY(self),
! GET_HOUR(self));
! else
! temp = Py_BuildValue("lll", GET_YEAR(self),
! GET_MONTH(self), GET_DAY(self));
if (temp != NULL) {
self->hashcode = PyObject_Hash(temp);
--- 145,157 ----
}
! 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),
! GET_HOUR(self), GET_MINUTE(self),
! GET_SECOND(self), GET_MICROSECOND(self));
if (temp != NULL) {
self->hashcode = PyObject_Hash(temp);
***************
*** 213,224 ****
return PyString_FromString(buffer);
}
-
- static PyObject *
- datetime_subtract(PyObject *left, PyObject *right)
- {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- }
-
static PyObject *
--- 250,253 ----
Index: obj_delta.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/obj_delta.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** obj_delta.c 26 Nov 2002 05:37:09 -0000 1.7
--- obj_delta.c 1 Dec 2002 19:37:28 -0000 1.8
***************
*** 1,7 ****
! /* imp_delta.c
*
* PyDateTime_Delta implementation.
*/
static PyObject *
--- 1,168 ----
! /* obj_delta.c
*
* PyDateTime_Delta implementation.
*/
+ /* Convert a timedelta to a number of us,
+ * (24*3600*self.days + self.seconds)*1000000 + self.microseconds
+ * as a Python int or long.
+ * Doing mixed-radix arithmetic by hand instead is excruciating in C,
+ * due to ubiquitous overflow possibilities.
+ */
+ static PyObject *
+ delta_to_microseconds(PyDateTime_Delta *self)
+ {
+ PyObject *x1 = NULL;
+ PyObject *x2 = NULL;
+ PyObject *x3 = NULL;
+ PyObject *result = NULL;
+
+ x1 = PyInt_FromLong(GET_TD_DAYS(self));
+ if (x1 == NULL)
+ goto Done;
+ x2 = PyNumber_Multiply(x1, seconds_per_day); /* days in seconds */
+ if (x2 == NULL)
+ goto Done;
+ Py_DECREF(x1);
+ x1 = NULL;
+
+ /* x2 has days in seconds */
+ x1 = PyInt_FromLong(GET_TD_SECONDS(self)); /* seconds */
+ if (x1 == NULL)
+ goto Done;
+ x3 = PyNumber_Add(x1, x2); /* days and seconds in seconds */
+ if (x3 == NULL)
+ goto Done;
+ Py_DECREF(x1);
+ Py_DECREF(x2);
+ x1 = x2 = NULL;
+
+ /* x3 has days+seconds in seconds */
+ x1 = PyNumber_Multiply(x3, us_per_second); /* us */
+ if (x1 == NULL)
+ goto Done;
+ Py_DECREF(x3);
+ x3 = NULL;
+
+ /* x1 has days+seconds in us */
+ x2 = PyInt_FromLong(GET_TD_MICROSECONDS(self));
+ if (x2 == NULL)
+ goto Done;
+ result = PyNumber_Add(x1, x2);
+
+ Done:
+ Py_XDECREF(x1);
+ Py_XDECREF(x2);
+ Py_XDECREF(x3);
+ return result;
+ }
+
+ /* Convert a number of us (as a Python int or long) to a timedelta.
+ */
+ static PyObject *
+ microseconds_to_delta(PyObject *pyus)
+ {
+ long us;
+ long s;
+ long d;
+
+ PyObject *tuple = NULL;
+ PyObject *num = NULL;
+ PyObject *result = NULL;
+
+ tuple = PyNumber_Divmod(pyus, us_per_second);
+ if (tuple == NULL)
+ goto Done;
+
+ num = PyTuple_GetItem(tuple, 1); /* us */
+ if (num == NULL)
+ goto Done;
+ us = PyLong_AsLong(num);
+ num = NULL;
+ if (us < 0) {
+ /* The divisor was positive, so this must be an error. */
+ assert(PyErr_Occurred());
+ goto Done;
+ }
+
+ num = PyTuple_GetItem(tuple, 0); /* leftover seconds */
+ if (num == NULL)
+ goto Done;
+ Py_INCREF(num);
+ Py_DECREF(tuple);
+
+ tuple = PyNumber_Divmod(num, seconds_per_day);
+ if (tuple == NULL)
+ goto Done;
+ Py_DECREF(num);
+
+ num = PyTuple_GetItem(tuple, 1); /* seconds */
+ if (num == NULL)
+ goto Done;
+ s = PyLong_AsLong(num);
+ num = NULL;
+ if (s < 0) {
+ /* The divisor was positive, so this must be an error. */
+ assert(PyErr_Occurred());
+ goto Done;
+ }
+
+ num = PyTuple_GetItem(tuple, 0); /* leftover days */
+ if (num == NULL)
+ goto Done;
+ Py_INCREF(num);
+
+ d = PyLong_AsLong(num);
+ if (d == -1 && PyErr_Occurred())
+ goto Done;
+ result = new_delta(d, s, us);
+
+ Done:
+ Py_XDECREF(tuple);
+ Py_XDECREF(num);
+ return result;
+ }
+
+ static PyObject *
+ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
+ {
+ PyObject *pyus_in;
+ PyObject *pyus_out;
+ PyObject *result;
+
+ pyus_in = delta_to_microseconds(delta);
+ if (pyus_in == NULL)
+ return NULL;
+
+ pyus_out = PyNumber_Multiply(pyus_in, intobj);
+ Py_DECREF(pyus_in);
+ if (pyus_out == NULL)
+ return NULL;
+
+ result = microseconds_to_delta(pyus_out);
+ Py_DECREF(pyus_out);
+ return result;
+ }
+
+ static PyObject *
+ divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj)
+ {
+ PyObject *pyus_in;
+ PyObject *pyus_out;
+ PyObject *result;
+
+ pyus_in = delta_to_microseconds(delta);
+ if (pyus_in == NULL)
+ return NULL;
+
+ pyus_out = PyNumber_FloorDivide(pyus_in, intobj);
+ Py_DECREF(pyus_in);
+ if (pyus_out == NULL)
+ return NULL;
+
+ result = microseconds_to_delta(pyus_out);
+ Py_DECREF(pyus_out);
+ return result;
+ }
static PyObject *
***************
*** 10,50 ****
PyTypeObject *left_type = left->ob_type;
PyTypeObject *right_type = right->ob_type;
- PyDateTime_Delta *delta;
- PyObject *other;
- PyTypeObject *other_type;
! if (PyType_IsSubtype(left_type, &PyDateTime_DeltaType)) {
! /* delta + ??? */
! delta = (PyDateTime_Delta *) left;
! if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
! /* delta + delta */
! /* seconds and microseconds can't overflow, due to
! * the invariant bounds on them.
! */
! const long seconds = GET_TD_SECONDS(delta) +
! GET_TD_SECONDS(right);
! const long microseconds = GET_TD_MICROSECONDS(delta) +
! GET_TD_MICROSECONDS(right);
! /* But days can overflow. */
! const long days1 = GET_TD_DAYS(delta);
! const long days2 = GET_TD_DAYS(right);
! const long days = days1 + days2;
! if (SIGNED_ADD_OVERFLOWED(days, days1, days2)) {
! PyErr_SetString(PyExc_OverflowError,
! "timedelta addition");
! return NULL;
! }
! return new_delta(days, seconds, microseconds);
! }
! /* XXX This code is unused. */
! other = right;
! other_type = right_type;
! }
! else {
! /* !delta + delta */
! /* XXX This code is unused. */
! delta = (PyDateTime_Delta *) right;
! other = left;
! other_type = left_type;
}
Py_INCREF(Py_NotImplemented);
--- 171,186 ----
PyTypeObject *left_type = left->ob_type;
PyTypeObject *right_type = right->ob_type;
! if (PyType_IsSubtype(left_type, &PyDateTime_DeltaType) &&
! PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
! /* delta + delta */
! /* The C-level additions can't overflow because of the
! * invariant bounds.
! */
! long days = GET_TD_DAYS(left) + GET_TD_DAYS(right);
! long seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right);
! long microseconds = GET_TD_MICROSECONDS(left) +
! GET_TD_MICROSECONDS(right);
! return new_delta(days, seconds, microseconds);
}
Py_INCREF(Py_NotImplemented);
***************
*** 113,116 ****
--- 249,253 ----
{
int result = -1;
+
if (!PyObject_TypeCheck(other, &PyDateTime_DeltaType))
PyErr_Format(PyExc_TypeError,
***************
*** 133,149 ****
}
! static int
delta_hash(PyDateTime_Delta *self)
{
! return -2;
! }
!
! static PyObject *
! multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
! {
! long i = PyInt_AS_LONG(intobj);
! return new_delta(GET_TD_DAYS(delta) * i,
! GET_TD_SECONDS(delta) * i,
! GET_TD_MICROSECONDS(delta) * i);
}
--- 270,287 ----
}
! static long
delta_hash(PyDateTime_Delta *self)
{
! if (self->hashcode == -1) {
! PyObject *temp = Py_BuildValue("lll",
! self->days,
! self->seconds,
! self->microseconds);
! if (temp != NULL) {
! self->hashcode = PyObject_Hash(temp);
! Py_DECREF(temp);
! }
! }
! return self->hashcode;
}
***************
*** 151,192 ****
delta_multiply(PyObject *left, PyObject *right)
{
! PyObject *result = NULL;
if (PyType_IsSubtype(left->ob_type, &PyDateTime_DeltaType)) {
/* delta * ??? */
! if (PyInt_Check(right))
result = multiply_int_timedelta(right,
(PyDateTime_Delta *) left);
- else {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
- }
}
! else if (PyInt_Check(left))
result = multiply_int_timedelta(left,
(PyDateTime_Delta *) right);
! else {
! /* !(delta | int) * delta */
! Py_INCREF(Py_NotImplemented);
! return Py_NotImplemented;
}
return result;
}
static PyObject *
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *self = NULL;
- long int days, seconds = 0, microseconds = 0;
! /* XXX We're missing 4 keyword args from the Python version. */
! /* XXX The Python version doesn't require a days argument. */
static char *keywords[] = {
! "days", "seconds", "microseconds", NULL
};
! if (PyArg_ParseTupleAndKeywords(args, kw, "l|ll:__new__", keywords,
! &days, &seconds, µseconds)) {
! self = new_delta(days, seconds, microseconds);
}
return self;
}
--- 289,526 ----
delta_multiply(PyObject *left, PyObject *right)
{
! PyObject *result = Py_NotImplemented;
!
if (PyType_IsSubtype(left->ob_type, &PyDateTime_DeltaType)) {
/* delta * ??? */
! if (PyInt_Check(right) || PyLong_Check(right))
result = multiply_int_timedelta(right,
(PyDateTime_Delta *) left);
}
! else if (PyInt_Check(left) || PyLong_Check(left))
result = multiply_int_timedelta(left,
(PyDateTime_Delta *) right);
!
! if (result == Py_NotImplemented)
! Py_INCREF(result);
! return result;
! }
!
! static PyObject *
! delta_divide(PyObject *left, PyObject *right)
! {
! PyObject *result = Py_NotImplemented;
!
! if (PyType_IsSubtype(left->ob_type, &PyDateTime_DeltaType)) {
! /* delta * ??? */
! if (PyInt_Check(right) || PyLong_Check(right))
! result = divide_timedelta_int(
! (PyDateTime_Delta *)left,
! right);
}
+
+ if (result == Py_NotImplemented)
+ Py_INCREF(result);
return result;
}
+ /* Fold in the value of the tag ("seconds", "weeks", etc) component of a
+ * a timedelta constructor. sofar is the # of microseconds accounted for
+ * so far, and there are factor microseconds per current unit, the number
+ * of which is given by num. num * factor is added to sofar in a
+ * numerically careful way, and that's the result. Any fractional
+ * microseconds left over (this can happen if num is a float type) are
+ * added into *leftover.
+ * If num is NULL, no computation is done, and sofar is returned (after
+ * incremented its refcount).
+ * Note that there are many ways this can give an error (NULL) return.
+ */
+ static PyObject *
+ accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor,
+ double *leftover)
+ {
+ PyObject *prod;
+ PyObject *sum;
+
+ if (num == NULL) {
+ Py_INCREF(sofar);
+ return sofar;
+ }
+
+ if (PyInt_Check(num) || PyLong_Check(num)) {
+ prod = PyNumber_Multiply(num, factor);
+ if (prod == NULL)
+ return NULL;
+ sum = PyNumber_Add(sofar, prod);
+ Py_DECREF(prod);
+ return sum;
+ }
+
+ if (PyFloat_Check(num)) {
+ double dnum;
+ double fracpart;
+ double intpart;
+ PyObject *x;
+ PyObject *y;
+
+ /* The Plan: decompose num into an integer part and a
+ * fractional part, num = intpart + fracpart.
+ * Then num * factor ==
+ * intpart * factor + fracpart * factor
+ * and the LHS can be computed exactly in long arithmetic.
+ * The RHS is again broken into an int part and frac part.
+ * and the frac part is added into *leftover.
+ */
+ dnum = PyFloat_AsDouble(num);
+ if (dnum == -1.0 && PyErr_Occurred())
+ return NULL;
+ fracpart = modf(dnum, &intpart);
+ x = PyLong_FromDouble(intpart);
+ if (x == NULL)
+ return NULL;
+
+ prod = PyNumber_Multiply(x, factor);
+ Py_DECREF(x);
+ if (prod == NULL)
+ return NULL;
+
+ sum = PyNumber_Add(sofar, prod);
+ Py_DECREF(prod);
+ if (sum == NULL)
+ return NULL;
+
+ /* So far we've lost no information. Dealing with the
+ * fractional part requires float arithmetic, and may
+ * lose a little info.
+ */
+ assert(PyInt_Check(factor) || PyLong_Check(factor));
+ if (PyInt_Check(factor))
+ dnum = (double)PyInt_AsLong(factor);
+ else
+ dnum = PyLong_AsDouble(factor);
+
+ dnum *= fracpart;
+ fracpart = modf(dnum, &intpart);
+ x = PyLong_FromDouble(intpart);
+ if (x == NULL) {
+ Py_DECREF(sum);
+ return NULL;
+ }
+
+ y = PyNumber_Add(sum, x);
+ Py_DECREF(sum);
+ Py_DECREF(x);
+ *leftover += fracpart;
+ return y;
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "unsupported type for timedelta %s component: %s",
+ tag, num->ob_type->tp_name);
+ return NULL;
+ }
+
static PyObject *
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *self = NULL;
! /* Argument objects. */
! PyObject *day = NULL;
! PyObject *second = NULL;
! PyObject *us = NULL;
! PyObject *ms = NULL;
! PyObject *minute = NULL;
! PyObject *hour = NULL;
! PyObject *week = NULL;
!
! PyObject *x = NULL;
! PyObject *y = NULL;
! double leftover_us = 0.0;
!
! PyObject *one;
!
static char *keywords[] = {
! "days", "seconds", "microseconds", "milliseconds",
! "minutes", "hours", "weeks", NULL
};
! if (PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOO:__new__",
! keywords,
! &day, &second, &us,
! &ms, &minute, &hour, &week) == 0)
! goto Done;
!
! x = PyInt_FromLong(0);
! if (x == NULL)
! goto Done;
!
! one = PyInt_FromLong(1);
! if (one == NULL)
! goto Done;
! y = accum("microseconds", x, us, one, &leftover_us);
! Py_DECREF(one);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("milliseconds", x, ms, us_per_ms, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("seconds", x, second, us_per_second, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("minutes", x, minute, us_per_minute, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("hours", x, hour, us_per_hour, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("days", x, day, us_per_day, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! y = accum("weeks", x, week, us_per_week, &leftover_us);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
!
! if (leftover_us) {
! PyObject *temp;
! if (leftover_us >= 0.0)
! leftover_us = floor(leftover_us + 0.5);
! else
! leftover_us = ceil(leftover_us - 0.5);
! temp = PyLong_FromDouble(leftover_us);
! if (temp == NULL) {
! Py_DECREF(x);
! goto Done;
! }
! y = PyNumber_Add(x, temp);
! Py_DECREF(temp);
! Py_DECREF(x);
! x = y;
! if (x == NULL)
! goto Done;
}
+
+ self = microseconds_to_delta(x);
+ Py_DECREF(x);
+ Done:
return self;
}
***************
*** 236,252 ****
static PyNumberMethods delta_as_number = {
! delta_add, /* nb_add */
! delta_subtract, /* nb_subtract */
! delta_multiply, /* nb_multiply */
! 0, /* nb_divide */
! 0, /* nb_remainder */
! 0, /* nb_divmod */
! 0, /* nb_power */
! (unaryfunc)delta_negative, /* nb_negative */
! (unaryfunc)delta_positive, /* nb_positive */
! (unaryfunc)delta_abs, /* nb_absolute */
! (inquiry)delta_nonzero, /* nb_nonzero */
};
-
static PyTypeObject PyDateTime_DeltaType = {
PyObject_HEAD_INIT(NULL)
--- 570,612 ----
static PyNumberMethods delta_as_number = {
! delta_add, /* nb_add */
! delta_subtract, /* nb_subtract */
! delta_multiply, /* nb_multiply */
! delta_divide, /* nb_divide */
! 0, /* nb_remainder */
! 0, /* nb_divmod */
! 0, /* nb_power */
! (unaryfunc)delta_negative, /* nb_negative */
! (unaryfunc)delta_positive, /* nb_positive */
! (unaryfunc)delta_abs, /* nb_absolute */
! (inquiry)delta_nonzero, /* nb_nonzero */
! 0, /*nb_invert*/
! 0, /*nb_lshift*/
! 0, /*nb_rshift*/
! 0, /*nb_and*/
! 0, /*nb_xor*/
! 0, /*nb_or*/
! 0, /*nb_coerce*/
! 0, /*nb_int*/
! 0, /*nb_long*/
! 0, /*nb_float*/
! 0, /*nb_oct*/
! 0, /*nb_hex*/
! 0, /*nb_inplace_add*/
! 0, /*nb_inplace_subtract*/
! 0, /*nb_inplace_multiply*/
! 0, /*nb_inplace_divide*/
! 0, /*nb_inplace_remainder*/
! 0, /*nb_inplace_power*/
! 0, /*nb_inplace_lshift*/
! 0, /*nb_inplace_rshift*/
! 0, /*nb_inplace_and*/
! 0, /*nb_inplace_xor*/
! 0, /*nb_inplace_or*/
! delta_divide, /* nb_floor_divide */
! 0, /* nb_true_divide */
! 0, /* nb_inplace_floor_divide */
! 0, /* nb_inplace_true_divide */
};
static PyTypeObject PyDateTime_DeltaType = {
PyObject_HEAD_INIT(NULL)
Index: test_both.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_both.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** test_both.py 26 Nov 2002 21:22:38 -0000 1.10
--- test_both.py 1 Dec 2002 19:37:28 -0000 1.11
***************
*** 55,59 ****
self.assertEqual(a*10, timedelta(70))
self.assertEqual(a*10, 10*a)
- # XXX Next one fails in the C implementation.
self.assertEqual(a*10L, 10*a)
self.assertEqual(b*10, timedelta(0, 600))
--- 55,58 ----
***************
*** 101,104 ****
--- 100,132 ----
eq(td(milliseconds=0.001), td(microseconds=1))
+ def test_carries(self):
+ t1 = timedelta(days=100,
+ weeks=-7,
+ hours=-24*(100-49),
+ minutes=-3,
+ seconds=12,
+ microseconds=(3*60 - 12) * 1000000)
+ t2 = timedelta()
+ self.assertEqual(t1, t2)
+
+ def test_hash_equality(self):
+ t1 = timedelta(days=100,
+ weeks=-7,
+ hours=-24*(100-49),
+ minutes=-3,
+ seconds=12,
+ microseconds=(3*60 - 12) * 1000000)
+ t2 = timedelta()
+ self.assertEqual(hash(t1), hash(t2))
+
+ t1 += timedelta(weeks=7)
+ t2 += timedelta(days=7*7)
+ self.assertEqual(t1, t2)
+ self.assertEqual(hash(t1), hash(t2))
+
+ d = {t1: 1}
+ d[t2] = 2
+ self.assertEqual(len(d), 1)
+
#############################################################################
# date tests
***************
*** 334,338 ****
n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
# n == 315537897599999999 ~= 2**58.13
- # XXX Next line fails in the C implementation.
justasbig = timedelta(0, 0, n)
self.assertEqual(big, justasbig)
--- 362,365 ----
***************
*** 359,367 ****
# datetime tests
! # XXX The Python version of this test class inherits from TestDate. And
! # XXX it should. Trying it here, though, causes 4 new errors when the
! # XXX C implementation is getting tested.
!
! class TestDateTime(unittest.TestCase):
theclass = datetime.datetime
--- 386,390 ----
# datetime tests
! class TestDateTime(TestDate):
theclass = datetime.datetime
***************
*** 415,419 ****
--- 438,580 ----
self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
+ def test_tz_independent_comparing(self):
+ dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
+ dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
+ dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
+ self.assertEqual(dt1, dt3)
+ self.assert_(dt2 > dt3)
+
+ # Make sure comparison doesn't forget microseconds, and isn't done
+ # via comparing a float timestamp (an IEEE double doesn't have enough
+ # precision to span microsecond resolution across years 1 thru 9999,
+ # so comparing via timestamp necessarily calls some distinct values
+ # equal).
+ dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
+ us = timedelta(microseconds=1)
+ dt2 = dt1 + us
+ self.assertEqual(dt2 - dt1, us)
+ self.assert_(dt1 < dt2)
+ def test_bad_constructor_arguments(self):
+ # bad years
+ self.theclass(MINYEAR, 1, 1) # no exception
+ self.theclass(MAXYEAR, 1, 1) # no exception
+ self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
+ self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
+ # bad months
+ self.theclass(2000, 1, 1) # no exception
+ self.theclass(2000, 12, 1) # no exception
+ self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
+ self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
+ # bad days
+ self.theclass(2000, 2, 29) # no exception
+ self.theclass(2004, 2, 29) # no exception
+ self.theclass(2400, 2, 29) # no exception
+ self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
+ self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
+ self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
+ self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
+ # bad hours
+ self.theclass(2000, 1, 31, 0) # no exception
+ self.theclass(2000, 1, 31, 23) # no exception
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
+ # bad minutes
+ self.theclass(2000, 1, 31, 23, 0) # no exception
+ self.theclass(2000, 1, 31, 23, 59) # no exception
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
+ # bad seconds
+ self.theclass(2000, 1, 31, 23, 59, 0) # no exception
+ self.theclass(2000, 1, 31, 23, 59, 59) # no exception
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
+ self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
+ # bad microseconds
+ self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
+ self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
+ self.assertRaises(ValueError, self.theclass,
+ 2000, 1, 31, 23, 59, 59, -1)
+ self.assertRaises(ValueError, self.theclass,
+ 2000, 1, 31, 23, 59, 59,
+ 1000000)
+
+ def test_hash_equality(self):
+ d = self.theclass(2000, 12, 31, 23, 30, 17)
+ e = self.theclass(2000, 12, 31, 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(2001, 1, 1, 0, 5, 17)
+ e = self.theclass(2001, 1, 1, 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_computations(self):
+ a = self.theclass(2002, 1, 31)
+ b = self.theclass(1956, 1, 31)
+ diff = a-b
+ self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
+ self.assertEqual(diff.seconds, 0)
+ self.assertEqual(diff.microseconds, 0)
+ a = self.theclass(2002, 3, 2, 17, 6)
+ millisec = timedelta(0, 0, 1000)
+ hour = timedelta(0, 3600)
+ day = timedelta(1)
+ week = timedelta(7)
+ self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
+ self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
+ self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
+ self.assertEqual(a - hour, a + -hour)
+ self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
+ self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
+ self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
+ self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
+ self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
+ self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
+ self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
+ self.assertEqual((a + week) - a, week)
+ self.assertEqual((a + day) - a, day)
+ self.assertEqual((a + hour) - a, hour)
+ self.assertEqual((a + millisec) - a, millisec)
+ self.assertEqual((a - week) - a, -week)
+ self.assertEqual((a - day) - a, -day)
+ self.assertEqual((a - hour) - a, -hour)
+ self.assertEqual((a - millisec) - a, -millisec)
+ self.assertEqual(a - (a + week), -week)
+ self.assertEqual(a - (a + day), -day)
+ self.assertEqual(a - (a + hour), -hour)
+ self.assertEqual(a - (a + millisec), -millisec)
+ self.assertEqual(a - (a - week), week)
+ self.assertEqual(a - (a - day), day)
+ self.assertEqual(a - (a - hour), hour)
+ self.assertEqual(a - (a - millisec), millisec)
+ self.assertEqual(a + (week + day + hour + millisec),
+ self.theclass(2002, 3, 10, 18, 6, 0, 1000))
+ self.assertEqual(a + (week + day + hour + millisec),
+ (((a + week) + day) + hour) + millisec)
+ self.assertEqual(a - (week + day + hour + millisec),
+ self.theclass(2002, 2, 22, 16, 5, 59, 999000))
+ self.assertEqual(a - (week + day + hour + millisec),
+ (((a - week) - day) - hour) - millisec)
+ # Add/sub ints, longs, floats should be illegal
+ for i in 1, 1L, 1.0:
+ self.assertRaises(TypeError, lambda: a+i)
+ self.assertRaises(TypeError, lambda: a-i)
+ self.assertRaises(TypeError, lambda: i+a)
+ self.assertRaises(TypeError, lambda: i-a)
def test_suite():
Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.52
retrieving revision 1.53
diff -C2 -d -r1.52 -r1.53
*** test_datetime.py 26 Nov 2002 21:22:38 -0000 1.52
--- test_datetime.py 1 Dec 2002 19:37:28 -0000 1.53
***************
*** 215,358 ****
theclass = datetime
- def test_tz_independent_comparing(self):
- dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
- dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
- dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
- self.assertEqual(dt1, dt3)
- self.assert_(dt2 > dt3)
-
- # Make sure comparison doesn't forget microseconds, and isn't done
- # via comparing a float timestamp (an IEEE double doesn't have enough
- # precision to span microsecond resolution across years 1 thru 9999,
- # so comparing via timestamp necessarily calls some distinct values
- # equal).
- dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
- us = timedelta(microseconds=1)
- dt2 = dt1 + us
- self.assertEqual(dt2 - dt1, us)
- self.assert_(dt1 < dt2)
-
- def test_bad_constructor_arguments(self):
- # bad years
- self.theclass(MINYEAR, 1, 1) # no exception
- self.theclass(MAXYEAR, 1, 1) # no exception
- self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
- self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
- # bad months
- self.theclass(2000, 1, 1) # no exception
- self.theclass(2000, 12, 1) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
- self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
- # bad days
- self.theclass(2000, 2, 29) # no exception
- self.theclass(2004, 2, 29) # no exception
- self.theclass(2400, 2, 29) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
- self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
- self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
- # bad hours
- self.theclass(2000, 1, 31, 0) # no exception
- self.theclass(2000, 1, 31, 23) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
- # bad minutes
- self.theclass(2000, 1, 31, 23, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
- # bad seconds
- self.theclass(2000, 1, 31, 23, 59, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
- # bad microseconds
- self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
- self.assertRaises(ValueError, self.theclass,
- 2000, 1, 31, 23, 59, 59, -1)
- self.assertRaises(ValueError, self.theclass,
- 2000, 1, 31, 23, 59, 59,
- 1000000)
-
- def test_hash_equality(self):
- d = self.theclass(2000, 12, 31, 23, 30, 17)
- e = self.theclass(2000, 12, 31, 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(2001, 1, 1, 0, 5, 17)
- e = self.theclass(2001, 1, 1, 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_computations(self):
- a = self.theclass(2002, 1, 31)
- b = self.theclass(1956, 1, 31)
- diff = a-b
- self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
- self.assertEqual(diff.seconds, 0)
- self.assertEqual(diff.microseconds, 0)
- a = self.theclass(2002, 3, 2, 17, 6)
- millisec = timedelta(0, 0, 1000)
- hour = timedelta(0, 3600)
- day = timedelta(1)
- week = timedelta(7)
- self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
- self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
- self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
- self.assertEqual(a - hour, a + -hour)
- self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
- self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
- self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
- self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
- self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
- self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
- self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
- self.assertEqual((a + week) - a, week)
- self.assertEqual((a + day) - a, day)
- self.assertEqual((a + hour) - a, hour)
- self.assertEqual((a + millisec) - a, millisec)
- self.assertEqual((a - week) - a, -week)
- self.assertEqual((a - day) - a, -day)
- self.assertEqual((a - hour) - a, -hour)
- self.assertEqual((a - millisec) - a, -millisec)
- self.assertEqual(a - (a + week), -week)
- self.assertEqual(a - (a + day), -day)
- self.assertEqual(a - (a + hour), -hour)
- self.assertEqual(a - (a + millisec), -millisec)
- self.assertEqual(a - (a - week), week)
- self.assertEqual(a - (a - day), day)
- self.assertEqual(a - (a - hour), hour)
- self.assertEqual(a - (a - millisec), millisec)
- self.assertEqual(a + (week + day + hour + millisec),
- self.theclass(2002, 3, 10, 18, 6, 0, 1000))
- self.assertEqual(a + (week + day + hour + millisec),
- (((a + week) + day) + hour) + millisec)
- self.assertEqual(a - (week + day + hour + millisec),
- self.theclass(2002, 2, 22, 16, 5, 59, 999000))
- self.assertEqual(a - (week + day + hour + millisec),
- (((a - week) - day) - hour) - millisec)
- # Add/sub ints, longs, floats should be illegal
- for i in 1, 1L, 1.0:
- self.assertRaises(TypeError, lambda: a+i)
- self.assertRaises(TypeError, lambda: a-i)
- self.assertRaises(TypeError, lambda: i+a)
- self.assertRaises(TypeError, lambda: i-a)
-
def test_tmxxx(self):
from datetime import tmxxx
--- 215,218 ----
--- test_cdatetime.py DELETED ---