[Python-checkins] python/nondist/sandbox/datetime obj_date.c,NONE,1.1 obj_datetime.c,NONE,1.1 obj_delta.c,NONE,1.1 Makefile,1.4,1.5 datetime.c,1.11,1.12 datetime.h,1.4,1.5 setup.py,1.1,1.2 test_cdatetime.py,1.4,1.5

fdrake@users.sourceforge.net fdrake@users.sourceforge.net
Tue, 20 Aug 2002 11:53:10 -0700


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

Modified Files:
	Makefile datetime.c datetime.h setup.py test_cdatetime.py 
Added Files:
	obj_date.c obj_datetime.c obj_delta.c 
Log Message:
Many updates to the datetime module:
- now implements both the date and timedelta types
- starts to add the datetime arithmetic support (very partial)
- passes many more tests

The implementation has been split into several files (1 per type); they
will all be merged when the initial implementation is complete, prior to
being merged into Python.


--- NEW FILE: obj_date.c ---
/* imp_date.c
 *
 * PyDateTime_Date implementation.
 */

static int
normalize_date(long int *year, long int *month, long int *day)
{
    long int carry, dim;

    /* This is 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 (*month > 12) {
        carry = (*month - 1) / 12;
        *month -= (carry * 12);
        *year += carry;
        assert(*month >= 1);
        assert(*month <= 12);
    }
    /* 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).  If day is out of bounds, what to do is arguable, but at
     * least the method here is principled and explainable.
     */
    dim = days_in_month(*year, *month);
    if (*day < 1 || *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 (*day == 0) {
            *month -= 1;
            if (*month > 0)
                *day = days_in_month(*year, *month);
            else {
                *year -= 1;
                *month = 12;
                *day = 31;
            }
        }
        else if (*day == dim + 1) {
            /* move forward a day */
            *month += 1;
            *day = 1;
            if (*month > 12) {
                *month = 1;
                *year += 1;
            }
        }
        else {
            long int ordinal = ymd_to_ord(*year, *month, 1) + *day - 1;
            ord_to_ymd(ordinal, year, month, day);
        }
    }
    assert(*month > 0);
    assert(*day > 0);
    if (*year < MINYEAR) {
        PyErr_SetString(PyExc_OverflowError, "date value out of range");
        return 0;
    }
    return 1;
}

static PyObject *
datetime_add(PyObject *left, PyObject *right);

static PyObject *
datetime_subtract(PyObject *left, PyObject *right);

/* date(2009, 12, 28) + timedelta(4) == 2010-01-01 (2010, 1, -2)
   expected (2009, 53, 5)
*/
static PyObject *
add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta)
{
    PyObject *result;
    /* delta + date */
    if (GET_TD_SECONDS(delta) != 0
        || GET_TD_MICROSECONDS(delta) != 0) {
        /* Convert to datetime and pass it off. */
        PyObject *dt = new_datetime(GET_YEAR(date), GET_MONTH(date),
                                    GET_DAY(date), 0, 0, 0, 0);
        if (dt == NULL)
            return NULL;
        result = datetime_add((PyObject *)delta, dt);
        Py_DECREF(dt);
    }
    else if (GET_TD_DAYS(delta) != 0) {
        /* There's actually something to do. */
        long int year = GET_YEAR(date);
        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 {
        /* The delta is timedelta(0), so return the original date. */
        Py_INCREF(date);
        result = (PyObject *) date;
    }
    return result;
}

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

    if (PyType_IsSubtype(left_type, &PyDateTime_DateTimeType)
        || PyType_IsSubtype(right_type, &PyDateTime_DateTimeType)) {
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
    }
    if (PyType_IsSubtype(left_type, &PyDateTime_DateType)) {
        /* date + ??? */
        if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType))
            return add_date_timedelta((PyDateTime_Date *) left,
                                      (PyDateTime_Delta *) right);
    }
    else {
        /* 'right' must be one of us, or we wouldn't have been called */
        if (PyType_IsSubtype(left_type, &PyDateTime_DeltaType))
            return add_date_timedelta((PyDateTime_Date *) right,
                                      (PyDateTime_Delta *) left);
        /* else fall through; we don't support it here */
    }
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

static int
date_compare(PyDateTime_Date *self, PyObject *other)
{
    if (!PyType_IsSubtype(other->ob_type, &PyDateTime_DateType)) {
        PyErr_SetString(PyExc_TypeError,
                        "can't compare date to %s instance");
        return -1;
    }
    return memcmp(self->data, ((PyDateTime_Date *)other)->data,
                  _PyDateTime_DATE_DATA_SIZE);
}

static PyObject *
date_ctime(PyDateTime_Date *self)
{
    return format_ctime(self, 0, 0, 0);
}

static int
date_hash(PyDateTime_Date *self)
{
    if (self->hashcode == -1) {
        PyObject *temp = Py_BuildValue("lll", GET_YEAR(self),
                                       GET_MONTH(self), GET_DAY(self));
        if (temp != NULL) {
            self->hashcode = PyObject_Hash(temp);
            Py_DECREF(temp);
        }
    }
    return self->hashcode;
}

static PyObject *
date_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
    PyObject *self = NULL;
    long int year, month, day;

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

    if (PyArg_ParseTupleAndKeywords(args, kw, "lll", keywords,
                                    &year, &month, &day)) {
        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;
        }
        self = new_date(year, month, day);
    }
    return self;
}

static PyObject *
date_year(PyDateTime_Date *self, void *unused)
{
    return (PyInt_FromLong(GET_YEAR(self)));
}

static PyObject *
date_month(PyDateTime_Date *self, void *unused)
{
    return (PyInt_FromLong(GET_MONTH(self)));
}

static PyObject *
date_day(PyDateTime_Date *self, void *unused)
{
    return (PyInt_FromLong(GET_DAY(self)));
}

static PyGetSetDef date_getset[] = {
    {"year",        (getter)date_year},
    {"month",       (getter)date_month},
    {"day",         (getter)date_day},
    {NULL}
};

static PyObject *
date_isocalendar(PyDateTime_Date *self)
{
    int  year         = GET_YEAR(self);
    int  week1_monday = iso_week1_monday(year);
    long today        = ymd_to_ord(year, GET_MONTH(self), GET_DAY(self));
    int  week         = (today - week1_monday) / 7;
    int  day          = (today - week1_monday) % 7;

    if (week < 0) {
        --year;
        week1_monday = iso_week1_monday(year);
        week         = (today - week1_monday) / 7;
        day          = (today - week1_monday) % 7;
    }
    else if (week >= 52 &&
             today >= iso_week1_monday(year + 1)) {
        ++year;
        week = 0;
    }
    return Py_BuildValue("iii", year, week + 1, day + 1);
}

static PyObject *
date_isoformat(PyDateTime_Date *self, PyObject *args, PyObject *kw)
{
    char buffer[128];

    isoformat_date(self, buffer, sizeof(buffer));

    return PyString_FromString(buffer);
}

static PyObject *
date_isoweekday(PyDateTime_Date *self)
{
    int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));

    return PyInt_FromLong(dow + 1);
}

static int
date_nonzero(PyDateTime_Date *self)
{
    return (GET_YEAR(self) != 0
            || GET_MONTH(self) != 0
            || GET_DAY(self) != 0);
}

static PyObject *
date_repr(PyDateTime_Date *self)
{
    char buffer[1028];
    char *typename;

    typename = self->ob_type->tp_name;
    PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d)",
                  typename,
                  GET_YEAR(self), GET_MONTH(self), GET_DAY(self));

    return PyString_FromString(buffer);
}

