[Python-checkins] r82642 - python/branches/py3k/Modules/datetimemodule.c

alexander.belopolsky python-checkins at python.org
Thu Jul 8 01:56:38 CEST 2010


Author: alexander.belopolsky
Date: Thu Jul  8 01:56:38 2010
New Revision: 82642

Log:
Issue #5288: Eliminated round-trips between timdelta and int offsets

Modified:
   python/branches/py3k/Modules/datetimemodule.c

Modified: python/branches/py3k/Modules/datetimemodule.c
==============================================================================
--- python/branches/py3k/Modules/datetimemodule.c	(original)
+++ python/branches/py3k/Modules/datetimemodule.c	Thu Jul  8 01:56:38 2010
@@ -88,8 +88,11 @@
 /* p is a pointer to a time or a datetime object; HASTZINFO(p) returns
  * p->hastzinfo.
  */
-#define HASTZINFO(p)            (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo)
-
+#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo)
+#define GET_TIME_TZINFO(p) (HASTZINFO(p) ? \
+                            ((PyDateTime_Time *)(p))->tzinfo : Py_None)
+#define GET_DT_TZINFO(p) (HASTZINFO(p) ? \
+                          ((PyDateTime_DateTime *)(p))->tzinfo : Py_None)
 /* M is a char or int claiming to be a valid month.  The macro is equivalent
  * to the two-sided Python test
  *      1 <= M <= 12
@@ -839,25 +842,6 @@
     return -1;
 }
 
-/* Return tzinfo.methname(tzinfoarg), without any checking of results.
- * If tzinfo is None, returns None.
- */
-static PyObject *
-call_tzinfo_method(PyObject *tzinfo, char *methname, PyObject *tzinfoarg)
-{
-    PyObject *result;
-
-    assert(tzinfo && methname && tzinfoarg);
-    assert(check_tzinfo_subclass(tzinfo) >= 0);
-    if (tzinfo == Py_None) {
-        result = Py_None;
-        Py_INCREF(result);
-    }
-    else
-        result = PyObject_CallMethod(tzinfo, methname, "O", tzinfoarg);
-    return result;
-}
-
 /* If self has a tzinfo member, return a BORROWED reference to it.  Else
  * return NULL, which is NOT AN ERROR.  There are no error returns here,
  * and the caller must not decref the result.
@@ -875,69 +859,53 @@
     return tzinfo;
 }
 
-/* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the
- * result.  tzinfo must be an instance of the tzinfo class.  If the method
- * returns None, this returns 0 and sets *none to 1.  If the method doesn't
- * return None or timedelta, TypeError is raised and this returns -1.  If it
- * returnsa timedelta and the value is out of range or isn't a whole number
- * of minutes, ValueError is raised and this returns -1.
- * Else *none is set to 0 and the integer method result is returned.
+/* Call getattr(tzinfo, name)(tzinfoarg), and check the result.  tzinfo must
+ * be an instance of the tzinfo class.  If the method returns None, this
+ * returns None.  If the method doesn't return None or timedelta, TypeError is
+ * raised and this returns NULL.  If it returns a timedelta and the value is
+ * out of range or isn't a whole number of minutes, ValueError is raised and
+ * this returns NULL.  Else result is returned.
  */
-static int
-call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg,
-                       int *none)
+static PyObject *
+call_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg)
 {
-    PyObject *u;
-    int result = -1;
+    PyObject *offset;
 
     assert(tzinfo != NULL);
-    assert(PyTZInfo_Check(tzinfo));
+    assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None);
     assert(tzinfoarg != NULL);
 
-    *none = 0;
-    u = call_tzinfo_method(tzinfo, name, tzinfoarg);
-    if (u == NULL)
-        return -1;
-
-    else if (u == Py_None) {
-        result = 0;
-        *none = 1;
-    }
-    else if (PyDelta_Check(u)) {
-        const int days = GET_TD_DAYS(u);
-        if (days < -1 || days > 0)
-            result = 24*60;             /* trigger ValueError below */
-        else {
-            /* next line can't overflow because we know days
-             * is -1 or 0 now
-             */
-            int ss = days * 24 * 3600 + GET_TD_SECONDS(u);
-            result = divmod(ss, 60, &ss);
-            if (ss || GET_TD_MICROSECONDS(u)) {
-                PyErr_Format(PyExc_ValueError,
-                             "tzinfo.%s() must return a "
-                             "whole number of minutes",
-                             name);
-                result = -1;
-            }
+    if (tzinfo == Py_None)
+        Py_RETURN_NONE;
+    offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg);
+    if (offset == Py_None || offset == NULL)
+        return offset;
+    if (PyDelta_Check(offset)) {
+        if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
+            Py_DECREF(offset);
+            PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
+                         " representing a whole number of minutes");
+            return NULL;
+        }
+        if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
+            GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
+            Py_DECREF(offset);
+            PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
+                         " strictly between -timedelta(hours=24) and"
+                         " timedelta(hours=24).");
+            return NULL;
         }
     }
     else {
+        Py_DECREF(offset);
         PyErr_Format(PyExc_TypeError,
                      "tzinfo.%s() must return None or "
-                     "timedelta, not '%s'",
-                     name, Py_TYPE(u)->tp_name);
+                     "timedelta, not '%.200s'",
+                     name, Py_TYPE(offset)->tp_name);
+        return NULL;
     }
 
-    Py_DECREF(u);
-    if (result < -1439 || result > 1439) {
-        PyErr_Format(PyExc_ValueError,
-                     "tzinfo.%s() returned %d; must be in "
-                     "-1439 .. 1439",
-                     name, result);
-        result = -1;
-    }
-    return result;
+    return offset;
 }
 
 /* Call tzinfo.utcoffset(tzinfoarg), and extract an integer from the
@@ -948,37 +916,10 @@
  * # of minutes), ValueError is raised and this returns -1.  Else *none is
  * set to 0 and the offset is returned (as int # of minutes east of UTC).
  */
-static int
-call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg, int *none)
-{
-    return call_utc_tzinfo_method(tzinfo, "utcoffset", tzinfoarg, none);
-}
-
-/* Call tzinfo.name(tzinfoarg), and return the offset as a timedelta or None.
- */
 static PyObject *
-offset_as_timedelta(PyObject *tzinfo, char *name, PyObject *tzinfoarg) {
-    PyObject *result;
-
-    assert(tzinfo && name && tzinfoarg);
-    if (tzinfo == Py_None) {
-        result = Py_None;
-        Py_INCREF(result);
-    }
-    else {
-        int none;
-        int offset = call_utc_tzinfo_method(tzinfo, name, tzinfoarg,
-                                            &none);
-        if (offset < 0 && PyErr_Occurred())
-            return NULL;
-        if (none) {
-            result = Py_None;
-            Py_INCREF(result);
-        }
-        else
-            result = new_delta(0, offset * 60, 0, 1);
-    }
-    return result;
+call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg)
+{
+    return call_tzinfo_method(tzinfo, "utcoffset", tzinfoarg);
 }
 
 /* Call tzinfo.dst(tzinfoarg), and extract an integer from the
@@ -989,10 +930,10 @@
  * ValueError is raised and this returns -1.  Else *none is set to 0 and
  * the offset is returned (as an int # of minutes east of UTC).
  */
-static int
-call_dst(PyObject *tzinfo, PyObject *tzinfoarg, int *none)
+static PyObject *
+call_dst(PyObject *tzinfo, PyObject *tzinfoarg)
 {
-    return call_utc_tzinfo_method(tzinfo, "dst", tzinfoarg, none);
+    return call_tzinfo_method(tzinfo, "dst", tzinfoarg);
 }
 
 /* Call tzinfo.tzname(tzinfoarg), and return the result.  tzinfo must be
@@ -1010,102 +951,23 @@
     assert(check_tzinfo_subclass(tzinfo) >= 0);
     assert(tzinfoarg != NULL);
 
-    if (tzinfo == Py_None) {
-        result = Py_None;
-        Py_INCREF(result);
-    }
-    else
-        result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg);
+    if (tzinfo == Py_None)
+        Py_RETURN_NONE;
 
-    if (result != NULL && result != Py_None) {
-        if (!PyUnicode_Check(result)) {
-            PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must "
-                         "return None or a string, not '%s'",
-                         Py_TYPE(result)->tp_name);
-            Py_DECREF(result);
-            result = NULL;
-        }
-    }
-    return result;
-}
+    result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg);
 
-typedef enum {
-              /* an exception has been set; the caller should pass it on */
-          OFFSET_ERROR,
-
-          /* type isn't date, datetime, or time subclass */
-          OFFSET_UNKNOWN,
-
-          /* date,
-           * datetime with !hastzinfo
-           * datetime with None tzinfo,
-           * datetime where utcoffset() returns None
-           * time with !hastzinfo
-           * time with None tzinfo,
-           * time where utcoffset() returns None
-           */
-          OFFSET_NAIVE,
-
-          /* time or datetime where utcoffset() doesn't return None */
-          OFFSET_AWARE
-} naivety;
-
-/* Classify an object as to whether it's naive or offset-aware.  See
- * the "naivety" typedef for details.  If the type is aware, *offset is set
- * to minutes east of UTC (as returned by the tzinfo.utcoffset() method).
- * If the type is offset-naive (or unknown, or error), *offset is set to 0.
- * tzinfoarg is the argument to pass to the tzinfo.utcoffset() method.
- */
-static naivety
-classify_utcoffset(PyObject *op, PyObject *tzinfoarg, int *offset)
-{
-    int none;
-    PyObject *tzinfo;
+    if (result == NULL || result == Py_None)
+        return result;
 
-    assert(tzinfoarg != NULL);
-    *offset = 0;
-    tzinfo = get_tzinfo_member(op);     /* NULL means no tzinfo, not error */
-    if (tzinfo == Py_None)
-        return OFFSET_NAIVE;
-    if (tzinfo == NULL) {
-        /* note that a datetime passes the PyDate_Check test */
-        return (PyTime_Check(op) || PyDate_Check(op)) ?
-               OFFSET_NAIVE : OFFSET_UNKNOWN;
-    }
-    *offset = call_utcoffset(tzinfo, tzinfoarg, &none);
-    if (*offset == -1 && PyErr_Occurred())
-        return OFFSET_ERROR;
-    return none ? OFFSET_NAIVE : OFFSET_AWARE;
-}
-
-/* Classify two objects as to whether they're naive or offset-aware.
- * This isn't quite the same as calling classify_utcoffset() twice:  for
- * binary operations (comparison and subtraction), we generally want to
- * ignore the tzinfo members if they're identical.  This is by design,
- * so that results match "naive" expectations when mixing objects from a
- * single timezone.  So in that case, this sets both offsets to 0 and
- * both naiveties to OFFSET_NAIVE.
- * The function returns 0 if everything's OK, and -1 on error.
- */
-static int
-classify_two_utcoffsets(PyObject *o1, int *offset1, naivety *n1,
-                        PyObject *tzinfoarg1,
-                        PyObject *o2, int *offset2, naivety *n2,
-                        PyObject *tzinfoarg2)
-{
-    if (get_tzinfo_member(o1) == get_tzinfo_member(o2)) {
-        *offset1 = *offset2 = 0;
-        *n1 = *n2 = OFFSET_NAIVE;
-    }
-    else {
-        *n1 = classify_utcoffset(o1, tzinfoarg1, offset1);
-        if (*n1 == OFFSET_ERROR)
-            return -1;
-        *n2 = classify_utcoffset(o2, tzinfoarg2, offset2);
-        if (*n2 == OFFSET_ERROR)
-            return -1;
+    if (!PyUnicode_Check(result)) {
+        PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must "
+                     "return None or a string, not '%s'",
+                     Py_TYPE(result)->tp_name);
+        Py_DECREF(result);
+        result = NULL;
     }
-    return 0;
+
+    return result;
 }
 
 /* repr is like "someclass(arg1, arg2)".  If tzinfo isn't None,
@@ -1157,6 +1019,8 @@
                                 GET_YEAR(date));
 }
 
+static PyObject *delta_negative(PyDateTime_Delta *self);
+
 /* Add an hours & minutes UTC offset string to buf.  buf has no more than
  * buflen bytes remaining.  The UTC offset is gotten by calling
  * tzinfo.uctoffset(tzinfoarg).  If that returns None, \0 is stored into
@@ -1171,28 +1035,41 @@
 format_utcoffset(char *buf, size_t buflen, const char *sep,
                 PyObject *tzinfo, PyObject *tzinfoarg)
 {
-    int offset;
-    int hours;
-    int minutes;
+    PyObject *offset;
+    int hours, minutes, seconds;
     char sign;
-    int none;
 
     assert(buflen >= 1);
 
-    offset = call_utcoffset(tzinfo, tzinfoarg, &none);
-    if (offset == -1 && PyErr_Occurred())
+    offset = call_utcoffset(tzinfo, tzinfoarg);
+    if (offset == NULL)
         return -1;
-    if (none) {
+    if (offset == Py_None) {
+        Py_DECREF(offset);
         *buf = '\0';
         return 0;
     }
-    sign = '+';
-    if (offset < 0) {
+    /* Offset is normalized, so it is negative if days < 0 */
+    if (GET_TD_DAYS(offset) < 0) {
+        PyObject *temp = offset;
         sign = '-';
-        offset = - offset;
+        offset = delta_negative((PyDateTime_Delta *)offset);
+        Py_DECREF(temp);
+        if (offset == NULL)
+            return -1;
     }
