mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Implemented a Wiki suggestion:
{timetz,datetimetz}.{utcoffset,dst}() now return a timedelta (or None)
instead of an int (or None).
tzinfo.{utcoffset,dst)() can now return a timedelta (or an int, or None).
Curiously, this was much easier to do in the C implementation than in the
Python implementation (which lives in the Zope3 code tree) -- the C code
already had lots of hair to extract C ints from offset objects, and used
C ints internally.
			
			
This commit is contained in:
		
							parent
							
								
									18091540db
								
							
						
					
					
						commit
						855fe88b24
					
				
					 2 changed files with 246 additions and 152 deletions
				
			
		|  | @ -1458,9 +1458,110 @@ def test_bool(self): | ||||||
|         self.failUnless(not cls(0)) |         self.failUnless(not cls(0)) | ||||||
|         self.failUnless(not cls()) |         self.failUnless(not cls()) | ||||||
| 
 | 
 | ||||||
|  | # A mixin for classes with a tzinfo= argument.  Subclasses must define | ||||||
|  | # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever) | ||||||
|  | # must be legit (which is true for timetz and datetimetz). | ||||||
|  | class TZInfoBase(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
| class TestTimeTZ(TestTime): |     def test_bad_tzinfo_classes(self): | ||||||
|  |         cls = self.theclass | ||||||
|  |         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12) | ||||||
| 
 | 
 | ||||||
|  |         class NiceTry(object): | ||||||
|  |             def __init__(self): pass | ||||||
|  |             def utcoffset(self, dt): pass | ||||||
|  |         self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry) | ||||||
|  | 
 | ||||||
|  |         class BetterTry(tzinfo): | ||||||
|  |             def __init__(self): pass | ||||||
|  |             def utcoffset(self, dt): pass | ||||||
|  |         b = BetterTry() | ||||||
|  |         t = cls(1, 1, 1, tzinfo=b) | ||||||
|  |         self.failUnless(t.tzinfo is b) | ||||||
|  | 
 | ||||||
|  |     def test_utc_offset_out_of_bounds(self): | ||||||
|  |         class Edgy(tzinfo): | ||||||
|  |             def __init__(self, offset): | ||||||
|  |                 self.offset = offset | ||||||
|  |             def utcoffset(self, dt): | ||||||
|  |                 return self.offset | ||||||
|  | 
 | ||||||
|  |         cls = self.theclass | ||||||
|  |         for offset, legit in ((-1440, False), | ||||||
|  |                               (-1439, True), | ||||||
|  |                               (1439, True), | ||||||
|  |                               (1440, False)): | ||||||
|  |             if cls is timetz: | ||||||
|  |                 t = cls(1, 2, 3, tzinfo=Edgy(offset)) | ||||||
|  |             elif cls is datetimetz: | ||||||
|  |                 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset)) | ||||||
|  |             if legit: | ||||||
|  |                 aofs = abs(offset) | ||||||
|  |                 h, m = divmod(aofs, 60) | ||||||
|  |                 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m) | ||||||
|  |                 if isinstance(t, datetimetz): | ||||||
|  |                     t = t.timetz() | ||||||
|  |                 self.assertEqual(str(t), "01:02:03" + tag) | ||||||
|  |             else: | ||||||
|  |                 self.assertRaises(ValueError, str, t) | ||||||
|  | 
 | ||||||