static PyObject *
date_str(PyDateTime_Date *self)
{
    char buffer[128];

    isoformat_date(self, buffer, sizeof(buffer));

    return PyString_FromString(buffer);
}

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

    if (PyType_IsSubtype(left_type, &PyDateTime_DateTimeType)
        || PyType_IsSubtype(right_type, &PyDateTime_DateTimeType)) {
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
    }
    if (PyType_IsSubtype(left_type, &PyDateTime_DateType)) {
        if (PyType_IsSubtype(right_type, &PyDateTime_DateType)) {
            /* date - date */
            long int left_ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left),
                                           GET_DAY(left));
            long int right_ord = ymd_to_ord(GET_YEAR(right), GET_MONTH(right),
                                            GET_DAY(right));
            return new_delta(left_ord - right_ord, 0, 0);
        }
        if (PyType_IsSubtype(right_type, &PyDateTime_DeltaType)) {
            PyObject *result = NULL;
            if (GET_TD_SECONDS(right) != 0
                || GET_TD_MICROSECONDS(right) != 0) {
                /* need to convert to datetime + delta */
                PyObject *dt = new_datetime(GET_YEAR(left), GET_MONTH(left),
                                            GET_DAY(left), 0, 0, 0, 0);
                if (dt != NULL) {
                    result = datetime_subtract(dt, right);
                    Py_DECREF(dt);
                }
            }
            else if (GET_TD_DAYS(right) == 0) {
                /* date - timedelta(0) */
                Py_INCREF(left);
                result = left;
            }
            else {
                long int year, month, day;
                long int ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left),
                                          GET_DAY(left));
                ord -= GET_TD_DAYS(right);
                if (ord < 1)
                    PyErr_SetString(PyExc_OverflowError,
                                    "resulting value out of range");
                else {
                    ord_to_ymd(ord, &year, &month, &day);
                    result = new_date(year, month, day);
                }
            }
            return result;
        }
    }
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

static PyObject *
date_today(PyObject *self, PyObject *cls)
{
    /* 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, "iii",
                                 tm->tm_year + 1900, tm->tm_mon + 1,
                                 tm->tm_mday);
}

static PyObject *
date_toordinal(PyDateTime_Date *self)
{
    return PyInt_FromLong(ymd_to_ord(GET_YEAR(self), GET_MONTH(self),
                                     GET_DAY(self)));
}

static PyObject *
date_weekday(PyDateTime_Date *self)
{
    int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));

    return PyInt_FromLong(dow);
}

static PyMethodDef date_methods[] = {
    /* Class methods: */
    {"today",         (PyCFunction)date_today,       METH_O | METH_CLASS,
     "Return a new date that represents the current date."},

    /* Instance methods: */
    {"ctime",       (PyCFunction)date_ctime,         METH_NOARGS,
     "Return ctime() style string."},
    {"isocalendar", (PyCFunction)date_isocalendar,   METH_NOARGS,
     "Return a 3-tuple containing ISO year, week number, and weekday.\n\n"
     "The first ISO week of the year is the (Mon-Sun) week containing the\n"
     "year's first Thursday; everything rest derives from that."},
    {"isoformat",   (PyCFunction)date_isoformat,     METH_KEYWORDS,
     "Return the day of the week represented by the date.\n"
     "Monday == 1 ... Sunday == 7"},
    {"isoweekday",  (PyCFunction)date_isoweekday,    METH_NOARGS,
     "Return the day of the week represented by the date.\n"
     "Monday == 1 ... Sunday == 7"},
    {"toordinal",   (PyCFunction)date_toordinal,     METH_NOARGS,
     "Return proleptic Gregorian ordinal for the year, month and day.\n"
     "January 1 of year 1 is day 1."},
    {"weekday",     (PyCFunction)date_weekday,       METH_NOARGS,
     "Return the day of the week represented by the date.\n"
     "Monday == 0 ... Sunday == 6"},
    {NULL}
};

static char date_doc[] =
"Basic date type.";

static PyNumberMethods date_as_number = {
    date_add,					/* nb_add */
    date_subtract,				/* nb_subtract */
    0,						/* nb_multiply */
    0,						/* nb_divide */
    0,						/* nb_remainder */
    0,						/* nb_divmod */
    0,						/* nb_power */
    0,						/* nb_negative */
    0,						/* nb_positive */
    0,						/* nb_absolute */
    (inquiry)date_nonzero,			/* nb_nonzero */
};

static PyTypeObject PyDateTime_DateType = {
    PyObject_HEAD_INIT(NULL)
    0,						/* ob_size */
    "date",					/* tp_name */
    sizeof(PyDateTime_Date),			/* tp_basicsize */
    0,						/* tp_itemsize */
    (destructor)PyObject_Del,			/* tp_dealloc */
    0,						/* tp_print */
    0,						/* tp_getattr */
    0,						/* tp_setattr */
    (cmpfunc)date_compare,			/* tp_compare */
    (reprfunc)date_repr,			/* tp_repr */
    &date_as_number,				/* tp_as_number */
    0,						/* tp_as_sequence */
    0,						/* tp_as_mapping */
    (hashfunc)date_hash,			/* tp_hash */
    0,              				/* tp_call */
    (reprfunc)date_str,				/* tp_str */
    PyObject_GenericGetAttr,			/* tp_getattro */
    0,						/* tp_setattro */
    0,						/* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
        Py_TPFLAGS_BASETYPE,			/* tp_flags */
    date_doc,					/* tp_doc */
    0,						/* tp_traverse */
    0,						/* tp_clear */
    0,						/* tp_richcompare */
    0,						/* tp_weaklistoffset */
    0,						/* tp_iter */
    0,						/* tp_iternext */
    date_methods,				/* tp_methods */
    0,						/* tp_members */
    date_getset,				/* tp_getset */
    0,						/* tp_base */
    0,						/* tp_dict */
    0,						/* tp_descr_get */
    0,						/* tp_descr_set */
    0,						/* tp_dictoffset */
    0,						/* tp_init */
    0,						/* tp_alloc */
    date_new,					/* tp_new */
    _PyObject_Del,				/* tp_free */
};

--- NEW FILE: obj_datetime.c ---
/* imp_datetime.c
 *
 * PyDateTime_DateTime implementation.
 */

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);
    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)
{
    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, &microsecond))
        return new_datetime(year, month, day,
                            hour, minute, second, microsecond);
    else
        return NULL;
}

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))
            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 int
datetime_compare(PyDateTime_DateTime *self, PyObject *other)
{
    if (!PyType_IsSubtype(other->ob_type, &PyDateTime_DateTimeType)) {
        PyErr_SetString(PyExc_TypeError,
                        "can't compare date to %s instance");
        return -1;
    }
    return memcmp(self->data, ((PyDateTime_DateTime *)other)->data,
                  _PyDateTime_DATETIME_DATA_SIZE);
}

static PyObject *
datetime_ctime(PyDateTime_DateTime *self)
{
    return format_ctime((PyDateTime_Date *)self,
                        GET_HOUR(self), GET_MINUTE(self), GET_SECOND(self));
}

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);
            Py_DECREF(temp);
        }
    }
    return self->hashcode;
}

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

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

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

static PyObject *
datetime_repr(PyDateTime_DateTime *self)
{
    char buffer[1028];
    char *typename;

    typename = self->ob_type->tp_name;
    if (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),
                      GET_HOUR(self), GET_MINUTE(self), GET_SECOND(self),
                      GET_MICROSECOND(self));
    }
    else if (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),
                      GET_HOUR(self), GET_MINUTE(self), 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),
                      GET_HOUR(self), GET_MINUTE(self));
    }
    return PyString_FromString(buffer);
}