-    hours = divmod(offset, 60, &minutes);
+    else {
+        sign = '+';
+    }
+    /* Offset is not negative here. */
+    seconds = GET_TD_SECONDS(offset);
+    Py_DECREF(offset);
+    minutes = divmod(seconds, 60, &seconds);
+    hours = divmod(minutes, 60, &minutes);
+    assert(seconds == 0);
+    /* XXX ignore sub-minute data, curently not allowed. */
     PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes);
+
     return 0;
 }
 
@@ -1434,7 +1311,7 @@
         format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt));
         if (format != NULL) {
             result = PyObject_CallMethod(time, "strftime", "OO",
-                                         format, timetuple);
+                                         format, timetuple, NULL);
             Py_DECREF(format);
         }
         Py_DECREF(time);
@@ -1937,17 +1814,24 @@
     return result;
 }
 
+static int
+delta_cmp(PyObject *self, PyObject *other)
+{
+    int 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);
+    }
+    return diff;
+}
+
 static PyObject *
 delta_richcompare(PyObject *self, PyObject *other, int op)
 {
     if (PyDelta_Check(other)) {
-        int 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);
-        }
+        int diff = delta_cmp(self, other);
         return diff_to_bool(diff, op);
     }
     else {
@@ -3119,76 +3003,73 @@
     return tzinfo_nogo("dst");
 }
 
+
+static PyObject *add_datetime_timedelta(PyDateTime_DateTime *date,
+                                        PyDateTime_Delta *delta,
+                                        int factor);
+static PyObject *datetime_utcoffset(PyObject *self, PyObject *);
+static PyObject *datetime_dst(PyObject *self, PyObject *);
+
 static PyObject *