|  |     def test_tzinfo_classes(self): | ||||||
|  |         cls = self.theclass | ||||||
|  |         class C1(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return None | ||||||
|  |             def dst(self, dt): return None | ||||||
|  |             def tzname(self, dt): return None | ||||||
|  |         for t in (cls(1, 1, 1), | ||||||
|  |                   cls(1, 1, 1, tzinfo=None), | ||||||
|  |                   cls(1, 1, 1, tzinfo=C1())): | ||||||
|  |             self.failUnless(t.utcoffset() is None) | ||||||
|  |             self.failUnless(t.dst() is None) | ||||||
|  |             self.failUnless(t.tzname() is None) | ||||||
|  | 
 | ||||||
|  |         class C2(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return -1439 | ||||||
|  |             def dst(self, dt): return 1439 | ||||||
|  |             def tzname(self, dt): return "aname" | ||||||
|  |         class C3(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return timedelta(minutes=-1439) | ||||||
|  |             def dst(self, dt): return timedelta(minutes=1439) | ||||||
|  |             def tzname(self, dt): return "aname" | ||||||
|  |         for t in cls(1, 1, 1, tzinfo=C2()), cls(1, 1, 1, tzinfo=C3()): | ||||||
|  |             self.assertEqual(t.utcoffset(), timedelta(minutes=-1439)) | ||||||
|  |             self.assertEqual(t.dst(), timedelta(minutes=1439)) | ||||||
|  |             self.assertEqual(t.tzname(), "aname") | ||||||
|  | 
 | ||||||
|  |         # Wrong types. | ||||||
|  |         class C4(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return "aname" | ||||||
|  |             def dst(self, dt): return () | ||||||
|  |             def tzname(self, dt): return 0 | ||||||
|  |         t = cls(1, 1, 1, tzinfo=C4()) | ||||||
|  |         self.assertRaises(TypeError, t.utcoffset) | ||||||
|  |         self.assertRaises(TypeError, t.dst) | ||||||
|  |         self.assertRaises(TypeError, t.tzname) | ||||||
|  | 
 | ||||||
|  |         # Offset out of range. | ||||||
|  |         class C5(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return -1440 | ||||||
|  |             def dst(self, dt): return 1440 | ||||||
|  |         class C6(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return timedelta(hours=-24) | ||||||
|  |             def dst(self, dt): return timedelta(hours=24) | ||||||
|  |         for t in cls(1, 1, 1, tzinfo=C5()), cls(1, 1, 1, tzinfo=C6()): | ||||||
|  |             self.assertRaises(ValueError, t.utcoffset) | ||||||
|  |             self.assertRaises(ValueError, t.dst) | ||||||
|  | 
 | ||||||
|  |         # Not a whole number of minutes. | ||||||
|  |         class C7(tzinfo): | ||||||
|  |             def utcoffset(self, dt): return timedelta(seconds=61) | ||||||
|  |             def dst(self, dt): return timedelta(microseconds=-81) | ||||||
|  |         t = cls(1, 1, 1, tzinfo=C7()) | ||||||
|  |         self.assertRaises(ValueError, t.utcoffset) | ||||||
|  |         self.assertRaises(ValueError, t.dst) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestTimeTZ(TestTime, TZInfoBase): | ||||||
|     theclass = timetz |     theclass = timetz | ||||||
| 
 | 
 | ||||||
|     def test_empty(self): |     def test_empty(self): | ||||||
|  | @ -1471,22 +1572,6 @@ def test_empty(self): | ||||||
|         self.assertEqual(t.microsecond, 0) |         self.assertEqual(t.microsecond, 0) | ||||||
|         self.failUnless(t.tzinfo is None) |         self.failUnless(t.tzinfo is None) | ||||||
| 
 | 
 | ||||||
|     def test_bad_tzinfo_classes(self): |  | ||||||
|         tz = self.theclass |  | ||||||
|         self.assertRaises(TypeError, tz, tzinfo=12) |  | ||||||
| 
 |  | ||||||
|         class NiceTry(object): |  | ||||||
|             def __init__(self): pass |  | ||||||
|             def utcoffset(self, dt): pass |  | ||||||
|         self.assertRaises(TypeError, tz, tzinfo=NiceTry) |  | ||||||
| 
 |  | ||||||
|         class BetterTry(tzinfo): |  | ||||||
|             def __init__(self): pass |  | ||||||
|             def utcoffset(self, dt): pass |  | ||||||
|         b = BetterTry() |  | ||||||
|         t = tz(tzinfo=b) |  | ||||||
|         self.failUnless(t.tzinfo is b) |  | ||||||
| 
 |  | ||||||
|     def test_zones(self): |     def test_zones(self): | ||||||
|         est = FixedOffset(-300, "EST", 1) |         est = FixedOffset(-300, "EST", 1) | ||||||
|         utc = FixedOffset(0, "UTC", -2) |         utc = FixedOffset(0, "UTC", -2) | ||||||
|  | @ -1503,9 +1588,9 @@ def test_zones(self): | ||||||
|         self.failUnless(t4.tzinfo is None) |         self.failUnless(t4.tzinfo is None) | ||||||
|         self.assertEqual(t5.tzinfo, utc) |         self.assertEqual(t5.tzinfo, utc) | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(t1.utcoffset(), -300) |         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) | ||||||
|         self.assertEqual(t2.utcoffset(), 0) |         self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) | ||||||
|         self.assertEqual(t3.utcoffset(), 60) |         self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) | ||||||
|         self.failUnless(t4.utcoffset() is None) |         self.failUnless(t4.utcoffset() is None) | ||||||
|         self.assertRaises(TypeError, t1.utcoffset, "no args") |         self.assertRaises(TypeError, t1.utcoffset, "no args") | ||||||
| 
 | 
 | ||||||
|  | @ -1515,9 +1600,9 @@ def test_zones(self): | ||||||
|         self.failUnless(t4.tzname() is None) |         self.failUnless(t4.tzname() is None) | ||||||
|         self.assertRaises(TypeError, t1.tzname, "no args") |         self.assertRaises(TypeError, t1.tzname, "no args") | ||||||
| 
 | 
 | ||||||
|         self.assertEqual(t1.dst(), 1) |         self.assertEqual(t1.dst(), timedelta(minutes=1)) | ||||||
|         self.assertEqual(t2.dst(), -2) |         self.assertEqual(t2.dst(), timedelta(minutes=-2)) | ||||||
|         self.assertEqual(t3.dst(), 3) |         self.assertEqual(t3.dst(), timedelta(minutes=3)) | ||||||
|         self.failUnless(t4.dst() is None) |         self.failUnless(t4.dst() is None) | ||||||
|         self.assertRaises(TypeError, t1.dst, "no args") |         self.assertRaises(TypeError, t1.dst, "no args") | ||||||
| 
 | 
 | ||||||
|  | @ -1578,26 +1663,6 @@ def test_hash_edge_cases(self): | ||||||
|         t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, "")) |         t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, "")) | ||||||
|         self.assertEqual(hash(t1), hash(t2)) |         self.assertEqual(hash(t1), hash(t2)) | ||||||
| 
 | 
 | ||||||