static PyObject *
datetime_str(PyDateTime_DateTime *self)
{
    char buffer[128];
    char *cp;

    cp = isoformat_date((PyDateTime_Date *)self, buffer, sizeof(buffer));
    *cp++ = ' ';
    isoformat_time(self, cp, sizeof(buffer) - (cp - buffer));

    return PyString_FromString(buffer);
}

static PyObject *
datetime_subtract(PyObject *left, PyObject *right)
{
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}


static PyObject *
datetime_hour(PyDateTime_DateTime *self, void *unused)
{
    return (PyInt_FromLong(GET_HOUR(self)));
}

static PyObject *
datetime_minute(PyDateTime_DateTime *self, void *unused)
{
    return (PyInt_FromLong(GET_MINUTE(self)));
}

static PyObject *
datetime_second(PyDateTime_DateTime *self, void *unused)
{
    return (PyInt_FromLong(GET_SECOND(self)));
}

static PyObject *
datetime_microsecond(PyDateTime_DateTime *self, void *unused)
{
    return (PyInt_FromLong(GET_MICROSECOND(self)));
}

static PyGetSetDef datetime_getset[] = {
    {"hour",        (getter)datetime_hour},
    {"minute",      (getter)datetime_minute},
    {"second",      (getter)datetime_second},
    {"microsecond", (getter)datetime_microsecond},
    {NULL}
};

static PyObject *
datetime_isoformat(PyDateTime_DateTime *self,
                   PyObject *args, PyObject *kw)
{
    char buffer[128];
    char sep = 'T';
    char *cp;

    static char *keywords[] = {"sep", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kw, "|c:isoformat", keywords, &sep))
        return NULL;

    cp = isoformat_date((PyDateTime_Date *)self, buffer, sizeof(buffer));
    assert(cp != NULL);
    *cp++ = sep;
    isoformat_time(self, cp, sizeof(buffer) - (cp - buffer));

    return PyString_FromString(buffer);
}

static int
datetime_nonzero(PyDateTime_DateTime *self)
{
    return (GET_MICROSECOND(self) != 0
            || GET_SECOND(self) != 0
            || GET_MINUTE(self) != 0
            || GET_HOUR(self) != 0
            || date_nonzero((PyDateTime_Date *) self) != 0);
}

static PyObject *
datetime_now(PyObject *self, PyObject *cls)
{
    /* 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);
}

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 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, 0, 0, 0, 0);
}

static PyMethodDef datetime_methods[] = {
    /* Class methods: */
    {"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."},

    /* Instance methods: */
    {"ctime",       (PyCFunction)datetime_ctime,         METH_NOARGS,
     "Return ctime() style string."},
    {"isoformat",   (PyCFunction)datetime_isoformat,     METH_KEYWORDS,
     "Return the day of the week represented by the datetime.\n"
     "Monday == 1 ... Sunday == 7"},
    {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 */
    0,						/* nb_remainder */
    0,						/* nb_divmod */
    0,						/* nb_power */
    0,						/* nb_negative */
    0,						/* nb_positive */
    0,						/* nb_absolute */
    (inquiry)datetime_nonzero,			/* nb_nonzero */
};

statichere PyTypeObject PyDateTime_DateTimeType = {
    PyObject_HEAD_INIT(NULL)
    0,						/* ob_size */
    "datetime",					/* tp_name */
    sizeof(PyDateTime_DateTime),		/* tp_basicsize */
    0,						/* tp_itemsize */
    (destructor)PyObject_Del,			/* tp_dealloc */
    0,						/* tp_print */
    0,						/* tp_getattr */
    0,						/* tp_setattr */
    (cmpfunc)datetime_compare,			/* 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 */
    0,						/* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
        Py_TPFLAGS_BASETYPE,			/* tp_flags */
    datetime_doc,				/* tp_doc */
    0,						/* tp_traverse */
    0,						/* tp_clear */
    0,						/* 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 */
    0,						/* tp_descr_set */
    0,						/* tp_dictoffset */
    0,						/* tp_init */
    0,						/* tp_alloc */
    datetime_new,				/* tp_new */
    _PyObject_Del,				/* tp_free */
};

--- NEW FILE: obj_delta.c ---
/* imp_delta.c
 *
 * PyDateTime_Delta implementation.
 */


static PyObject *
delta_add(PyObject *left, PyObject *right)
{
    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 */
            long int days = GET_TD_DAYS(delta) + GET_TD_DAYS(right);
            long int seconds = GET_TD_SECONDS(delta) + GET_TD_SECONDS(right);
            long int microseconds = (GET_TD_MICROSECONDS(delta)
                                     + GET_TD_MICROSECONDS(right));
            return new_delta(days, seconds, microseconds);
        }
        other = right;
        other_type = right_type;
    }
    else {
        /* !delta + delta */
        delta = (PyDateTime_Delta *) right;
        other = left;
        other_type = left_type;
    }
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

static int
delta_compare(PyDateTime_Delta *self, PyObject *other)
{
    int result = -1;
    if (!PyObject_TypeCheck(other, &PyDateTime_DeltaType))
        PyErr_Format(PyExc_TypeError,
                     "can't compare %s to %s instance",
                     self->ob_type->tp_name, other->ob_type->tp_name);
    else {
        long diff = GET_TD_DAYS(self) - GET_TD_DAYS(other);
        if (diff == 0) {
            diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other);
            if (diff == 0)
                diff = GET_TD_MICROSECONDS(self) - GET_TD_MICROSECONDS(other);
        }
        if (diff == 0)
            result = 0;
        else if (diff > 0)
            result = 1;
    }
    return result;
}

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);
}

static PyObject *
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_negative(PyDateTime_Delta *self)
{
    return new_delta(-GET_TD_DAYS(self),
                     -GET_TD_SECONDS(self),
                     -GET_TD_MICROSECONDS(self));
}

static PyObject *
delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
    PyObject *self = NULL;
    long int days, seconds = 0, microseconds = 0;

    static char *keywords[] = {
        "days", "seconds", "microseconds", NULL
    };

    if (PyArg_ParseTupleAndKeywords(args, kw, "l|ll:__new__", keywords,
                                    &days, &seconds, &microseconds)) {
        if (seconds < 0 || seconds >= (24 * 3600)) {
            PyErr_SetString(PyExc_ValueError,
                            "seconds must be in 0..86399");
            return NULL;
        }
        if (microseconds < 0 || microseconds >= 1000000) {
            PyErr_SetString(PyExc_ValueError,
                            "microseconds must be in 0..999999");
            return NULL;
        }
        self = new_delta(days, seconds, microseconds);
    }
    return self;
}

static PyObject *
delta_subtract(PyObject *left, PyObject *right)
{
    Py_INCREF(Py_NotImplemented);
    return Py_NotImplemented;
}

static int
delta_nonzero(PyDateTime_Delta *self)
{
    return (GET_TD_DAYS(self) != 0
            || GET_TD_SECONDS(self) != 0
            || GET_TD_MICROSECONDS(self) != 0);
}