-tzinfo_fromutc(PyDateTime_TZInfo *self, PyDateTime_DateTime *dt)
+tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt)
 {
-    int y, m, d, hh, mm, ss, us;
-
-    PyObject *result;
-    int off, dst;
-    int none;
-    int delta;
+    PyObject *result = NULL;
+    PyObject *off = NULL, *dst = NULL;
+    PyDateTime_Delta *delta = NULL;
 
-    if (! PyDateTime_Check(dt)) {
+    if (!PyDateTime_Check(dt)) {
         PyErr_SetString(PyExc_TypeError,
                         "fromutc: argument must be a datetime");
         return NULL;
     }
-    if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
+    if (GET_DT_TZINFO(dt) != (PyObject *)self) {
         PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
                         "is not self");
         return NULL;
     }
 
-    off = call_utcoffset(dt->tzinfo, (PyObject *)dt, &none);
-    if (off == -1 && PyErr_Occurred())
+    off = datetime_utcoffset(dt, NULL);
+    if (off == NULL)
         return NULL;
-    if (none) {
+    if (off == Py_None) {
         PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
                         "utcoffset() result required");
-        return NULL;
+        goto Fail;
     }
 
-    dst = call_dst(dt->tzinfo, (PyObject *)dt, &none);
-    if (dst == -1 && PyErr_Occurred())
-        return NULL;
-    if (none) {
+    dst = datetime_dst(dt, NULL);
+    if (dst == NULL)
+        goto Fail;
+    if (dst == Py_None) {
         PyErr_SetString(PyExc_ValueError, "fromutc: non-None "
                         "dst() result required");
-        return NULL;
+        goto Fail;
     }
 
-    y = GET_YEAR(dt);
-    m = GET_MONTH(dt);
-    d = GET_DAY(dt);
-    hh = DATE_GET_HOUR(dt);
-    mm = DATE_GET_MINUTE(dt);
-    ss = DATE_GET_SECOND(dt);
-    us = DATE_GET_MICROSECOND(dt);
-
-    delta = off - dst;
-    mm += delta;
-    if ((mm < 0 || mm >= 60) &&
-        normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
-        return NULL;
-    result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo);
+    delta = (PyDateTime_Delta *)delta_subtract(off, dst);
+    if (delta == NULL)
+        goto Fail;
+    result = add_datetime_timedelta((PyDateTime_DateTime *)dt, delta, 1);
     if (result == NULL)
-        return result;
-
-    dst = call_dst(dt->tzinfo, result, &none);
-    if (dst == -1 && PyErr_Occurred())
         goto Fail;
-    if (none)
-        goto Inconsistent;
-    if (dst == 0)
-        return result;
 
-    mm += dst;
-    if ((mm < 0 || mm >= 60) &&
-        normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
+    Py_DECREF(dst);
+    dst = call_dst(GET_DT_TZINFO(dt), result);
+    if (dst == NULL)
         goto Fail;
-    Py_DECREF(result);
-    result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo);
+    if (dst == Py_None)
+        goto Inconsistent;
+    if (delta_bool(delta) != 0) {
+        PyObject *temp = result;
+        result = add_datetime_timedelta((PyDateTime_DateTime *)result,
+                                        (PyDateTime_Delta *)dst, 1);
+        Py_DECREF(temp);
+        if (result == NULL)
+            goto Fail;
+    }
+    Py_DECREF(delta);
+    Py_DECREF(dst);
+    Py_DECREF(off);
     return result;
 
 Inconsistent:
@@ -3197,7 +3078,10 @@
 
     /* fall thru to failure */
 Fail:
-    Py_DECREF(result);
+    Py_XDECREF(off);
+    Py_XDECREF(dst);
+    Py_XDECREF(delta);
+    Py_XDECREF(result);
     return NULL;
 }
 
@@ -3464,18 +3348,14 @@
 }
 
 static PyObject *
-add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta,
-                       int factor);
-
-static PyObject *
 timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt)
 {
-    if (! PyDateTime_Check(dt)) {
+    if (!PyDateTime_Check(dt)) {
         PyErr_SetString(PyExc_TypeError,
                         "fromutc: argument must be a datetime");
         return NULL;
     }
-    if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
+    if (!HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) {
         PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo "
                         "is not self");
         return NULL;
@@ -3689,21 +3569,18 @@
 
 /* These are all METH_NOARGS, so don't need to check the arglist. */
 static PyObject *
-time_utcoffset(PyDateTime_Time *self, PyObject *unused) {
-    return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None,
-                               "utcoffset", Py_None);
+time_utcoffset(PyObject *self, PyObject *unused) {
+    return call_utcoffset(GET_TIME_TZINFO(self), Py_None);
 }
 
 static PyObject *
-time_dst(PyDateTime_Time *self, PyObject *unused) {
-    return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None,
-                               "dst", Py_None);
+time_dst(PyObject *self, PyObject *unused) {
+    return call_dst(GET_TIME_TZINFO(self), Py_None);
 }
 
 static PyObject *
 time_tzname(PyDateTime_Time *self, PyObject *unused) {
-    return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None,
-                       Py_None);
+    return call_tzname(GET_TIME_TZINFO(self), Py_None);
 }
 
 /*
@@ -3758,7 +3635,7 @@
                                       TIME_GET_MINUTE(self),
                                       TIME_GET_SECOND(self));
 
-    if (result == NULL || ! HASTZINFO(self) || self->tzinfo == Py_None)
+    if (result == NULL || !HASTZINFO(self) || self->tzinfo == Py_None)
         return result;
 
     /* We need to append the UTC offset. */
@@ -3809,98 +3686,108 @@
 static PyObject *
 time_richcompare(PyObject *self, PyObject *other, int op)
 {
+    PyObject *result = NULL;
+    PyObject *offset1, *offset2;
     int diff;
-    naivety n1, n2;
-    int offset1, offset2;
 
     if (! PyTime_Check(other)) {
         Py_INCREF(Py_NotImplemented);
         return Py_NotImplemented;
     }
-    if (classify_two_utcoffsets(self, &offset1, &n1, Py_None,
-                                other, &offset2, &n2, Py_None) < 0)
+
+    if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) {
+        diff = memcmp(((PyDateTime_Time *)self)->data,
+                      ((PyDateTime_Time *)other)->data,
+                      _PyDateTime_TIME_DATASIZE);
+        return diff_to_bool(diff, op);
+    }
+    offset1 = time_utcoffset(self, NULL);
+    if (offset1 == NULL)
         return NULL;
-    assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN);
+    offset2 = time_utcoffset(other, NULL);
+    if (offset2 == NULL)
+        goto done;
     /* If they're both naive, or both aware and have the same offsets,
      * we get off cheap.  Note that if they're both naive, offset1 ==
-     * offset2 == 0 at this point.
+     * offset2 == Py_None at this point.
      */
-    if (n1 == n2 && offset1 == offset2) {
+    if ((offset1 == offset2) ||
+        (PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
+         delta_cmp(offset1, offset2) == 0)) {
         diff = memcmp(((PyDateTime_Time *)self)->data,
                       ((PyDateTime_Time *)other)->data,
                       _PyDateTime_TIME_DATASIZE);
-        return diff_to_bool(diff, op);
+        result = diff_to_bool(diff, op);
     }
-
-    if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) {
-        assert(offset1 != offset2);             /* else last "if" handled it */
-        /* Convert everything except microseconds to seconds.  These
-         * can't overflow (no more than the # of seconds in 2 days).
-         */
-        offset1 = TIME_GET_HOUR(self) * 3600 +
-                  (TIME_GET_MINUTE(self) - offset1) * 60 +
-                  TIME_GET_SECOND(self);
-        offset2 = TIME_GET_HOUR(other) * 3600 +
-                  (TIME_GET_MINUTE(other) - offset2) * 60 +
-                  TIME_GET_SECOND(other);
-        diff = offset1 - offset2;
+    /* The hard case: both aware with different UTC offsets */
+    else if (offset1 != Py_None && offset2 != Py_None) {
+        int offsecs1, offsecs2;
+        assert(offset1 != offset2); /* else last "if" handled it */
+        offsecs1 = TIME_GET_HOUR(self) * 3600 +
+                   TIME_GET_MINUTE(self) * 60 +
+                   TIME_GET_SECOND(self) -
+                   GET_TD_DAYS(offset1) * 86400 -
+                   GET_TD_SECONDS(offset1);
+        offsecs2 = TIME_GET_HOUR(other) * 3600 +
+                   TIME_GET_MINUTE(other) * 60 +
+                   TIME_GET_SECOND(other) -
+                   GET_TD_DAYS(offset2) * 86400 -
+                   GET_TD_SECONDS(offset2);
+        diff = offsecs1 - offsecs2;
         if (diff == 0)
             diff = TIME_GET_MICROSECOND(self) -
                    TIME_GET_MICROSECOND(other);
-        return diff_to_bool(diff, op);
+        result = diff_to_bool(diff, op);
     }
-
-    assert(n1 != n2);
-    PyErr_SetString(PyExc_TypeError,
-                    "can't compare offset-naive and "
-                    "offset-aware times");
-    return NULL;
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "can't compare offset-naive and "
+                        "offset-aware times");
+    }
+ done:
+    Py_DECREF(offset1);
+    Py_XDECREF(offset2);
+    return result;
 }
 
 static long
 time_hash(PyDateTime_Time *self)
 {
     if (self->hashcode == -1) {
-        naivety n;
-        int offset;
-        PyObject *temp;
-
-        n = classify_utcoffset((PyObject *)self, Py_None, &offset);
-        assert(n != OFFSET_UNKNOWN);
-        if (n == OFFSET_ERROR)
+        PyObject *offset;
+
+        offset = time_utcoffset((PyObject *)self, NULL);
+
+        if (offset == NULL)
             return -1;
 
         /* Reduce this to a hash of another object. */
-        if (offset == 0) {
+        if (offset == Py_None)
             self->hashcode = generic_hash(
                 (unsigned char *)self->data, _PyDateTime_TIME_DATASIZE);
-            return self->hashcode;
-        }
         else {
-            int hour;
-            int minute;
-
-            assert(n == OFFSET_AWARE);
+            PyObject *temp1, *temp2;
+            int seconds, microseconds;
             assert(HASTZINFO(self));
-            hour = divmod(TIME_GET_HOUR(self) * 60 +
-                            TIME_GET_MINUTE(self) - offset,
-                          60,
-                          &minute);
-            if (0 <= hour && hour < 24)
-                temp = new_time(hour, minute,
-                                TIME_GET_SECOND(self),
-                                TIME_GET_MICROSECOND(self),
-                                Py_None);
-            else
-                temp = Py_BuildValue("iiii",
-                           hour, minute,
-                           TIME_GET_SECOND(self),
-                           TIME_GET_MICROSECOND(self));
-        }
-        if (temp != NULL) {
-            self->hashcode = PyObject_Hash(temp);
-            Py_DECREF(temp);
+            seconds = TIME_GET_HOUR(self) * 3600 +
+                      TIME_GET_MINUTE(self) * 60 +
+                      TIME_GET_SECOND(self);
+            microseconds = TIME_GET_MICROSECOND(self);
+            temp1 = new_delta(0, seconds, microseconds, 1);
+            if (temp1 == NULL) {
+                Py_DECREF(offset);
+                return -1;
+            }
+            temp2 = delta_subtract(temp1, offset);
+            Py_DECREF(temp1);
+            if (temp2 == NULL) {
+                Py_DECREF(offset);
+                return -1;
+            }
+            self->hashcode = PyObject_Hash(temp2);
+            Py_DECREF(temp2);
         }
+        Py_DECREF(offset);
     }
     return self->hashcode;
 }
@@ -3929,10 +3816,10 @@
 }
 
 static int