|     def test_utc_offset_out_of_bounds(self): |  | ||||||
|         class Edgy(tzinfo): |  | ||||||
|             def __init__(self, offset): |  | ||||||
|                 self.offset = offset |  | ||||||
|             def utcoffset(self, dt): |  | ||||||
|                 return self.offset |  | ||||||
| 
 |  | ||||||
|         for offset, legit in ((-1440, False), |  | ||||||
|                               (-1439, True), |  | ||||||
|                               (1439, True), |  | ||||||
|                               (1440, False)): |  | ||||||
|             t = timetz(1, 2, 3, tzinfo=Edgy(offset)) |  | ||||||
|             if legit: |  | ||||||
|                 aofs = abs(offset) |  | ||||||
|                 h, m = divmod(aofs, 60) |  | ||||||
|                 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m) |  | ||||||
|                 self.assertEqual(str(t), "01:02:03" + tag) |  | ||||||
|             else: |  | ||||||
|                 self.assertRaises(ValueError, str, t) |  | ||||||
| 
 |  | ||||||
|     def test_pickling(self): |     def test_pickling(self): | ||||||
|         import pickle, cPickle |         import pickle, cPickle | ||||||
| 
 | 
 | ||||||
|  | @ -1623,7 +1688,7 @@ def test_pickling(self): | ||||||
|         derived.__setstate__(state) |         derived.__setstate__(state) | ||||||
|         self.assertEqual(orig, derived) |         self.assertEqual(orig, derived) | ||||||
|         self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) |         self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) | ||||||
|         self.assertEqual(derived.utcoffset(), -300) |         self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) | ||||||
|         self.assertEqual(derived.tzname(), 'cookie') |         self.assertEqual(derived.tzname(), 'cookie') | ||||||
| 
 | 
 | ||||||
|         for pickler in pickle, cPickle: |         for pickler in pickle, cPickle: | ||||||
|  | @ -1633,7 +1698,7 @@ def test_pickling(self): | ||||||
|                 self.assertEqual(orig, derived) |                 self.assertEqual(orig, derived) | ||||||
|                 self.failUnless(isinstance(derived.tzinfo, |                 self.failUnless(isinstance(derived.tzinfo, | ||||||
|                                 PicklableFixedOffset)) |                                 PicklableFixedOffset)) | ||||||
|                 self.assertEqual(derived.utcoffset(), -300) |                 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) | ||||||
|                 self.assertEqual(derived.tzname(), 'cookie') |                 self.assertEqual(derived.tzname(), 'cookie') | ||||||
| 
 | 
 | ||||||
|     def test_more_bool(self): |     def test_more_bool(self): | ||||||
|  | @ -1664,8 +1729,7 @@ def test_more_bool(self): | ||||||
|         t = cls(0, tzinfo=FixedOffset(-24*60, "")) |         t = cls(0, tzinfo=FixedOffset(-24*60, "")) | ||||||
|         self.assertRaises(ValueError, lambda: bool(t)) |         self.assertRaises(ValueError, lambda: bool(t)) | ||||||
| 
 | 
 | ||||||
| class TestDateTimeTZ(TestDateTime): | class TestDateTimeTZ(TestDateTime, TZInfoBase): | ||||||
| 
 |  | ||||||
|     theclass = datetimetz |     theclass = datetimetz | ||||||
| 
 | 
 | ||||||
|     def test_trivial(self): |     def test_trivial(self): | ||||||
|  | @ -1744,22 +1808,6 @@ def utcoffset(self, dt): return 1440 # out of bounds | ||||||
|         t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, "")) |         t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, "")) | ||||||
|         self.assertRaises(ValueError, lambda: t1 == t1) |         self.assertRaises(ValueError, lambda: t1 == t1) | ||||||
| 
 | 
 | ||||||
