@@ -4754,7 +4754,7 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
47544754
47554755 PyObject * result ;
47564756 PyObject * temp ;
4757- int selfoff , resoff , resdst , total_added_to_result ;
4757+ int selfoff , resoff , dst1 , dst2 ;
47584758 int none ;
47594759 int delta ;
47604760
@@ -4792,19 +4792,24 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
47924792
47934793 /* See the long comment block at the end of this file for an
47944794 * explanation of this algorithm. That it always works requires a
4795- * pretty intricate proof.
4795+ * pretty intricate proof. There are many equivalent ways to code
4796+ * up the proof as an algorithm. This way favors calling dst() over
4797+ * calling utcoffset(), because "the usual" utcoffset() calls dst()
4798+ * itself, and calling the latter instead saves a Python-level
4799+ * function call. This way of coding it also follows the proof
4800+ * closely, w/ x=self, y=result, z=result, and z'=temp.
47964801 */
4797- resdst = call_dst (tzinfo , result , & none );
4798- if (resdst == -1 && PyErr_Occurred ())
4802+ dst1 = call_dst (tzinfo , result , & none );
4803+ if (dst1 == -1 && PyErr_Occurred ())
47994804 goto Fail ;
48004805 if (none ) {
48014806 PyErr_SetString (PyExc_ValueError , "astimezone(): utcoffset() "
48024807 "returned a duration but dst() returned None" );
48034808 goto Fail ;
48044809 }
4805- total_added_to_result = resoff - resdst - selfoff ;
4806- if (total_added_to_result != 0 ) {
4807- mm += total_added_to_result ;
4810+ delta = resoff - dst1 - selfoff ;
4811+ if (delta ) {
4812+ mm += delta ;
48084813 if ((mm < 0 || mm >= 60 ) &&
48094814 normalize_datetime (& y , & m , & d , & hh , & mm , & ss , & us ) < 0 )
48104815 goto Fail ;
@@ -4814,58 +4819,47 @@ datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
48144819 Py_DECREF (result );
48154820 result = temp ;
48164821
4817- resoff = call_utcoffset (tzinfo , result , & none );
4818- if (resoff == -1 && PyErr_Occurred ())
4822+ dst1 = call_dst (tzinfo , result , & none );
4823+ if (dst1 == -1 && PyErr_Occurred ())
48194824 goto Fail ;
48204825 if (none )
48214826 goto Inconsistent ;
48224827 }
4823-
4824- /* The distance now from self to result is
4825- * self - result == naive(self) - selfoff - (naive(result) - resoff) ==
4826- * naive(self) - selfoff -
4827- * ((naive(self) + total_added_to_result - resoff) ==
4828- * - selfoff - total_added_to_result + resoff.
4829- */
4830- delta = resoff - selfoff - total_added_to_result ;
4831-
4832- /* Now self and result are the same UTC time iff delta is 0.
4833- * If it is 0, we're done, although that takes some proving.
4834- */
4835- if (delta == 0 )
4828+ if (dst1 == 0 )
48364829 return result ;
48374830
4838- total_added_to_result += delta ;
4839- mm += delta ;
4831+ mm += dst1 ;
48404832 if ((mm < 0 || mm >= 60 ) &&
48414833 normalize_datetime (& y , & m , & d , & hh , & mm , & ss , & us ) < 0 )
48424834 goto Fail ;
4843-
48444835 temp = new_datetimetz (y , m , d , hh , mm , ss , us , tzinfo );
48454836 if (temp == NULL )
48464837 goto Fail ;
4847- Py_DECREF (result );
4848- result = temp ;
48494838
4850- resoff = call_utcoffset (tzinfo , result , & none );
4851- if (resoff == -1 && PyErr_Occurred ())
4839+ dst2 = call_dst (tzinfo , temp , & none );
4840+ if (dst2 == -1 && PyErr_Occurred ()) {
4841+ Py_DECREF (temp );
48524842 goto Fail ;
4853- if (none )
4843+ }
4844+ if (none ) {
4845+ Py_DECREF (temp );
48544846 goto Inconsistent ;
4847+ }
48554848
4856- if (resoff - selfoff == total_added_to_result )
4857- /* self and result are the same UTC time */
4858- return result ;
4859-
4860- /* Else there's no way to spell self in zone tzinfo. */
4861- PyErr_SetString (PyExc_ValueError , "astimezone(): the source "
4862- "datetimetz can't be expressed in the target "
4863- "timezone's local time" );
4864- goto Fail ;
4849+ if (dst1 == dst2 ) {
4850+ /* The normal case: we want temp, not result. */
4851+ Py_DECREF (result );
4852+ result = temp ;
4853+ }
4854+ else {
4855+ /* The "unspellable hour" at the end of DST. */
4856+ Py_DECREF (temp );
4857+ }
4858+ return result ;
48654859
48664860Inconsistent :
4867- PyErr_SetString (PyExc_ValueError , "astimezone(): tz.utcoffset () "
4868- "gave inconsistent results; cannot convert" );
4861+ PyErr_SetString (PyExc_ValueError , "astimezone(): tz.dst () gave "
4862+ "inconsistent results; cannot convert" );
48694863
48704864 /* fall thru to failure */
48714865Fail :
0 commit comments