static PyObject *
delta_repr(PyDateTime_Delta *self)
{
    if (GET_TD_MICROSECONDS(self) != 0)
        return PyString_FromFormat("%s(%ld, %ld, %ld)",
                                   self->ob_type->tp_name,
                                   GET_TD_DAYS(self),
                                   GET_TD_SECONDS(self),
                                   GET_TD_MICROSECONDS(self));
    if (GET_TD_SECONDS(self) != 0)
        return PyString_FromFormat("%s(%ld, %ld)",
                                   self->ob_type->tp_name,
                                   GET_TD_DAYS(self),
                                   GET_TD_SECONDS(self));

    return PyString_FromFormat("%s(%ld)",
                               self->ob_type->tp_name,
                               GET_TD_DAYS(self));
}

#define OFFSET(field)  offsetof(PyDateTime_Delta, field)

static PyMemberDef delta_members[] = {
    {"days",         T_LONG, OFFSET(days),         READONLY,
     "Number os days."},
    {"seconds",      T_LONG, OFFSET(seconds),      READONLY,
     "Number of seconds (less than 1 day)."},
    {"microseconds", T_LONG, OFFSET(microseconds), READONLY,
     "Number of microseconds (less than 1 second)."},
    {NULL}
};

static char delta_doc[] =
"Difference between two datetime values.";

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 */
    0,						/* nb_positive */
    0,						/* nb_absolute */
    (inquiry)delta_nonzero,			/* nb_nonzero */
};

static PyTypeObject PyDateTime_DeltaType = {
    PyObject_HEAD_INIT(NULL)
    0,						/* ob_size */
    "timedelta",				/* tp_name */
    sizeof(PyDateTime_Delta),			/* tp_basicsize */
    0,						/* tp_itemsize */
    0,						/* tp_dealloc */
    0,						/* tp_print */
    0,						/* tp_getattr */
    0,						/* tp_setattr */
    (cmpfunc)delta_compare,			/* tp_compare */
    (reprfunc)delta_repr,			/* tp_repr */
    &delta_as_number,				/* tp_as_number */
    0,						/* tp_as_sequence */
    0,						/* tp_as_mapping */
    (hashfunc)delta_hash,			/* tp_hash */
    0,              				/* tp_call */
    0,						/* tp_str */
    PyObject_GenericGetAttr,			/* tp_getattro */
    0,						/* tp_setattro */
    0,						/* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,	/* tp_flags */
    delta_doc,					/* tp_doc */
    0,						/* tp_traverse */
    0,						/* tp_clear */
    0,						/* tp_richcompare */
    0,						/* tp_weaklistoffset */
    0,						/* tp_iter */
    0,						/* tp_iternext */
    0,						/* tp_methods */
    delta_members,				/* tp_members */
    0,						/* tp_getset */
    0,						/* tp_base */
    0,						/* tp_dict */
    0,						/* tp_descr_get */
    0,						/* tp_descr_set */
    0,						/* tp_dictoffset */
    0,						/* tp_init */
    0,						/* tp_alloc */
    delta_new,					/* tp_new */
    _PyObject_Del,				/* tp_free */
};

Index: Makefile
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/Makefile,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** Makefile	28 Mar 2002 21:17:47 -0000	1.4
--- Makefile	20 Aug 2002 18:53:07 -0000	1.5
***************
*** 1,3 ****
! PYTHON=python2.3
  
  default: check
--- 1,4 ----
! #PYTHON=python2.3
! PYTHON=../../../trunk/debug/python
  
  default: check

Index: datetime.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -d -r1.11 -r1.12
*** datetime.c	28 Mar 2002 21:17:47 -0000	1.11
--- datetime.c	20 Aug 2002 18:53:07 -0000	1.12
***************
*** 5,8 ****
--- 5,9 ----
  #include "Python.h"
  #include "modsupport.h"
+ #include "structmember.h"
  
  #include <time.h>
***************
*** 13,38 ****
  #define MAXYEAR 9999
  
  /* Rename the long macros in datetime.h to more reasonable short names. */
! #define GET_YEAR(o)        PyDateTime_GET_YEAR(o)
! #define GET_MONTH(o)       PyDateTime_GET_MONTH(o)
! #define GET_DAY(o)         PyDateTime_GET_DAY(o)
! #define GET_HOUR(o)        PyDateTime_GET_HOUR(o)
! #define GET_MINUTE(o)      PyDateTime_GET_MINUTE(o)
! #define GET_SECOND(o)      PyDateTime_GET_SECOND(o)
! #define GET_MICROSECOND(o) PyDateTime_GET_MICROSECOND(o)
  
! /* Set accessors. */
! #define SET_YEAR(o, v)        (((o)->data[0] = ((v) & 0xff00) >> 8), \
!                                ((o)->data[1] = ((v) & 0x00ff)))
! #define SET_MONTH(o, v)       (PyDateTime_GET_MONTH(o) = (v))
! #define SET_DAY(o, v)         (PyDateTime_GET_DAY(o) = (v))
! #define SET_HOUR(o, v)        (PyDateTime_GET_HOUR(o) = (v))
! #define SET_MINUTE(o, v)      (PyDateTime_GET_MINUTE(o) = (v))
! #define SET_SECOND(o, v)      (PyDateTime_GET_SECOND(o) = (v))
! #define SET_MICROSECOND(o, v) (((o)->data[7] = ((v) & 0xff0000) >> 16), \
!                                ((o)->data[8] = ((v) & 0x00ff00) >> 8), \
!                                ((o)->data[9] = ((v) & 0x0000ff)))
  
! staticforward PyTypeObject PyDateTime_Type;
  
  /*
--- 14,60 ----
  #define MAXYEAR 9999
  
+ typedef struct
+ {
+     PyObject_HEAD
+     int hashcode;
+     long int days;
+     long int seconds;
+     long int microseconds;
+ } PyDateTime_Delta;
+ 
  /* Rename the long macros in datetime.h to more reasonable short names. */
! #define GET_YEAR(o)               PyDateTime_GET_YEAR(o)
! #define GET_MONTH(o)              PyDateTime_GET_MONTH(o)
! #define GET_DAY(o)                PyDateTime_GET_DAY(o)
! #define GET_HOUR(o)               PyDateTime_GET_HOUR(o)
! #define GET_MINUTE(o)             PyDateTime_GET_MINUTE(o)
! #define GET_SECOND(o)             PyDateTime_GET_SECOND(o)
! #define GET_MICROSECOND(o)        PyDateTime_GET_MICROSECOND(o)
  
! /* Date accessors. */
! #define SET_YEAR(o, v)            (((o)->data[0] = ((v) & 0xff00) >> 8), \
!                                    ((o)->data[1] = ((v) & 0x00ff)))
! #define SET_MONTH(o, v)           (PyDateTime_GET_MONTH(o) = (v))
! #define SET_DAY(o, v)             (PyDateTime_GET_DAY(o) = (v))
  
