Commit b5a16f33 authored by Tim Peters's avatar Tim Peters

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.
parent 36087edc
...@@ -4753,7 +4753,7 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4753,7 +4753,7 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
PyObject *result; PyObject *result;
PyObject *temp; PyObject *temp;
int myoff, otoff, newoff; int selfoff, resoff, tempoff, total_added_to_result;
int none; int none;
PyObject *tzinfo; PyObject *tzinfo;
...@@ -4776,21 +4776,23 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4776,21 +4776,23 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
/* Get the offsets. If either object turns out to be naive, again /* Get the offsets. If either object turns out to be naive, again
* there's no conversion of date or time fields. * there's no conversion of date or time fields.
*/ */
myoff = call_utcoffset(self->tzinfo, (PyObject *)self, &none); selfoff = call_utcoffset(self->tzinfo, (PyObject *)self, &none);
if (myoff == -1 && PyErr_Occurred()) if (selfoff == -1 && PyErr_Occurred())
goto Fail; goto Fail;
if (none) if (none)
return result; return result;
otoff = call_utcoffset(tzinfo, result, &none); resoff = call_utcoffset(tzinfo, result, &none);
if (otoff == -1 && PyErr_Occurred()) if (resoff == -1 && PyErr_Occurred())
goto Fail; goto Fail;
if (none) if (none)
return result; return result;
/* Add otoff-myoff to result. */ /* Add resoff-selfoff to result. */
mm += otoff - myoff; total_added_to_result = resoff - selfoff;
if (normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) mm += total_added_to_result;
if ((mm < 0 || mm >= 60) &&
normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
goto Fail; goto Fail;
temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo); temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
if (temp == NULL) if (temp == NULL)
...@@ -4805,16 +4807,19 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4805,16 +4807,19 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
* Unfortunately, we can be in trouble even if we didn't cross a * 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". * DST boundary, if we landed on one of the DST "problem hours".
*/ */
newoff = call_utcoffset(tzinfo, result, &none); tempoff = call_utcoffset(tzinfo, result, &none);
if (newoff == -1 && PyErr_Occurred()) if (tempoff == -1 && PyErr_Occurred())
goto Fail; goto Fail;
if (none) if (none)
goto Inconsistent; goto Inconsistent;
if (newoff != otoff) { if (tempoff != resoff) {
/* We did cross a boundary. Try to correct. */ /* We did cross a boundary. Try to correct. */
mm += newoff - otoff; const int delta = tempoff - resoff;
if (normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) total_added_to_result += delta;
mm += delta;
if ((mm < 0 || mm >= 60) &&
normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
goto Fail; goto Fail;
temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo); temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
if (temp == NULL) if (temp == NULL)
...@@ -4822,8 +4827,8 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4822,8 +4827,8 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
Py_DECREF(result); Py_DECREF(result);
result = temp; result = temp;
otoff = call_utcoffset(tzinfo, result, &none); resoff = call_utcoffset(tzinfo, result, &none);
if (otoff == -1 && PyErr_Occurred()) if (resoff == -1 && PyErr_Occurred())
goto Fail; goto Fail;
if (none) if (none)
goto Inconsistent; goto Inconsistent;
...@@ -4834,13 +4839,13 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4834,13 +4839,13 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
* sense on the local clock. So force that. * sense on the local clock. So force that.
*/ */
hh -= 1; 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; goto Fail;
temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo); temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
if (temp == NULL) if (temp == NULL)
goto Fail; goto Fail;
newoff = call_utcoffset(tzinfo, temp, &none); tempoff = call_utcoffset(tzinfo, temp, &none);
if (newoff == -1 && PyErr_Occurred()) { if (tempoff == -1 && PyErr_Occurred()) {
Py_DECREF(temp); Py_DECREF(temp);
goto Fail; goto Fail;
} }
...@@ -4849,11 +4854,11 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4849,11 +4854,11 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
goto Inconsistent; goto Inconsistent;
} }
/* Are temp and result really the same time? temp == result iff /* Are temp and result really the same time? temp == result iff
* temp - newoff == result - otoff, iff * temp - tempoff == result - resoff, iff
* (result - HOUR) - newoff = result - otoff, iff * (result - HOUR) - tempoff = result - resoff, iff
* otoff - newoff == HOUR * resoff - tempoff == HOUR
*/ */
if (otoff - newoff == 60) { if (resoff - tempoff == 60) {
/* use the local time that makes sense */ /* use the local time that makes sense */
Py_DECREF(result); Py_DECREF(result);
return temp; return temp;
...@@ -4861,18 +4866,19 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, ...@@ -4861,18 +4866,19 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
Py_DECREF(temp); Py_DECREF(temp);
/* There's still a problem with the unspellable (in local time) /* 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
temp = datetime_richcompare((PyDateTime_DateTime *)self, * zone. The result's local time now is
result, Py_EQ); * self + total_added_to_result, so self == result iff
if (temp == NULL) * self - selfoff == result - resoff, iff
goto Fail; * self - selfoff == (self + total_added_to_result) - resoff, iff
if (temp == Py_True) { * - selfoff == total_added_to_result - resoff, iff
Py_DECREF(temp); * total_added_to_result == resoff - selfoff
*/
if (total_added_to_result == resoff - selfoff)
return result; return result;
}
Py_DECREF(temp); /* Else there's no way to spell self in zone tzinfo. */
/* Else there's no way to spell self in zone other.tz. */
PyErr_SetString(PyExc_ValueError, "astimezone(): the source " PyErr_SetString(PyExc_ValueError, "astimezone(): the source "
"datetimetz can't be expressed in the target " "datetimetz can't be expressed in the target "
"timezone's local time"); "timezone's local time");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment