datetimetz_astimezone():  Speed optimizations -- although I'd rather
find a more elegant algorithm (OTOH, the hairy new implementation allows
user-written tzinfo classes to be elegant, so it's a big win even if
astimezone() remains hairy).

Darn!  I've only got 10 minutes left to get falling-down drunk!  I suppose
I'll have to smoke crack instead now.
diff --git a/Modules/datetimemodule.c b/Modules/datetimemodule.c
index 40f4773..fb1f9e1 100644
--- a/Modules/datetimemodule.c
+++ b/Modules/datetimemodule.c
@@ -4753,7 +4753,7 @@
 
 	PyObject *result;
 	PyObject *temp;
-	int myoff, otoff, newoff;
+	int selfoff, resoff, tempoff, total_added_to_result;
 	int none;
 
 	PyObject *tzinfo;
@@ -4776,21 +4776,23 @@
         /* Get the offsets.  If either object turns out to be naive, again
          * there's no conversion of date or time fields.
          */
-	myoff = call_utcoffset(self->tzinfo, (PyObject *)self, &none);
-	if (myoff == -1 && PyErr_Occurred())
+	selfoff = call_utcoffset(self->tzinfo, (PyObject *)self, &none);
+	if (selfoff == -1 && PyErr_Occurred())
 		goto Fail;
 	if (none)
 		return result;
 
-	otoff = call_utcoffset(tzinfo, result, &none);
-	if (otoff == -1 && PyErr_Occurred())
+	resoff = call_utcoffset(tzinfo, result, &none);
+	if (resoff == -1 && PyErr_Occurred())
 		goto Fail;
 	if (none)
 		return result;
 
-	/* Add otoff-myoff to result. */
-	mm += otoff - myoff;
-	if (normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
+	/* Add resoff-selfoff to result. */
+	total_added_to_result = resoff - selfoff;
+	mm += total_added_to_result;
+	if ((mm < 0 || mm >= 60) &&
+	    normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
 		goto Fail;
 	temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
 	if (temp == NULL)
@@ -4805,16 +4807,19 @@
 	 * Unfortunately, we can be in trouble even if we didn't cross a
 	 * DST boundary, if we landed on one of the DST "problem hours".
 	 */
-	newoff = call_utcoffset(tzinfo, result, &none);
-	if (newoff == -1 && PyErr_Occurred())
+	tempoff = call_utcoffset(tzinfo, result, &none);
+	if (tempoff == -1 && PyErr_Occurred())
 		goto Fail;
 	if (none)
 		goto Inconsistent;
 
-	if (newoff != otoff) {
+	if (tempoff != resoff) {
 		/* We did cross a boundary.  Try to correct. */
-		mm += newoff - otoff;
-		if (normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
+		const int delta = tempoff - resoff;
+		total_added_to_result += delta;
+		mm += delta;
+		if ((mm < 0 || mm >= 60) &&
+		    normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
 			goto Fail;
 		temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
 		if (temp == NULL)
@@ -4822,8 +4827,8 @@
 		Py_DECREF(result);
 		result = temp;
 
-		otoff = call_utcoffset(tzinfo, result, &none);
-		if (otoff == -1 && PyErr_Occurred())
+		resoff = call_utcoffset(tzinfo, result, &none);
+		if (resoff == -1 && PyErr_Occurred())
 			goto Fail;
 		if (none)
 			goto Inconsistent;
@@ -4834,13 +4839,13 @@
 	 * sense on the local clock.  So force that.
 	 */
 	hh -= 1;
-	if (normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
+	if (hh < 0 && normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
 		goto Fail;
 	temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
 	if (temp == NULL)
 		goto Fail;
-	newoff = call_utcoffset(tzinfo, temp, &none);
-	if (newoff == -1 && PyErr_Occurred()) {
+	tempoff = call_utcoffset(tzinfo, temp, &none);
+	if (tempoff == -1 && PyErr_Occurred()) {
 		Py_DECREF(temp);
 		goto Fail;
 	}
@@ -4849,11 +4854,11 @@
 		goto Inconsistent;
 	}
 	/* Are temp and result really the same time?  temp == result iff
-	 * temp - newoff == result - otoff, iff
-	 * (result - HOUR) - newoff = result - otoff, iff
-	 * otoff - newoff == HOUR
+	 * temp - tempoff == result - resoff, iff
+	 * (result - HOUR) - tempoff = result - resoff, iff
+	 * resoff - tempoff == HOUR
 	 */
-	if (otoff - newoff == 60) {
+	if (resoff - tempoff == 60) {
 		/* use the local time that makes sense */
 		Py_DECREF(result);
 		return temp;
@@ -4861,18 +4866,19 @@
 	Py_DECREF(temp);
 
 	/* There's still a problem with the unspellable (in local time)
-	 * hour after DST ends.
+	 * hour after DST ends.  If self and result map to the same UTC time
+	 * time, we're OK, else the hour is unrepresentable in the tzinfo
+	 * zone.  The result's local time now is
+	 * self + total_added_to_result, so self == result iff
+	 * self - selfoff == result - resoff, iff
+	 * self - selfoff == (self + total_added_to_result) - resoff, iff
+	 * - selfoff == total_added_to_result - resoff, iff
+	 * total_added_to_result == resoff - selfoff
 	 */
-	temp = datetime_richcompare((PyDateTime_DateTime *)self,
-				    result, Py_EQ);
-	if (temp == NULL)
-		goto Fail;
-	if (temp == Py_True) {
-		Py_DECREF(temp);
+	if (total_added_to_result == resoff - selfoff)
 		return result;
-	}
-	Py_DECREF(temp);
-        /* Else there's no way to spell self in zone other.tz. */
+
+        /* Else there's no way to spell self in zone tzinfo. */
         PyErr_SetString(PyExc_ValueError, "astimezone(): the source "
         		"datetimetz can't be expressed in the target "
         		"timezone's local time");