! /* Date/Time accessors. */
! #define SET_HOUR(o, v)            (PyDateTime_GET_HOUR(o) = (v))
! #define SET_MINUTE(o, v)          (PyDateTime_GET_MINUTE(o) = (v))
! #define SET_SECOND(o, v)          (PyDateTime_GET_SECOND(o) = (v))
! #define SET_MICROSECOND(o, v)     (((o)->data[7] = ((v) & 0xff0000) >> 16), \
!                                    ((o)->data[8] = ((v) & 0x00ff00) >> 8), \
!                                    ((o)->data[9] = ((v) & 0x0000ff)))
! 
! /* Delta accessors. */
! #define GET_TD_DAYS(o)            (((PyDateTime_Delta *)(o))->days)
! #define GET_TD_SECONDS(o)         (((PyDateTime_Delta *)(o))->seconds)
! #define GET_TD_MICROSECONDS(o)    (((PyDateTime_Delta *)(o))->microseconds)
! 
! #define SET_TD_DAYS(o, v)         ((o)->days = (v))
! #define SET_TD_SECONDS(o, v)      ((o)->seconds = (v))
! #define SET_TD_MICROSECONDS(o, v) ((o)->microseconds = (v))
! 
! static PyTypeObject PyDateTime_DateType;
! static PyTypeObject PyDateTime_DateTimeType;
  
  /*
***************
*** 43,47 ****
  is_leap(int year)
  {
!     return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
  }
  
--- 65,69 ----
  is_leap(int year)
  {
!     return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 1 : 0;
  }
  
***************
*** 88,91 ****
--- 110,163 ----
  }
  
+ /* ordinal -> year, month, day, considering 01-Jan-0001 as day 1. */
+ static void
+ ord_to_ymd(long int ordinal, long int *year, long int *month, long int *day)
+ {
+     int di400y = days_before_year(401);
+     int di100y = days_before_year(101);
+     int di4y = days_before_year(5);
+     int n, n1, n4, n100, n400, leapyear, preceding;
+ 
+     assert(ordinal >= 1);
+     --ordinal;
+     n400 = ordinal / di400y;
+     n = ordinal % di400y;
+     *year = n400 * 400 + 1;
+ 
+     n100 = n / di100y;
+     n = n % di100y;
+ 
+     n4 = n / di4y;
+     n = n % di4y;
+ 
+     n1 = n / 365;
+     n = n % 365;
+ 
+     *year += n100 * 100 + n4 * 4 + n1;
+     if (n1 == 4 || n100 == 4) {
+         assert(n == 0);
+         *year -= 1;
+         *month = 12;
+         *day = 31;
+         return;
+     }
+     leapyear = (n1 == 3 && (n4 != 24 || n100 == 3)) ? 1 : 0;
+     assert(leapyear == is_leap(*year));
+     *month = (n + 50) >> 5;
+     preceding = (_days_before_month[*month]
+                  + ((*month > 2 && leapyear) ? 1 : 0));
+     if (preceding > n) {
+         /* estimate is too large */
+         *month -= 1;
+         preceding -= days_in_month(*year, *month);
+     }
+     n -= preceding;
+     assert(0 <= n);
+     assert(n < days_in_month(*year, *month));
+ 
+     *day = n + 1;
+ }
+ 
+ /* year, month, day -> ordinal, considering 01-Jan-0001 as day 1. */
  static long
  ymd_to_ord(int year, int month, int day)
***************
*** 114,187 ****
  }
  
  
! /*
!  * PyDateTime_Object implementation
!  */
  
! static int
! datetime_compare(PyDateTime_Object *self, PyObject *other)
! {
!     if (!PyType_IsSubtype(other->ob_type, &PyDateTime_Type)) {
!         PyErr_SetString(PyExc_TypeError,
!                         "can't compare date to %s instance");
!         return -1;
!     }
!     return memcmp(self->data, ((PyDateTime_Object *)other)->data,
!                   _PyDateTime_DATA_SIZE);
  }
  
! static PyObject *
! datetime_repr(PyDateTime_Object *self)
  {
!     char buffer[1028];
!     char *typename;
! 
!     typename = self->ob_type->tp_name;
!     if (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),
!                       GET_HOUR(self), GET_MINUTE(self), GET_SECOND(self),
!                       GET_MICROSECOND(self));
!     }
!     else if (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),
!                       GET_HOUR(self), GET_MINUTE(self), 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),
!                       GET_HOUR(self), GET_MINUTE(self));
!     }
!     return PyString_FromString(buffer);
  }
  
  static void
! isoformat(PyDateTime_Object *dt, char sep, char buffer[], int bufflen)
  {
      PyOS_snprintf(buffer, bufflen,
!                   "%04d-%02d-%02d%c%02d:%02d:%02d.%06d",
!                   GET_YEAR(dt), GET_MONTH(dt), GET_DAY(dt),
!                   sep,
                    GET_HOUR(dt), GET_MINUTE(dt), GET_SECOND(dt),
                    GET_MICROSECOND(dt));
  }
  
  static PyObject *
! datetime_str(PyDateTime_Object *self)
  {
!     char buffer[128];
!     isoformat(self, ' ', buffer, sizeof(buffer));
! 
!     return PyString_FromString(buffer);
! }
  
! static int
! datetime_hash(PyDateTime_Object *self)
! {
!     return -2;
  }
  
--- 186,245 ----
  }
  
+ static PyObject *
+ format_ctime(PyDateTime_Date *date,
+              int hours, int minutes, int seconds)
+ {
+     static char *DayNames[] = {
+         "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
+     };
+     static char *MonthNames[] = {
+         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+     };
  
!     char buffer[128];
!     int wday = weekday(GET_YEAR(date), GET_MONTH(date), GET_DAY(date));
  
!     PyOS_snprintf(buffer, sizeof(buffer), "%s %s %2d %02d:%02d:%02d %04d",
!                   DayNames[wday], MonthNames[GET_MONTH(date) - 1],
!                   GET_DAY(date), hours, minutes, seconds,
!                   GET_YEAR(date));
!     return PyString_FromString(buffer);
  }
  
! static char *
! isoformat_date(PyDateTime_Date *dt, char buffer[], int bufflen)
  {
!     int x;
!     x = PyOS_snprintf(buffer, bufflen,
!                       "%04d-%02d-%02d",
!                       GET_YEAR(dt), GET_MONTH(dt), GET_DAY(dt));
!     return buffer + x;
  }
  
  static void
! isoformat_time(PyDateTime_DateTime *dt, char buffer[], int bufflen)
  {
      PyOS_snprintf(buffer, bufflen,
!                   "%02d:%02d:%02d.%06d",
                    GET_HOUR(dt), GET_MINUTE(dt), GET_SECOND(dt),
                    GET_MICROSECOND(dt));
  }
  
+ 
+ /* 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;
  }
  
***************
*** 191,198 ****
               int second, int usecond)
  {
!     PyDateTime_Object *self;
  
!     self = PyObject_New(PyDateTime_Object, &PyDateTime_Type);
      if (self != NULL) {
          SET_YEAR(self, year);
          SET_MONTH(self, month);
--- 249,257 ----
               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);
***************
*** 207,463 ****
  
  static PyObject *
! datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw)
  {
!     PyObject *self = NULL;
!     long int year, month, day, hour = 0, minute = 0, second = 0, usecond = 0;
! 
!     static char *keywords[] = {
!         "year", "month", "day", "hour", "minute", "second", "microsecond", NULL
!     };
  
!     if (PyArg_ParseTupleAndKeywords(args, kw, "lll|llll", keywords,
!                                     &year, &month, &day, &hour, &minute,
!                                     &second, &usecond)) {
!         if (year < MINYEAR || year > MAXYEAR) {
!             PyErr_SetString(PyExc_ValueError, "year is out of range");
!             return NULL;
!         }
!         if (month < 1 || month > 12) {
!             PyErr_SetString(PyExc_ValueError, "month must be in 1..12");
!             return NULL;
!         }
!         if (day < 1 || day > days_in_month(year, month)) {
!             PyErr_SetString(PyExc_ValueError, "day is out of range for month");
!             return NULL;
!         }
!         if (hour < 0 || hour > 23) {
!             PyErr_SetString(PyExc_ValueError, "hour must be in 0..23");
!             return NULL;
!         }
!         if (minute < 0 || minute > 59) {
!             PyErr_SetString(PyExc_ValueError, "minute must be in 0..59");
!             return NULL;
!         }
!         if (second < 0 || second > 59) {
!             PyErr_SetString(PyExc_ValueError, "second must be in 0..59");
!             return NULL;
!         }
!         if (usecond < 0 || usecond > 999999) {
!             PyErr_SetString(PyExc_ValueError,
!                             "microsecond must be in 0..999999");
!             return NULL;
!         }
!         self = new_datetime(year, month, day, hour, minute, second, usecond);
      }
!     return self;
! }
! 
! 
! static PyObject *
! datetime_year(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_YEAR(self)));
! }
! 
! static PyObject *
! datetime_month(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_MONTH(self)));
! }
! 
! static PyObject *
! datetime_day(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_DAY(self)));
! }
! 
! static PyObject *
! datetime_hour(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_HOUR(self)));
! }
! 
! static PyObject *
! datetime_minute(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_MINUTE(self)));
! }
! 
! static PyObject *
! datetime_second(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_SECOND(self)));
! }
! 
! static PyObject *
! datetime_microsecond(PyDateTime_Object *self, void *unused)
! {
!     return (PyInt_FromLong(GET_MICROSECOND(self)));
! }
! 
! static PyGetSetDef datetime_getset[] = {
!     {"year",(getter)datetime_year},
!     {"month",       (getter)datetime_month},
!     {"day",         (getter)datetime_day},
!     {"hour",        (getter)datetime_hour},
!     {"minute",      (getter)datetime_minute},
!     {"second",      (getter)datetime_second},
!     {"microsecond", (getter)datetime_microsecond},
!     {NULL}
! };
! 
! static PyObject *
! datetime_isocalendar(PyDateTime_Object *self)
! {
!     int  year         = GET_YEAR(self);
!     int  week1_monday = iso_week1_monday(year);
!     long today        = ymd_to_ord(year, GET_MONTH(self), GET_DAY(self));
!     int  week         = (today - week1_monday) / 7;
!     int  day          = (today - week1_monday) % 7;
! 
!     if (week < 0) {
!         --year;
!         week1_monday = iso_week1_monday(year);
!         week         = (today - week1_monday) / 7;
!         day          = (today - week1_monday) % 7;
      }
!     else if (week >= 52 &&
!              today >= iso_week1_monday(year + 1)) {
!         ++year;
!         week = 0;
      }
!     return Py_BuildValue("iii", year, week + 1, day + 1);
! }
! 
! static PyObject *
! datetime_isoformat(PyDateTime_Object *self, PyObject *args, PyObject *kw)
! {
!     char buffer[128];
!     char sep = 'T';
! 
!     static char *keywords[] = {"sep", NULL};
! 
!     if (!PyArg_ParseTupleAndKeywords(args, kw, "|c:isoformat", keywords, &sep))
!         return NULL;
!     isoformat(self, sep, buffer, sizeof(buffer));
! 
!     return PyString_FromString(buffer);
! }
! 
! static PyObject *
! datetime_isoweekday(PyDateTime_Object *self)
! {
!     int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
! 
!     return PyInt_FromLong(dow + 1);
! }
! 
! static PyObject *
! datetime_weekday(PyDateTime_Object *self)
! {
!     int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self));
! 
!     return PyInt_FromLong(dow);
! }
! 
! static PyObject *
! datetime_now(PyObject *self, PyObject *cls)
! {
!     /* 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);
  }
  
! static PyMethodDef datetime_methods[] = {
!     /* Class methods: */
!     {"now",         (PyCFunction)datetime_now,           METH_O | METH_CLASS,
!      "Return a new datetime that represents the current time."},
! 
!     /* Instance methods: */
!     {"isocalendar", (PyCFunction)datetime_isocalendar,   METH_NOARGS,
!      "Return a 3-tuple containing ISO year, week number, and weekday.\n\n"
!      "The first ISO week of the year is the (Mon-Sun) week containing the\n"
!      "year's first Thursday; everything rest derives from that."},
!     {"isoformat",   (PyCFunction)datetime_isoformat,     METH_VARARGS|METH_KEYWORDS,
!      "Return the day of the week represented by the datetime.\n"
!      "Monday == 1 ... Sunday == 7"},
!     {"isoweekday",  (PyCFunction)datetime_isoweekday,    METH_NOARGS,
!      "Return the day of the week represented by the datetime.\n"
!      "Monday == 1 ... Sunday == 7"},
!     {"weekday",     (PyCFunction)datetime_weekday,       METH_NOARGS,
!      "Return the day of the week represented by the datetime.\n"
!      "Monday == 0 ... Sunday == 6"},
!     {NULL}
! };
! 
! 
! static char datetime_doc[] =
! "Basic date/time type.";
! 
! 
! statichere PyTypeObject PyDateTime_Type = {
!     PyObject_HEAD_INIT(NULL)
!     0,						/* ob_size */
!     "datetime",					/* tp_name */
!     sizeof(PyDateTime_Object),			/* tp_basicsize */
!     0,						/* tp_itemsize */
!     _PyObject_Del,				/* tp_dealloc */
!     0,						/* tp_print */
!     0,						/* tp_getattr */
!     0,						/* tp_setattr */
!     (cmpfunc)datetime_compare,			/* tp_compare */
!     (reprfunc)datetime_repr,			/* tp_repr */
!     0,						/* 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 */
!     0,						/* tp_as_buffer */
!     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
!         Py_TPFLAGS_BASETYPE,			/* tp_flags */
!     datetime_doc,				/* tp_doc */
!     0,						/* tp_traverse */
!     0,						/* tp_clear */
!     0,						/* tp_richcompare */
!     0,						/* tp_weaklistoffset */
!     0,						/* tp_iter */
!     0,						/* tp_iternext */
!     datetime_methods,				/* tp_methods */
!     0,						/* tp_members */
!     datetime_getset,				/* tp_getset */
!     0,						/* tp_base */
!     0,						/* tp_dict */
!     0,						/* tp_descr_get */
!     0,						/* tp_descr_set */
!     0,						/* tp_dictoffset */
!     0,						/* tp_init */
!     0,						/* tp_alloc */
!     datetime_new,				/* tp_new */
!     _PyObject_Del,				/* tp_free */
! };
! 
! 
! static
! PyMethodDef functions[] = {
!     {NULL, NULL, 0, NULL}
! };
! 
  
  void
--- 266,294 ----
  
  static PyObject *
! new_delta(long int days, long int seconds, long int microseconds)
  {
!     PyDateTime_Delta *self;
  
!     if (microseconds >= 1000000 || microseconds <= -1000000) {
!         seconds += microseconds / 1000000;
!         microseconds %= 1000000;
      }
!     if (seconds >= 24*3600 || seconds <= 24*3600) {
!         days += seconds / (24*3600);
!         seconds %= (24*3600);
      }
!     self = PyObject_New(PyDateTime_Delta, &PyDateTime_DeltaType);
!     if (self != NULL) {
!         self->hashcode = -1;
!         SET_TD_DAYS(self, days);
!         SET_TD_SECONDS(self, seconds);
!         SET_TD_MICROSECONDS(self, microseconds);
      }
!     return (PyObject *) self;
  }
  
! #include "obj_delta.c"
! #include "obj_date.c"
! #include "obj_datetime.c"
  
  void
***************
*** 466,477 ****
      PyObject *m;
      PyObject *d, *dt;
-     int err;
  
!     PyDateTime_Type.ob_type = &PyType_Type;
  
!     if (PyType_Ready(&PyDateTime_Type) < 0)
          return;
  
!     d = PyDateTime_Type.tp_dict;
      dt = new_datetime(1, 1, 1, 0, 0, 0, 0);
      if (dt == NULL || PyDict_SetItemString(d, "min", dt) < 0)
--- 297,325 ----
      PyObject *m;
      PyObject *d, *dt;
  
!     if (PyType_Ready(&PyDateTime_DateType) < 0)
!         return;
!     if (PyType_Ready(&PyDateTime_DateTimeType) < 0)
!         return;
!     if (PyType_Ready(&PyDateTime_DeltaType) < 0)
!         return;
  
!     /* date values */
!     d = PyDateTime_DateType.tp_dict;
!     dt = new_date(1, 1, 1);
!     if (dt == NULL || PyDict_SetItemString(d, "min", dt) < 0)
!         return;
!     Py_DECREF(dt);
!     dt = new_date(MAXYEAR, 12, 31);
!     if (dt == NULL || PyDict_SetItemString(d, "max", dt) < 0)
!         return;
!     Py_DECREF(dt);
!     dt = new_delta(1, 0, 0);
!     if (dt == NULL || PyDict_SetItemString(d, "resolution", dt) < 0)
          return;
+     Py_DECREF(dt);
  
!     /* date/time values */
!     d = PyDateTime_DateTimeType.tp_dict;
      dt = new_datetime(1, 1, 1, 0, 0, 0, 0);
      if (dt == NULL || PyDict_SetItemString(d, "min", dt) < 0)
***************
*** 482,491 ****
          return;
      Py_DECREF(dt);
  
!     m = Py_InitModule3("_datetime", functions,
                         "Fast implementation of the datetime type.");
      PyModule_AddIntConstant(m, "MINYEAR", 1);
!     PyModule_AddIntConstant(m, "MAXYEAR", 9999);
!     Py_INCREF(&PyDateTime_Type);
!     PyModule_AddObject(m, "datetime", (PyObject *) &PyDateTime_Type);
  }
--- 330,348 ----
          return;
      Py_DECREF(dt);
+     dt = new_delta(0, 0, 1);
+     if (dt == NULL || PyDict_SetItemString(d, "resolution", dt) < 0)
+         return;
+     Py_DECREF(dt);
  
!     /* module initialization */
!     m = Py_InitModule3("_datetime", NULL,
                         "Fast implementation of the datetime type.");
      PyModule_AddIntConstant(m, "MINYEAR", 1);
!     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);
  }

Index: datetime.h
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** datetime.h	12 Mar 2002 22:33:51 -0000	1.4
--- datetime.h	20 Aug 2002 18:53:07 -0000	1.5
***************
*** 5,27 ****
  #define DATETIME_H
  
! #define _PyDateTime_DATA_SIZE 10
  
  typedef struct
  {
      PyObject_HEAD
!     unsigned char data[_PyDateTime_DATA_SIZE];
! }  PyDateTime_Object;
  
  
! #define PyDateTime_GET_YEAR(o)        (((PyDateTime_Object*)o)->data[0] << 8  \
!                                        | ((PyDateTime_Object*)o)->data[1])
! #define PyDateTime_GET_MONTH(o)       (((PyDateTime_Object*)o)->data[2])
! #define PyDateTime_GET_DAY(o)         (((PyDateTime_Object*)o)->data[3])
! #define PyDateTime_GET_HOUR(o)        (((PyDateTime_Object*)o)->data[4])
! #define PyDateTime_GET_MINUTE(o)      (((PyDateTime_Object*)o)->data[5])
! #define PyDateTime_GET_SECOND(o)      (((PyDateTime_Object*)o)->data[6])
! #define PyDateTime_GET_MICROSECOND(o) (((PyDateTime_Object*)o)->data[7] << 16 \
!                                        | ((PyDateTime_Object*)o)->data[8] << 8\
!                                        | ((PyDateTime_Object*)o)->data[9])
  
  #endif
--- 5,42 ----
  #define DATETIME_H
  
! #define _PyDateTime_DATE_DATA_SIZE 4
! #define _PyDateTime_DATETIME_DATA_SIZE 10
  
  typedef struct
  {
      PyObject_HEAD
!     long hashcode;
!     unsigned char data[_PyDateTime_DATE_DATA_SIZE];
! }  PyDateTime_Date;
  
+ typedef struct
+ {
+     PyObject_HEAD
+     long hashcode;
+     unsigned char data[_PyDateTime_DATETIME_DATA_SIZE];
+ }  PyDateTime_DateTime;
  
! PyAPI_DATA(PyTypeObject) PyDateTime_DateType;
! PyAPI_DATA(PyTypeObject) PyDateTime_DateTimeType;
! PyAPI_DATA(PyTypeObject) PyDateTime_DeltaType;
! 
! /* Apply for date instances. */
! #define PyDateTime_GET_YEAR(o)     (((PyDateTime_Date*)o)->data[0] << 8 \
!                                     | ((PyDateTime_Date*)o)->data[1])
! #define PyDateTime_GET_MONTH(o)    (((PyDateTime_Date*)o)->data[2])
! #define PyDateTime_GET_DAY(o)      (((PyDateTime_Date*)o)->data[3])
! 
! /* Apply for datetime instances. */
! #define PyDateTime_GET_HOUR(o)        (((PyDateTime_DateTime*)o)->data[4])
! #define PyDateTime_GET_MINUTE(o)      (((PyDateTime_DateTime*)o)->data[5])
! #define PyDateTime_GET_SECOND(o)      (((PyDateTime_DateTime*)o)->data[6])
! #define PyDateTime_GET_MICROSECOND(o) (((PyDateTime_DateTime*)o)->data[7] << 16 \
!                                        | ((PyDateTime_DateTime*)o)->data[8] << 8\
!                                        | ((PyDateTime_DateTime*)o)->data[9])
  
  #endif

Index: setup.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/setup.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** setup.py	4 Mar 2002 14:36:16 -0000	1.1
--- setup.py	20 Aug 2002 18:53:07 -0000	1.2
***************
*** 1,4 ****
  from distutils.core import setup, Extension
  
  setup(name="datetime", version = "0.1",
!       ext_modules=[Extension("_datetime", ["datetime.c"])])
--- 1,13 ----
+ #! /usr/bin/env python
+ #
+ # For now, the individual object implementations are split out into
+ # separate files (obj_*.c), with helper functions and module
+ # initialization in datetime.c.  This will be changed to simply be a
+ # single file when things have settled down.
+ 
  from distutils.core import setup, Extension
  
  setup(name="datetime", version = "0.1",
!       ext_modules=[Extension("_datetime", ["datetime.c"],
!                              depends=["obj_date.c", "obj_datetime.c",
!                                       "obj_delta.c"])])

Index: test_cdatetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_cdatetime.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** test_cdatetime.py	26 Mar 2002 22:22:50 -0000	1.4
--- test_cdatetime.py	20 Aug 2002 18:53:07 -0000	1.5
***************
*** 8,11 ****
--- 8,218 ----
  
  from _datetime import datetime, MINYEAR, MAXYEAR