|     def test_bad_tzinfo_classes(self): |  | ||||||
|         tz = self.theclass |  | ||||||
|         self.assertRaises(TypeError, tz, 1, 2, 3, tzinfo=12) |  | ||||||
| 
 |  | ||||||
|         class NiceTry(object): |  | ||||||
|             def __init__(self): pass |  | ||||||
|             def utcoffset(self, dt): pass |  | ||||||
|         self.assertRaises(TypeError, tz, 1, 2, 3, tzinfo=NiceTry) |  | ||||||
| 
 |  | ||||||
|         class BetterTry(tzinfo): |  | ||||||
|             def __init__(self): pass |  | ||||||
|             def utcoffset(self, dt): pass |  | ||||||
|         b = BetterTry() |  | ||||||
|         t = tz(1, 2, 3, tzinfo=b) |  | ||||||
|         self.failUnless(t.tzinfo is b) |  | ||||||
| 
 |  | ||||||
|     def test_pickling(self): |     def test_pickling(self): | ||||||
|         import pickle, cPickle |         import pickle, cPickle | ||||||
| 
 | 
 | ||||||
|  | @ -1785,7 +1833,7 @@ def test_pickling(self): | ||||||
|         derived.__setstate__(state) |         derived.__setstate__(state) | ||||||
|         self.assertEqual(orig, derived) |         self.assertEqual(orig, derived) | ||||||
|         self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) |         self.failUnless(isinstance(derived.tzinfo, PicklableFixedOffset)) | ||||||
|         self.assertEqual(derived.utcoffset(), -300) |         self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) | ||||||
|         self.assertEqual(derived.tzname(), 'cookie') |         self.assertEqual(derived.tzname(), 'cookie') | ||||||
| 
 | 
 | ||||||
|         for pickler in pickle, cPickle: |         for pickler in pickle, cPickle: | ||||||
|  | @ -1795,7 +1843,7 @@ def test_pickling(self): | ||||||
|                 self.assertEqual(orig, derived) |                 self.assertEqual(orig, derived) | ||||||
|                 self.failUnless(isinstance(derived.tzinfo, |                 self.failUnless(isinstance(derived.tzinfo, | ||||||
|                                 PicklableFixedOffset)) |                                 PicklableFixedOffset)) | ||||||
|                 self.assertEqual(derived.utcoffset(), -300) |                 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) | ||||||
|                 self.assertEqual(derived.tzname(), 'cookie') |                 self.assertEqual(derived.tzname(), 'cookie') | ||||||
| 
 | 
 | ||||||
|     def test_extreme_hashes(self): |     def test_extreme_hashes(self): | ||||||
|  | @ -1822,9 +1870,9 @@ def test_zones(self): | ||||||
|         self.assertEqual(t1.tzinfo, est) |         self.assertEqual(t1.tzinfo, est) | ||||||
|         self.assertEqual(t2.tzinfo, utc) |         self.assertEqual(t2.tzinfo, utc) | ||||||
|         self.assertEqual(t3.tzinfo, met) |         self.assertEqual(t3.tzinfo, met) | ||||||
|         self.assertEqual(t1.utcoffset(), -300) |         self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) | ||||||
|         self.assertEqual(t2.utcoffset(), 0) |         self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) | ||||||
|         self.assertEqual(t3.utcoffset(), 60) |         self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) | ||||||
|         self.assertEqual(t1.tzname(), "EST") |         self.assertEqual(t1.tzname(), "EST") | ||||||
|         self.assertEqual(t2.tzname(), "UTC") |         self.assertEqual(t2.tzname(), "UTC") | ||||||
|         self.assertEqual(t3.tzname(), "MET") |         self.assertEqual(t3.tzname(), "MET") | ||||||
|  | @ -1914,8 +1962,7 @@ def test_tz_aware_arithmetic(self): | ||||||
|         #            (nowaware base - nowawareplus base) + |         #            (nowaware base - nowawareplus base) + | ||||||
|         #            (nowawareplus offset - nowaware offset) = |         #            (nowawareplus offset - nowaware offset) = | ||||||
|         #            -delta + nowawareplus offset - nowaware offset |         #            -delta + nowawareplus offset - nowaware offset | ||||||
|         expected = timedelta(minutes=nowawareplus.utcoffset() - |         expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta | ||||||
|                                      nowaware.utcoffset()) - delta |  | ||||||
|         self.assertEqual(got, expected) |         self.assertEqual(got, expected) | ||||||
| 
 | 
 | ||||||