-time_bool(PyDateTime_Time *self)
+time_bool(PyObject *self)
 {
-    int offset;
-    int none;
+    PyObject *offset, *tzinfo;
+    int offsecs = 0;
 
     if (TIME_GET_SECOND(self) || TIME_GET_MICROSECOND(self)) {
         /* Since utcoffset is in whole minutes, nothing can
@@ -3940,13 +3827,15 @@
          */
         return 1;
     }
-    offset = 0;
-    if (HASTZINFO(self) && self->tzinfo != Py_None) {
-        offset = call_utcoffset(self->tzinfo, Py_None, &none);
-        if (offset == -1 && PyErr_Occurred())
+    tzinfo = GET_TIME_TZINFO(self);
+    if (tzinfo != Py_None) {
+        offset = call_utcoffset(tzinfo, Py_None);
+        if (offset == NULL)
             return -1;
+        offsecs = GET_TD_DAYS(offset)*86400 + GET_TD_SECONDS(offset);
+        Py_DECREF(offset);
     }
-    return (TIME_GET_MINUTE(self) - offset + TIME_GET_HOUR(self)*60) != 0;
+    return (TIME_GET_MINUTE(self)*60 - offsecs + TIME_GET_HOUR(self)*3600) != 0;
 }
 
 /* Pickle support, a simple use of __reduce__. */
@@ -4455,21 +4344,18 @@
 
 /* These are all METH_NOARGS, so don't need to check the arglist. */
 static PyObject *
-datetime_utcoffset(PyDateTime_DateTime *self, PyObject *unused) {
-    return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None,
-                               "utcoffset", (PyObject *)self);
+datetime_utcoffset(PyObject *self, PyObject *unused) {
+    return call_utcoffset(GET_DT_TZINFO(self), self);
 }
 
 static PyObject *
-datetime_dst(PyDateTime_DateTime *self, PyObject *unused) {
-    return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None,
-                               "dst", (PyObject *)self);
+datetime_dst(PyObject *self, PyObject *unused) {
+    return call_dst(GET_DT_TZINFO(self), self);
 }
 
 static PyObject *
-datetime_tzname(PyDateTime_DateTime *self, PyObject *unused) {
-    return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None,
-                       (PyObject *)self);
+datetime_tzname(PyObject *self, PyObject *unused) {
+    return call_tzname(GET_DT_TZINFO(self), self);
 }
 
 /*
@@ -4536,21 +4422,43 @@
         /* datetime - ??? */
         if (PyDateTime_Check(right)) {
             /* datetime - datetime */
-            naivety n1, n2;
-            int offset1, offset2;
+            PyObject *offset1, *offset2, *offdiff = NULL;
             int delta_d, delta_s, delta_us;
 
-            if (classify_two_utcoffsets(left, &offset1, &n1, left,
-                                        right, &offset2, &n2,
-                                        right) < 0)
-                return NULL;
-            assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN);
-            if (n1 != n2) {
-                PyErr_SetString(PyExc_TypeError,
-                    "can't subtract offset-naive and "
-                    "offset-aware datetimes");
-                return NULL;
+            if (GET_DT_TZINFO(left) == GET_DT_TZINFO(right)) {
+                offset2 = offset1 = Py_None;
+                Py_INCREF(offset1);
+                Py_INCREF(offset2);
             }
+            else {
+                offset1 = datetime_utcoffset(left, NULL);
+                if (offset1 == NULL)
+                    return NULL;
+                offset2 = datetime_utcoffset(right, NULL);
+                if (offset2 == NULL) {
+                    Py_DECREF(offset1);
+                    return NULL;
+                }
+                if ((offset1 != Py_None) != (offset2 != Py_None)) {
+                    PyErr_SetString(PyExc_TypeError,
+                                    "can't subtract offset-naive and "
+                                    "offset-aware datetimes");
+                    Py_DECREF(offset1);
+                    Py_DECREF(offset2);
+                    return NULL;
+                }
+            }
+            if ((offset1 != offset2) &&
+                delta_cmp(offset1, offset2) != 0) {
+                offdiff = delta_subtract(offset1, offset2);
+                if (offdiff == NULL) {
+                    Py_DECREF(offset1);
+                    Py_DECREF(offset2);
+                    return NULL;
+                }
+            }
+            Py_DECREF(offset1);
+            Py_DECREF(offset2);
             delta_d = ymd_to_ord(GET_YEAR(left),
                                  GET_MONTH(left),
                                  GET_DAY(left)) -
@@ -4569,11 +4477,13 @@
                        DATE_GET_SECOND(right));
             delta_us = DATE_GET_MICROSECOND(left) -
                        DATE_GET_MICROSECOND(right);
-            /* (left - offset1) - (right - offset2) =
-             * (left - right) + (offset2 - offset1)
-             */
-            delta_s += (offset2 - offset1) * 60;
             result = new_delta(delta_d, delta_s, delta_us, 1);
+            if (offdiff != NULL) {
+                PyObject *temp = result;
+                result = delta_subtract(result, offdiff);
+                Py_DECREF(temp);
+                Py_DECREF(offdiff);
+            }
         }
         else if (PyDelta_Check(right)) {
             /* datetime - delta */
@@ -4683,9 +4593,9 @@
 static PyObject *
 datetime_richcompare(PyObject *self, PyObject *other, int op)
 {
+    PyObject *result = NULL;
+    PyObject *offset1, *offset2;
     int diff;
-    naivety n1, n2;
-    int offset1, offset2;
 
     if (! PyDateTime_Check(other)) {
         if (PyDate_Check(other)) {
@@ -4706,85 +4616,99 @@
         return Py_NotImplemented;
     }
 
-    if (classify_two_utcoffsets(self, &offset1, &n1, self,
-                                other, &offset2, &n2, other) < 0)
+    if (GET_DT_TZINFO(self) == GET_DT_TZINFO(other)) {
+        diff = memcmp(((PyDateTime_DateTime *)self)->data,
+                      ((PyDateTime_DateTime *)other)->data,
+                      _PyDateTime_DATETIME_DATASIZE);
+        return diff_to_bool(diff, op);
+    }
+    offset1 = datetime_utcoffset(self, NULL);
+    if (offset1 == NULL)
         return NULL;
-    assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN);
+    offset2 = datetime_utcoffset(other, NULL);
+    if (offset2 == NULL)
+        goto done;
     /* If they're both naive, or both aware and have the same offsets,
      * we get off cheap.  Note that if they're both naive, offset1 ==
-     * offset2 == 0 at this point.
+     * offset2 == Py_None at this point.
      */
-    if (n1 == n2 && offset1 == offset2) {
+    if ((offset1 == offset2) ||
+        (PyDelta_Check(offset1) && PyDelta_Check(offset2) &&
+         delta_cmp(offset1, offset2) == 0)) {
         diff = memcmp(((PyDateTime_DateTime *)self)->data,
                       ((PyDateTime_DateTime *)other)->data,
                       _PyDateTime_DATETIME_DATASIZE);
-        return diff_to_bool(diff, op);
+        result = diff_to_bool(diff, op);
     }
-
-    if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) {
+    else if (offset1 != Py_None && offset2 != Py_None) {
         PyDateTime_Delta *delta;
 
-        assert(offset1 != offset2);             /* else last "if" handled it */
+        assert(offset1 != offset2); /* else last "if" handled it */
         delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self,
                                                        other);
         if (delta == NULL)
-            return NULL;
+            goto done;
         diff = GET_TD_DAYS(delta);
         if (diff == 0)
             diff = GET_TD_SECONDS(delta) |
                    GET_TD_MICROSECONDS(delta);
         Py_DECREF(delta);
-        return diff_to_bool(diff, op);
+        result = diff_to_bool(diff, op);
     }
-
-    assert(n1 != n2);
-    PyErr_SetString(PyExc_TypeError,
-                    "can't compare offset-naive and "
-                    "offset-aware datetimes");
-    return NULL;
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "can't compare offset-naive and "
+                        "offset-aware datetimes");
+    }
+ done:
+    Py_DECREF(offset1);
+    Py_XDECREF(offset2);
+    return result;
 }
 
 static long
 datetime_hash(PyDateTime_DateTime *self)
 {
     if (self->hashcode == -1) {
-        naivety n;
-        int offset;
-        PyObject *temp;
-
-        n = classify_utcoffset((PyObject *)self, (PyObject *)self,
-                               &offset);
-        assert(n != OFFSET_UNKNOWN);
-        if (n == OFFSET_ERROR)
+        PyObject *offset;
+
+        offset = datetime_utcoffset((PyObject *)self, NULL);
+
+        if (offset == NULL)
             return -1;
 
         /* Reduce this to a hash of another object. */
-        if (n == OFFSET_NAIVE) {
+        if (offset == Py_None)
             self->hashcode = generic_hash(
                 (unsigned char *)self->data, _PyDateTime_DATETIME_DATASIZE);
-            return self->hashcode;
-        }
         else {
-            int days;
-            int seconds;
+            PyObject *temp1, *temp2;
+            int days, seconds;
 
-            assert(n == OFFSET_AWARE);
             assert(HASTZINFO(self));
             days = ymd_to_ord(GET_YEAR(self),
                               GET_MONTH(self),
                               GET_DAY(self));
             seconds = DATE_GET_HOUR(self) * 3600 +
-                      (DATE_GET_MINUTE(self) - offset) * 60 +
+                      DATE_GET_MINUTE(self) * 60 +
                       DATE_GET_SECOND(self);
-            temp = new_delta(days,
-                             seconds,
-                             DATE_GET_MICROSECOND(self),
-                             1);
-        }
-        if (temp != NULL) {
-            self->hashcode = PyObject_Hash(temp);
-            Py_DECREF(temp);
+            temp1 = new_delta(days, seconds,
+                              DATE_GET_MICROSECOND(self),
+                              1);
+            if (temp1 == NULL) {
+                Py_DECREF(offset);
+                return -1;
+            }
+            temp2 = delta_subtract(temp1, offset);
+            Py_DECREF(temp1);
+            if (temp2 == NULL) {
+                Py_DECREF(offset);
+                return -1;
+            }
+            self->hashcode = PyObject_Hash(temp2);
+            Py_DECREF(temp2);
         }
+        Py_DECREF(offset);
     }
     return self->hashcode;
 }