+ from _datetime import date, timedelta
+ 
+ 
+ class TestDate(unittest.TestCase):
+ 
+     theclass = date
+ 
+     def test_basic_attributes(self):
+         dt = self.theclass(2002, 3, 1)
+         self.assertEqual(dt.year, 2002)
+         self.assertEqual(dt.month, 3)
+         self.assertEqual(dt.day, 1)
+ 
+     def test_roundtrip(self):
+         for dt in (self.theclass(1, 2, 3),
+                    self.theclass.today()):
+             # Verify dt -> string -> date identity.
+             s = repr(dt)
+             dt2 = eval(s)
+             self.assertEqual(dt, dt2)
+ 
+             # Verify identity via reconstructing from pieces.
+             dt2 = self.theclass(dt.year, dt.month, dt.day)
+             self.assertEqual(dt, 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)
+ 
+     def test_hash_equality(self):
+         d = self.theclass(2000, 12, 31)
+         # same thing
+         e = self.theclass(2000, 12, 31)
+         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)
+         # same thing
+         e = self.theclass(2001,  1,  1)
+         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)
+ ##         print >>sys.__stderr__, a, b
+ ##         diff = a-b
+ ##         self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
+ ##         self.assertEqual(diff.seconds, 0)
+ ##         self.assertEqual(diff.microseconds, 0)
+         day = timedelta(1)
+         week = timedelta(7)
+         a = self.theclass(2002, 3, 2)
+         self.assertEqual(a + day, self.theclass(2002, 3, 3))
+         self.assertEqual(a - day, self.theclass(2002, 3, 1))
+         self.assertEqual(a + week, self.theclass(2002, 3, 9))
+         self.assertEqual(a - week, self.theclass(2002, 2, 23))
+         self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
+         self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
+         self.assertEqual((a + week) - a, week)
+         self.assertEqual((a + day) - a, day)
+         self.assertEqual((a - week) - a, -week)
+         self.assertEqual((a - day) - a, -day)
+         self.assertEqual(a - (a + week), -week)
+         self.assertEqual(a - (a + day), -day)
+         self.assertEqual(a - (a - week), week)
+         self.assertEqual(a - (a - day), day)
+         # 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_overflow(self):
+         tiny = self.theclass.resolution
+ 
+         dt = self.theclass.min + tiny
+         dt -= tiny  # no problem
+         self.assertRaises(OverflowError, dt.__sub__, tiny)
+         self.assertRaises(OverflowError, dt.__add__, -tiny)
+ 
+         dt = self.theclass.max - tiny
+         dt += tiny  # no problem
+ ##         self.assertRaises(OverflowError, dt.__add__, tiny)
+ ##         self.assertRaises(OverflowError, dt.__sub__, -tiny)
+ 
+     def test_weekday(self):
+         for i in range(7):
+             # March 4, 2002 is a Monday
+             self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
+             self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
+             # January 2, 1956 is a Monday
+             self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
+             self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
+ 
+     def test_isocalendar(self):
+         # Check examples from
+         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
+         for i in range(7):
+             d = self.theclass(2003, 12, 22+i)
+             self.assertEqual(d.isocalendar(), (2003, 52, i+1))
+             d = self.theclass(2003, 12, 29) + timedelta(i)
+             self.assertEqual(d.isocalendar(), (2004, 1, i+1))
+             d = self.theclass(2004, 1, 5+i)
+             self.assertEqual(d.isocalendar(), (2004, 2, i+1))
+             d = self.theclass(2009, 12, 21+i)
+             self.assertEqual(d.isocalendar(), (2009, 52, i+1))
+             d = self.theclass(2009, 12, 28) + timedelta(i)
+ ##             print >>sys.__stderr__, i, `d`, d.isocalendar()
+ ##             self.assertEqual(d.isocalendar(), (2009, 53, i+1))
+             d = self.theclass(2010, 1, 4+i)
+             self.assertEqual(d.isocalendar(), (2010, 1, i+1))
+ 
+     def test_iso_long_years(self):
+         # Calculate long ISO years and compare to table from
+         # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
+         ISO_LONG_YEARS_TABLE = """
+               4   32   60   88
+               9   37   65   93
+              15   43   71   99
+              20   48   76
+              26   54   82
+ 
+             105  133  161  189
+             111  139  167  195
+             116  144  172
+             122  150  178
+             128  156  184
+ 
+             201  229  257  285
+             207  235  263  291
+             212  240  268  296
+             218  246  274
+             224  252  280
+ 
+             303  331  359  387
+             308  336  364  392
+             314  342  370  398
+             320  348  376
+             325  353  381
+         """
+         iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
+         iso_long_years.sort()
+         L = []
+         for i in range(400):
+             d = self.theclass(2000+i, 12, 31)
+             d1 = self.theclass(1600+i, 12, 31)
+             self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
+             if d.isocalendar()[1] == 53:
+                 L.append(i)
+         self.assertEqual(L, iso_long_years)
+ 
+     def test_isoformat(self):
+         t = self.theclass(2, 3, 2)
+         self.assertEqual(t.isoformat(), "0002-03-02")
+ 
+     def test_ctime(self):
+         t = self.theclass(2002, 3, 2)
+         self.assertEqual(t.ctime(), "Sat Mar  2 00:00:00 2002")
+ 
+     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_extreme_timedelta(self):
+         big = self.theclass.max - self.theclass.min
+         # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
+         n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
+         # n = 315537897599999999 ~= 2**58.13
+ ##         justasbig = timedelta(0, 0, n)
+ ##         self.assertEqual(big, justasbig)
+         self.assertEqual(self.theclass.min + big, self.theclass.max)
+         self.assertEqual(self.theclass.max - big, self.theclass.min)
+ 
  
  class TestDateTime(unittest.TestCase):
***************
*** 55,65 ****
              d = self.theclass(2003, 12, 22+i)
              self.assertEqual(d.isocalendar(), (2003, 52, i+1))
! ##             d = self.theclass(2003, 12, 29) + timedelta(i)
! ##             self.assertEqual(d.isocalendar(), (2004, 1, i+1))
              d = self.theclass(2004, 1, 5+i)
              self.assertEqual(d.isocalendar(), (2004, 2, i+1))
              d = self.theclass(2009, 12, 21+i)
              self.assertEqual(d.isocalendar(), (2009, 52, i+1))
! ##             d = self.theclass(2009, 12, 28) + timedelta(i)
  ##             self.assertEqual(d.isocalendar(), (2009, 53, i+1))
              d = self.theclass(2010, 1, 4+i)
--- 262,272 ----
              d = self.theclass(2003, 12, 22+i)
              self.assertEqual(d.isocalendar(), (2003, 52, i+1))
!             d = self.theclass(2003, 12, 29) + timedelta(i)
!             self.assertEqual(d.isocalendar(), (2004, 1, i+1))
              d = self.theclass(2004, 1, 5+i)
              self.assertEqual(d.isocalendar(), (2004, 2, i+1))
              d = self.theclass(2009, 12, 21+i)
              self.assertEqual(d.isocalendar(), (2009, 52, i+1))
!             d = self.theclass(2009, 12, 28) + timedelta(i)
  ##             self.assertEqual(d.isocalendar(), (2009, 53, i+1))
              d = self.theclass(2010, 1, 4+i)
***************
*** 120,127 ****
              self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
  
  
  def test_suite():
!     s1 = unittest.makeSuite(TestDateTime, 'test')
!     return unittest.TestSuite([s1])
  
  def test_main():
--- 327,339 ----
              self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
  
+     def test_ctime(self):
+         t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
+         self.assertEqual(t.ctime(), "Sat Mar  2 18:03:05 2002")
+ 
  
  def test_suite():
!     s1 = unittest.makeSuite(TestDate, 'test')
!     s2 = unittest.makeSuite(TestDateTime, 'test')
!     return unittest.TestSuite([s1, s2])
  
  def test_main():