diff options
author | Tim Peters <tim.peters@gmail.com> | 2003-01-01 04:48:01 (GMT) |
---|---|---|
committer | Tim Peters <tim.peters@gmail.com> | 2003-01-01 04:48:01 (GMT) |
commit | b5a16f33758bb3d98bed0782c7b61c754ea494f6 (patch) | |
tree | 890c0e7e72b8537fdfae865b86ae1a5e70300881 /Modules | |
parent | 36087edc05b6ccb4a4850ac579ad721d1a72ffdd (diff) | |
download | cpython-b5a16f33758bb3d98bed0782c7b61c754ea494f6.zip cpython-b5a16f33758bb3d98bed0782c7b61c754ea494f6.tar.gz cpython-b5a16f33758bb3d98bed0782c7b61c754ea494f6.tar.bz2 |
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.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/datetimemodule.c | 70 |
1 files changed, 38 insertions, 32 deletions
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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, PyObject *result; PyObject *temp; - int myoff, otoff, newoff; + int selfoff, resoff, tempoff, total_added_to_result; int none; PyObject *tzinfo; @@ -4776,21 +4776,23 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, /* 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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, * 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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, 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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, * 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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, 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 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, 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"); |