@@ -4819,10 +4743,9 @@
 static PyObject *
 datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
 {
-    int y, m, d, hh, mm, ss, us;
     PyObject *result;
-    int offset, none;
-
+    PyObject *offset;
+    PyObject *temp;
     PyObject *tzinfo;
     static char *keywords[] = {"tz", NULL};
 
@@ -4840,39 +4763,35 @@
     }
 
     /* Convert self to UTC. */
-    offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none);
-    if (offset == -1 && PyErr_Occurred())
+    offset = datetime_utcoffset((PyObject *)self, NULL);
+    if (offset == NULL)
         return NULL;
-    if (none)
-        goto NeedAware;
+    if (offset == Py_None) {
+        Py_DECREF(offset);
+      NeedAware:
+        PyErr_SetString(PyExc_ValueError, "astimezone() cannot be applied to "
+                        "a naive datetime");
+        return NULL;
+    }
 
-    y = GET_YEAR(self);
-    m = GET_MONTH(self);
-    d = GET_DAY(self);
-    hh = DATE_GET_HOUR(self);
-    mm = DATE_GET_MINUTE(self);
-    ss = DATE_GET_SECOND(self);
-    us = DATE_GET_MICROSECOND(self);
-
-    mm -= offset;
-    if ((mm < 0 || mm >= 60) &&
-        normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
+    /* result = self - offset */
+    result = add_datetime_timedelta(self,
+                (PyDateTime_Delta *)offset, -1);
+    Py_DECREF(offset);
+    if (result == NULL)
         return NULL;
 
     /* Attach new tzinfo and let fromutc() do the rest. */
-    result = new_datetime(y, m, d, hh, mm, ss, us, tzinfo);
-    if (result != NULL) {
-        PyObject *temp = result;
+    temp = ((PyDateTime_DateTime *)result)->tzinfo;
+    ((PyDateTime_DateTime *)result)->tzinfo = tzinfo;
+    Py_INCREF(tzinfo);
+    Py_DECREF(temp);
 
-        result = PyObject_CallMethod(tzinfo, "fromutc", "O", temp);
-        Py_DECREF(temp);
-    }
-    return result;
+    temp = result;
+    result = PyObject_CallMethod(tzinfo, "fromutc", "O", temp);
+    Py_DECREF(temp);
 
-NeedAware:
-    PyErr_SetString(PyExc_ValueError, "astimezone() cannot be applied to "
-                                      "a naive datetime");
-    return NULL;
+    return result;
 }
 
 static PyObject *
@@ -4881,17 +4800,15 @@
     int dstflag = -1;
 
     if (HASTZINFO(self) && self->tzinfo != Py_None) {
-        int none;
+        PyObject * dst;
 
-        dstflag = call_dst(self->tzinfo, (PyObject *)self, &none);
-        if (dstflag == -1 && PyErr_Occurred())
+        dst = call_dst(self->tzinfo, (PyObject *)self);
+        if (dst == NULL)
             return NULL;
 
-        if (none)
-            dstflag = -1;
-        else if (dstflag != 0)
-            dstflag = 1;
-
+        if (dst != Py_None)
+            dstflag = delta_bool((PyDateTime_Delta *)dst);
+        Py_DECREF(dst);
     }
     return build_struct_time(GET_YEAR(self),
                              GET_MONTH(self),
@@ -4927,41 +4844,47 @@
                     DATE_GET_MINUTE(self),
                     DATE_GET_SECOND(self),
                     DATE_GET_MICROSECOND(self),
-                    HASTZINFO(self) ? self->tzinfo : Py_None);
+                    GET_DT_TZINFO(self));
 }
 
 static PyObject *
 datetime_utctimetuple(PyDateTime_DateTime *self)
 {
-    int y = GET_YEAR(self);
-    int m = GET_MONTH(self);
-    int d = GET_DAY(self);
-    int hh = DATE_GET_HOUR(self);
-    int mm = DATE_GET_MINUTE(self);
-    int ss = DATE_GET_SECOND(self);
-    int us = 0;         /* microseconds are ignored in a timetuple */
-    int offset = 0;
-
-    if (HASTZINFO(self) && self->tzinfo != Py_None) {
-        int none;
+    int y, m, d, hh, mm, ss;
+    PyObject *tzinfo;
+    PyDateTime_DateTime *utcself;
 
-        offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none);
-        if (offset == -1 && PyErr_Occurred())
-            return NULL;
+    tzinfo = GET_DT_TZINFO(self);
+    if (tzinfo == Py_None) {
+        utcself = self;
+        Py_INCREF(utcself);
     }
-    /* Even if offset is 0, don't call timetuple() -- tm_isdst should be
-     * 0 in a UTC timetuple regardless of what dst() says.
-     */
-    if (offset) {
-        /* Subtract offset minutes & normalize. */
-        int stat;
-
-        mm -= offset;
-        stat = normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us);
-        /* OverflowError may be raised in the edge cases. */
-        if (stat < 0)
+    else {
+        PyObject *offset;
+        offset = call_utcoffset(tzinfo, (PyObject *)self);
+        if (offset == NULL)
             return NULL;
+        if (offset == Py_None) {
+            Py_DECREF(offset);
+            utcself = self;
+            Py_INCREF(utcself);
+        }
+        else {
+            utcself = (PyDateTime_DateTime *)add_datetime_timedelta(self,
+                                                (PyDateTime_Delta *)offset, -1);
+            Py_DECREF(offset);
+            if (utcself == NULL)
+                return NULL;
+        }
     }
+    y = GET_YEAR(utcself);
+    m = GET_MONTH(utcself);
+    d = GET_DAY(utcself);
+    hh = DATE_GET_HOUR(utcself);
+    mm = DATE_GET_MINUTE(utcself);
+    ss = DATE_GET_SECOND(utcself);
+
+    Py_DECREF(utcself);
     return build_struct_time(y, m, d, hh, mm, ss, 0);
 }
 


More information about the Python-checkins mailing list