[Python-checkins] python/dist/src/Modules datetimemodule.c,1.20,1.21

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
2002年12月31日 09:36:59 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory sc8-pr-cvs1:/tmp/cvs-serv6382/python/Modules
Modified Files:
	datetimemodule.c 
Log Message:
A new, and much hairier, implementation of astimezone(), building on
an idea from Guido. This restores that the datetime implementation
never passes a datetime d to a tzinfo method unless d.tzinfo is the
tzinfo instance whose method is being called. That in turn allows
enormous simplifications in user-written tzinfo classes (see the Python
sandbox US.py and EU.py for fully fleshed-out examples).
d.astimezone(tz) also raises ValueError now if d lands in the one hour
of the year that can't be expressed in tz (this can happen iff tz models
both standard and daylight time). That it used to return a nonsense
result always ate at me, and it turned out that it seemed impossible to
force a consistent nonsense result under the new implementation (which
doesn't know anything about how tzinfo classes implement their methods --
it can only infer properties indirectly). Guido doesn't like this --
expect it to change.
New tests of conversion between adjacent DST-aware timezones don't pass
yet, and are commented out.
Running the datetime tests in a loop under a debug build leaks 9
references per test run, but I don't believe the datetime code is the
cause (it didn't leak the last time I changed the C code, and the leak
is the same if I disable all the tests that invoke the only function
that changed here). I'll pursue that next.
Index: datetimemodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/datetimemodule.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -C2 -d -r1.20 -r1.21
*** datetimemodule.c	30 Dec 2002 21:28:52 -0000	1.20
--- datetimemodule.c	31 Dec 2002 17:36:56 -0000	1.21
***************
*** 4752,4755 ****
--- 4752,4760 ----
 	int us = DATE_GET_MICROSECOND(self);
 
+ 	PyObject *result;
+ 	PyObject *temp;
+ 	int myoff, otoff, newoff;
+ 	int none;
+ 
 	PyObject *tzinfo;
 	static char *keywords[] = {"tz", NULL};
***************
*** 4761,4788 ****
 		return NULL;
 
! 	if (tzinfo != Py_None && self->tzinfo != Py_None) {
! 		int none;
! 		int selfoffset;
! 		selfoffset = call_utcoffset(self->tzinfo,
! 					 (PyObject *)self,
! 					 &none);
! 	 if (selfoffset == -1 && PyErr_Occurred())
! 	 	return NULL;
! 	 if (! none) {
! 			int tzoffset;
! 	 	tzoffset = call_utcoffset(tzinfo,
! 	 				 (PyObject *)self,
! 	 				 &none);
! 	 	if (tzoffset == -1 && PyErr_Occurred())
! 	 		return NULL;
! 	 	if (! none) {
! 	 		mm -= selfoffset - tzoffset;
! 	 		if (normalize_datetime(&y, &m, &d,
! 	 				 &hh, &mm, &ss, &us) < 0)
! 	 			return NULL;
! 	 	}
! 	 }
 	}
! 	return new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
 }
 
--- 4766,4890 ----
 		return NULL;
 
! /* Don't call utcoffset unless necessary. */
! 	result = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
! 	if (result == NULL ||
! 	 tzinfo == Py_None ||
! 	 self->tzinfo == Py_None ||
! 	 self->tzinfo == tzinfo)
! 		return result;
! 
! /* 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())
! 		goto Fail;
! 	if (none)
! 		return result;
! 
! 	otoff = call_utcoffset(tzinfo, result, &none);
! 	if (otoff == -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)
! 		goto Fail;
! 	temp = new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
! 	if (temp == NULL)
! 		goto Fail;
! 	Py_DECREF(result);
! 	result = temp;
! 
! 	/* If tz is a fixed-offset class, we're done, but we can't know
! 	 * whether it is. If it's a DST-aware class, and we're not near a
! 	 * DST boundary, we're also done. If we crossed a DST boundary,
! 	 * the offset will be different now, and that's our only clue.
! 	 * 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())
! 		goto Fail;
! 	if (none)
! 		goto Inconsistent;
! 
! 	if (newoff != otoff) {
! 		/* We did cross a boundary. Try to correct. */
! 		mm += newoff - otoff;
! 		if (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;
! 
! 		otoff = call_utcoffset(tzinfo, result, &none);
! 		if (otoff == -1 && PyErr_Occurred())
! 			goto Fail;
! 		if (none)
! 			goto Inconsistent;
! }
! 	/* If this is the first hour of DST, it may be a local time that
! 	 * doesn't make sense on the local clock, in which case the naive
! 	 * hour before it (in standard time) is equivalent and does make
! 	 * sense on the local clock. So force that.
! 	 */
! 	hh -= 1;
! 	if (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()) {
! 		Py_DECREF(temp);
! 		goto Fail;
 	}
! 	if (none) {
! 		Py_DECREF(temp);
! 		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
! 	 */
! 	if (otoff - newoff == 60) {
! 		/* use the local time that makes sense */
! 		Py_DECREF(result);
! 		return temp;
! 	}
! 	Py_DECREF(temp);
! 
! 	/* There's still a problem with the unspellable (in local time)
! 	 * hour after DST ends.
! 	 */
! 	temp = datetime_richcompare((PyDateTime_DateTime *)self,
! 				 result, Py_EQ);
! 	if (temp == NULL)
! 		goto Fail;
! 	if (temp == Py_True) {
! 		Py_DECREF(temp);
! 		return result;
! 	}
! 	Py_DECREF(temp);
! /* Else there's no way to spell self in zone other.tz. */
! 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 */
! Fail:
! 	Py_DECREF(result);
! 	return NULL;
 }
 

AltStyle によって変換されたページ (->オリジナル) /