|         # Try max possible difference. |         # Try max possible difference. | ||||||
|  | @ -1935,7 +1982,7 @@ def test_tzinfo_now(self): | ||||||
|         another = meth(off42) |         another = meth(off42) | ||||||
|         again = meth(tzinfo=off42) |         again = meth(tzinfo=off42) | ||||||
|         self.failUnless(another.tzinfo is again.tzinfo) |         self.failUnless(another.tzinfo is again.tzinfo) | ||||||
|         self.assertEqual(another.utcoffset(), 42) |         self.assertEqual(another.utcoffset(), timedelta(minutes=42)) | ||||||
|         # Bad argument with and w/o naming the keyword. |         # Bad argument with and w/o naming the keyword. | ||||||
|         self.assertRaises(TypeError, meth, 16) |         self.assertRaises(TypeError, meth, 16) | ||||||
|         self.assertRaises(TypeError, meth, tzinfo=16) |         self.assertRaises(TypeError, meth, tzinfo=16) | ||||||
|  | @ -1955,7 +2002,7 @@ def test_tzinfo_fromtimestamp(self): | ||||||
|         another = meth(ts, off42) |         another = meth(ts, off42) | ||||||
|         again = meth(ts, tzinfo=off42) |         again = meth(ts, tzinfo=off42) | ||||||
|         self.failUnless(another.tzinfo is again.tzinfo) |         self.failUnless(another.tzinfo is again.tzinfo) | ||||||
|         self.assertEqual(another.utcoffset(), 42) |         self.assertEqual(another.utcoffset(), timedelta(minutes=42)) | ||||||
|         # Bad argument with and w/o naming the keyword. |         # Bad argument with and w/o naming the keyword. | ||||||
|         self.assertRaises(TypeError, meth, ts, 16) |         self.assertRaises(TypeError, meth, ts, 16) | ||||||
|         self.assertRaises(TypeError, meth, ts, tzinfo=16) |         self.assertRaises(TypeError, meth, ts, tzinfo=16) | ||||||
|  |  | ||||||
|  | @ -549,6 +549,40 @@ normalize_datetime(int *year, int *month, int *day, | ||||||
|  * tzinfo helpers. |  * tzinfo helpers. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | /* Ensure that p is None or of a tzinfo subclass.  Return 0 if OK; if not
 | ||||||
|  |  * raise TypeError and return -1. | ||||||
|  |  */ | ||||||
|  | static int | ||||||
|  | check_tzinfo_subclass(PyObject *p) | ||||||
|  | { | ||||||
|  | 	if (p == Py_None || PyTZInfo_Check(p)) | ||||||
|  | 		return 0; | ||||||
|  | 	PyErr_Format(PyExc_TypeError, | ||||||
|  | 		     "tzinfo argument must be None or of a tzinfo subclass, " | ||||||
|  | 		     "not type '%s'", | ||||||
|  | 		     p->ob_type->tp_name); | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Return tzinfo.methname(self), without any checking of results.
 | ||||||
|  |  * If tzinfo is None, returns None. | ||||||
|  |  */ | ||||||
|  | static PyObject * | ||||||
|  | call_tzinfo_method(PyObject *self, PyObject *tzinfo, char *methname) | ||||||
|  | { | ||||||
|  | 	PyObject *result; | ||||||
|  | 
 | ||||||
|  | 	assert(self && tzinfo && methname); | ||||||
|  | 	assert(check_tzinfo_subclass(tzinfo) >= 0); | ||||||
|  | 	if (tzinfo == Py_None) { | ||||||
|  | 		result = Py_None; | ||||||
|  | 		Py_INCREF(result); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		result = PyObject_CallMethod(tzinfo, methname, "O", self); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* If self has a tzinfo member, return a BORROWED reference to it.  Else
 | /* If self has a tzinfo member, return a BORROWED reference to it.  Else
 | ||||||
|  * return NULL, which is NOT AN ERROR.  There are no error returns here, |  * return NULL, which is NOT AN ERROR.  There are no error returns here, | ||||||
|  * and the caller must not decref the result. |  * and the caller must not decref the result. | ||||||
|  | @ -566,28 +600,15 @@ get_tzinfo_member(PyObject *self) | ||||||
| 	return tzinfo; | 	return tzinfo; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Ensure that p is None or of a tzinfo subclass.  Return 0 if OK; if not
 |  | ||||||
|  * raise TypeError and return -1. |  | ||||||
|  */ |  | ||||||
| static int |  | ||||||
| check_tzinfo_subclass(PyObject *p) |  | ||||||
| { |  | ||||||
| 	if (p == Py_None || PyTZInfo_Check(p)) |  | ||||||
| 		return 0; |  | ||||||
| 	PyErr_Format(PyExc_TypeError, |  | ||||||
| 		     "tzinfo argument must be None or of a tzinfo subclass, " |  | ||||||
| 		     "not type '%s'", |  | ||||||
| 		     p->ob_type->tp_name); |  | ||||||
| 	return -1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Internal helper.
 | /* Internal helper.
 | ||||||
|  * Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the |  * Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the | ||||||
|  * result.  tzinfo must be an instance of the tzinfo class.  If the method |  * result.  tzinfo must be an instance of the tzinfo class.  If the method | ||||||
|  * returns None, this returns 0 and sets *none to 1.  If the method doesn't |  * returns None, this returns 0 and sets *none to 1.  If the method doesn't | ||||||
|  * return a Python int or long, TypeError is raised and this returns -1. |  * return a Python int or long or timedelta, TypeError is raised and this | ||||||
|  * If it does return an int or long, but is outside the valid range for |  * returns -1.  If it returns an int or long, but is outside the valid | ||||||
|  * a UTC minute offset, ValueError is raised and this returns -1. |  * range for a UTC minute offset, or it returns a timedelta and the value is | ||||||
|  |  * out of range or isn't a whole number of minutes, ValueError is raised and | ||||||
|  |  * this returns -1. | ||||||
|  * Else *none is set to 0 and the integer method result is returned. |  * Else *none is set to 0 and the integer method result is returned. | ||||||
|  */ |  */ | ||||||
| static int | static int | ||||||
|  | @ -602,7 +623,7 @@ call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg, | ||||||
| 	assert(tzinfoarg != NULL); | 	assert(tzinfoarg != NULL); | ||||||
| 
 | 
 | ||||||
| 	*none = 0; | 	*none = 0; | ||||||
| 	u = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); | 	u = call_tzinfo_method(tzinfoarg, tzinfo, name); | ||||||
| 	if (u == NULL) | 	if (u == NULL) | ||||||
| 		return -1; | 		return -1; | ||||||
| 
 | 
 | ||||||
|  | @ -614,12 +635,35 @@ call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg, | ||||||
| 
 | 
 | ||||||
| 	if (PyInt_Check(u)) | 	if (PyInt_Check(u)) | ||||||
| 		result = PyInt_AS_LONG(u); | 		result = PyInt_AS_LONG(u); | ||||||
|  | 
 | ||||||
| 	else if (PyLong_Check(u)) | 	else if (PyLong_Check(u)) | ||||||
| 		result = PyLong_AsLong(u); | 		result = PyLong_AsLong(u); | ||||||
|  | 
 | ||||||
|  | 	else if (PyDelta_Check(u)) { | ||||||
|  | 		const int days = GET_TD_DAYS(u); | ||||||
|  | 		if (days < -1 || days > 0) | ||||||
|  | 			result = 24*60;	/* trigger ValueError below */ | ||||||
|  | 		else { | ||||||
|  | 			/* next line can't overflow because we know days
 | ||||||
|  | 			 * is -1 or 0 now | ||||||
|  | 			 */ | ||||||
|  | 			int ss = days * 24 * 3600 + GET_TD_SECONDS(u); | ||||||
|  | 			result = divmod(ss, 60, &ss); | ||||||
|  | 			if (ss || GET_TD_MICROSECONDS(u)) { | ||||||
|  | 				PyErr_Format(PyExc_ValueError, | ||||||
|  | 					     "tzinfo.%s() must return a " | ||||||
|  | 					     "whole number of minutes", | ||||||
|  | 					     name); | ||||||
|  | 				result = -1; | ||||||
|  | 				goto Done; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	else { | 	else { | ||||||
| 		PyErr_Format(PyExc_TypeError, | 		PyErr_Format(PyExc_TypeError, | ||||||
| 			     "tzinfo.%s() must return None or int or long", | 			     "tzinfo.%s() must return None, integer or " | ||||||
| 			     name); | 			     "timedelta, not '%s'", | ||||||
|  | 			     name, u->ob_type->tp_name); | ||||||
| 		goto Done; | 		goto Done; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -649,6 +693,32 @@ call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg, int *none) | ||||||
| 	return call_utc_tzinfo_method(tzinfo, "utcoffset", tzinfoarg, none); | 	return call_utc_tzinfo_method(tzinfo, "utcoffset", tzinfoarg, none); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static PyObject *new_delta(int d, int sec, int usec, int normalize); | ||||||
|  | 
 | ||||||
|  | /* Call tzinfo.name(self) and return the offset as a timedelta or None. */ | ||||||
|  | static PyObject * | ||||||
|  | offset_as_timedelta(PyObject *self, PyObject *tzinfo, char *name) { | ||||||
|  | 	PyObject *result; | ||||||
|  | 
 | ||||||
|  | 	if (tzinfo == Py_None) { | ||||||
|  | 		result = Py_None; | ||||||
|  | 		Py_INCREF(result); | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
|  | 		int none; | ||||||
|  | 		int offset = call_utc_tzinfo_method(tzinfo, name, self, &none); | ||||||
|  | 		if (offset < 0 && PyErr_Occurred()) | ||||||
|  | 			return NULL; | ||||||
|  | 		if (none) { | ||||||
|  | 			result = Py_None; | ||||||
|  | 			Py_INCREF(result); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			result = new_delta(0, offset * 60, 0, 1); | ||||||
|  | 	} | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Call tzinfo.dst(tzinfoarg), and extract an integer from the
 | /* Call tzinfo.dst(tzinfoarg), and extract an integer from the
 | ||||||
|  * result.  tzinfo must be an instance of the tzinfo class.  If dst() |  * result.  tzinfo must be an instance of the tzinfo class.  If dst() | ||||||
|  * returns None, call_dst returns 0 and sets *none to 1.  If dst() |  * returns None, call_dst returns 0 and sets *none to 1.  If dst() | ||||||
|  | @ -663,22 +733,29 @@ call_dst(PyObject *tzinfo, PyObject *tzinfoarg, int *none) | ||||||
| 	return call_utc_tzinfo_method(tzinfo, "dst", tzinfoarg, none); | 	return call_utc_tzinfo_method(tzinfo, "dst", tzinfoarg, none); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Call tzinfo.tzname(tzinfoarg), and return the result.  tzinfo must be
 | /* Call tzinfo.tzname(self), and return the result.  tzinfo must be
 | ||||||
|  * an instance of the tzinfo class.  If tzname() doesn't return None or |  * an instance of the tzinfo class or None.  If tzinfo isn't None, and | ||||||
|  * a string, TypeError is raised and this returns NULL. |  * tzname() doesn't return None ora string, TypeError is raised and this | ||||||
|  |  * returns NULL. | ||||||
|  */ |  */ | ||||||
| static PyObject * | static PyObject * | ||||||
| call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) | call_tzname(PyObject *self, PyObject *tzinfo) | ||||||
| { | { | ||||||
| 	PyObject *result; | 	PyObject *result; | ||||||
| 
 | 
 | ||||||
|  | 	assert(self != NULL); | ||||||
| 	assert(tzinfo != NULL); | 	assert(tzinfo != NULL); | ||||||
| 	assert(PyTZInfo_Check(tzinfo)); | 	assert(check_tzinfo_subclass(tzinfo) >= 0); | ||||||
| 	assert(tzinfoarg != NULL); | 
 | ||||||
|  | 	if (tzinfo == Py_None) { | ||||||
|  | 		result = Py_None; | ||||||
|  | 		Py_INCREF(result); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		result = PyObject_CallMethod(tzinfo, "tzname", "O", self); | ||||||
| 
 | 
 | ||||||
| 	result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg); |  | ||||||
| 	if (result != NULL && result != Py_None && ! PyString_Check(result)) { | 	if (result != NULL && result != Py_None && ! PyString_Check(result)) { | ||||||
| 		PyErr_Format(PyExc_TypeError, ".tzinfo.tzname() must " | 		PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must " | ||||||
| 			     "return None or a string, not '%s'", | 			     "return None or a string, not '%s'", | ||||||
| 			     result->ob_type->tp_name); | 			     result->ob_type->tp_name); | ||||||
| 		Py_DECREF(result); | 		Py_DECREF(result); | ||||||
|  | @ -699,7 +776,7 @@ typedef enum { | ||||||
| 	      /* date,
 | 	      /* date,
 | ||||||
| 	       * datetime, | 	       * datetime, | ||||||
| 	       * datetimetz with None tzinfo, | 	       * datetimetz with None tzinfo, | ||||||
| 	       * datetimetz where utcoffset() return None | 	       * datetimetz where utcoffset() returns None | ||||||
| 	       * time, | 	       * time, | ||||||
| 	       * timetz with None tzinfo, | 	       * timetz with None tzinfo, | ||||||
| 	       * timetz where utcoffset() returns None | 	       * timetz where utcoffset() returns None | ||||||
|  | @ -919,8 +996,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple) | ||||||
| 				Zreplacement = PyString_FromString(""); | 				Zreplacement = PyString_FromString(""); | ||||||
| 				if (Zreplacement == NULL) goto Done; | 				if (Zreplacement == NULL) goto Done; | ||||||
| 				if (tzinfo != Py_None && tzinfo != NULL) { | 				if (tzinfo != Py_None && tzinfo != NULL) { | ||||||
| 					PyObject *temp = call_tzname(tzinfo, | 					PyObject *temp = call_tzname(object, | ||||||
| 								     object); | 								     tzinfo); | ||||||
| 					if (temp == NULL) goto Done; | 					if (temp == NULL) goto Done; | ||||||
| 					if (temp != Py_None) { | 					if (temp != Py_None) { | ||||||
| 						assert(PyString_Check(temp)); | 						assert(PyString_Check(temp)); | ||||||
|  | @ -3917,38 +3994,24 @@ timetz_dealloc(PyDateTime_TimeTZ *self) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Indirect access to tzinfo methods.  One more "convenience function" and |  * Indirect access to tzinfo methods. | ||||||
|  * it won't be possible to find the useful methods anymore <0.5 wink>. |  | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static PyObject * |  | ||||||
| timetz_convienience(PyDateTime_TimeTZ *self, char *name) |  | ||||||
| { |  | ||||||
| 	PyObject *result; |  | ||||||
| 
 |  | ||||||
| 	if (self->tzinfo == Py_None) { |  | ||||||
| 		result = Py_None; |  | ||||||
| 		Py_INCREF(result); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 		result = PyObject_CallMethod(self->tzinfo, name, "O", self); |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* These are all METH_NOARGS, so don't need to check the arglist. */ | /* These are all METH_NOARGS, so don't need to check the arglist. */ | ||||||
| static PyObject * | static PyObject * | ||||||
| timetz_utcoffset(PyDateTime_TimeTZ *self, PyObject *unused) { | timetz_utcoffset(PyDateTime_TimeTZ *self, PyObject *unused) { | ||||||
| 	return timetz_convienience(self, "utcoffset"); | 	return offset_as_timedelta((PyObject *)self, self->tzinfo, | ||||||
| } | 				   "utcoffset"); | ||||||
| 
 |  | ||||||
| static PyObject * |  | ||||||
| timetz_tzname(PyDateTime_TimeTZ *self, PyObject *unused) { |  | ||||||
| 	return timetz_convienience(self, "tzname"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| timetz_dst(PyDateTime_TimeTZ *self, PyObject *unused) { | timetz_dst(PyDateTime_TimeTZ *self, PyObject *unused) { | ||||||
| 	return timetz_convienience(self, "dst"); | 	return offset_as_timedelta((PyObject *)self, self->tzinfo, "dst"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | timetz_tzname(PyDateTime_TimeTZ *self, PyObject *unused) { | ||||||
|  | 	return call_tzname((PyObject *)self, self->tzinfo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -4325,37 +4388,21 @@ datetimetz_dealloc(PyDateTime_DateTimeTZ *self) | ||||||
|  * Indirect access to tzinfo methods. |  * Indirect access to tzinfo methods. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /* Internal helper.
 |  | ||||||
|  * Call a tzinfo object's method, or return None if tzinfo is None. |  | ||||||
|  */ |  | ||||||
| static PyObject * |  | ||||||
| datetimetz_convienience(PyDateTime_DateTimeTZ *self, char *name) |  | ||||||
| { |  | ||||||
| 	PyObject *result; |  | ||||||
| 
 |  | ||||||
| 	if (self->tzinfo == Py_None) { |  | ||||||
| 		result = Py_None; |  | ||||||
| 		Py_INCREF(result); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 		result = PyObject_CallMethod(self->tzinfo, name, "O", self); |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* These are all METH_NOARGS, so don't need to check the arglist. */ | /* These are all METH_NOARGS, so don't need to check the arglist. */ | ||||||
| static PyObject * | static PyObject * | ||||||
| datetimetz_utcoffset(PyDateTime_DateTimeTZ *self, PyObject *unused) { | datetimetz_utcoffset(PyDateTime_DateTimeTZ *self, PyObject *unused) { | ||||||
| 	return datetimetz_convienience(self, "utcoffset"); | 	return offset_as_timedelta((PyObject *)self, self->tzinfo, | ||||||
| } | 				   "utcoffset"); | ||||||
| 
 |  | ||||||
| static PyObject * |  | ||||||
| datetimetz_tzname(PyDateTime_DateTimeTZ *self, PyObject *unused) { |  | ||||||
| 	return datetimetz_convienience(self, "tzname"); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| datetimetz_dst(PyDateTime_DateTimeTZ *self, PyObject *unused) { | datetimetz_dst(PyDateTime_DateTimeTZ *self, PyObject *unused) { | ||||||
| 	return datetimetz_convienience(self, "dst"); | 	return offset_as_timedelta((PyObject *)self, self->tzinfo, "dst"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | datetimetz_tzname(PyDateTime_DateTimeTZ *self, PyObject *unused) { | ||||||
|  | 	return call_tzname((PyObject *)self, self->tzinfo); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tim Peters
						Tim Peters