[Python-checkins] python/dist/src/Modules datetimemodule.c,1.30,1.31

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Fri, 03 Jan 2003 22:03:18 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv8819/python/Modules

Modified Files:
	datetimemodule.c 
Log Message:
A new implementation of astimezone() that does what we agreed on in all
cases, plus even tougher tests of that.  This implementation follows
the correctness proof very closely, and should also be quicker (yes,
I wrote the proof before the code, and the code proves the proof <wink>).


Index: datetimemodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/datetimemodule.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -C2 -d -r1.30 -r1.31
*** datetimemodule.c	4 Jan 2003 01:02:25 -0000	1.30
--- datetimemodule.c	4 Jan 2003 06:03:15 -0000	1.31
***************
*** 4755,4759 ****
  	PyObject *result;
  	PyObject *temp;
! 	int selfoff, resoff, resdst, total_added_to_result;
  	int none;
  	int delta;
--- 4755,4759 ----
  	PyObject *result;
  	PyObject *temp;
! 	int selfoff, resoff, dst1, dst2;
  	int none;
  	int delta;
***************
*** 4793,4800 ****
  	/* See the long comment block at the end of this file for an
  	 * explanation of this algorithm.  That it always works requires a
! 	 * pretty intricate proof.
  	 */
! 	resdst = call_dst(tzinfo, result, &none);
! 	if (resdst == -1 && PyErr_Occurred())
  		goto Fail;
  	if (none) {
--- 4793,4805 ----
  	/* See the long comment block at the end of this file for an
  	 * explanation of this algorithm.  That it always works requires a
! 	 * pretty intricate proof.  There are many equivalent ways to code
! 	 * up the proof as an algorithm.  This way favors calling dst() over
! 	 * calling utcoffset(), because "the usual" utcoffset() calls dst()
! 	 * itself, and calling the latter instead saves a Python-level
! 	 * function call.  This way of coding it also follows the proof
! 	 * closely, w/ x=self, y=result, z=result, and z'=temp.
  	 */
! 	dst1 = call_dst(tzinfo, result, &none);
! 	if (dst1 == -1 && PyErr_Occurred())
  		goto Fail;
  	if (none) {
***************
*** 4803,4809 ****
  		goto Fail;
  	}
! 	total_added_to_result = resoff - resdst - selfoff;
! 	if (total_added_to_result != 0) {
! 		mm += total_added_to_result;
  		if ((mm < 0 || mm >= 60) &&
  		    normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
--- 4808,4814 ----
  		goto Fail;
  	}
! 	delta = resoff - dst1 - selfoff;
! 	if (delta) {
! 		mm += delta;
  		if ((mm < 0 || mm >= 60) &&
  		    normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0)
***************
*** 4815,4870 ****
  		result = temp;
  
! 		resoff = call_utcoffset(tzinfo, result, &none);
! 		if (resoff == -1 && PyErr_Occurred())
  			goto Fail;
  		if (none)
  			goto Inconsistent;
  	}
! 
! 	/* The distance now from self to result is
! 	 * self - result == naive(self) - selfoff - (naive(result) - resoff) ==
! 	 * naive(self) - selfoff -
! 	 *             ((naive(self) + total_added_to_result - resoff) ==
! 	 * - selfoff - total_added_to_result + resoff.
! 	 */
! 	delta = resoff - selfoff - total_added_to_result;
! 
! 	/* Now self and result are the same UTC time iff delta is 0.
! 	 * If it is 0, we're done, although that takes some proving.
! 	 */
! 	if (delta == 0)
  		return result;
  
! 	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)
  		goto Fail;
- 	Py_DECREF(result);
- 	result = temp;
  
! 	resoff = call_utcoffset(tzinfo, result, &none);
! 	if (resoff == -1 && PyErr_Occurred())
  		goto Fail;
! 	if (none)
  		goto Inconsistent;
  
! 	if (resoff - selfoff == total_added_to_result)
! 		/* self and result are the same UTC time */
! 		return result;
! 
!         /* 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");
!         goto Fail;
  
  Inconsistent:
! 	PyErr_SetString(PyExc_ValueError, "astimezone(): tz.utcoffset() "
! 			"gave inconsistent results; cannot convert");
  
  	/* fall thru to failure */
--- 4820,4864 ----
  		result = temp;
  
! 		dst1 = call_dst(tzinfo, result, &none);
! 		if (dst1 == -1 && PyErr_Occurred())
  			goto Fail;
  		if (none)
  			goto Inconsistent;
  	}
! 	if (dst1 == 0)
  		return result;
  
! 	mm += dst1;
  	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)
  		goto Fail;
  
! 	dst2 = call_dst(tzinfo, temp, &none);
! 	if (dst2 == -1 && PyErr_Occurred()) {
! 		Py_DECREF(temp);
  		goto Fail;
! 	}
! 	if (none) {
! 		Py_DECREF(temp);
  		goto Inconsistent;
+ 	}
  
! 	if (dst1 == dst2) {
! 		/* The normal case:  we want temp, not result. */
! 		Py_DECREF(result);
! 		result = temp;
! 	}
! 	else {
! 		/* The "unspellable hour" at the end of DST. */
! 		Py_DECREF(temp);
! 	}
! 	return result;
  
  Inconsistent:
! 	PyErr_SetString(PyExc_ValueError, "astimezone(): tz.dst() gave"
! 			"inconsistent results; cannot convert");
  
  	/* fall thru to failure */