mirror of
				https://github.com/python/cpython.git
				synced 2025-11-01 06:01:29 +00:00 
			
		
		
		
	Implemented datetime.astimezone() and datetimetz.astimezone().
This commit is contained in:
		
							parent
							
								
									6578dc925f
								
							
						
					
					
						commit
						80475bb4d2
					
				
					 3 changed files with 159 additions and 18 deletions
				
			
		|  | @ -601,6 +601,11 @@ Instance methods: | ||||||
|     Return a datetime with the same value, except for those fields given |     Return a datetime with the same value, except for those fields given | ||||||
|     new values by whichever keyword arguments are specified. |     new values by whichever keyword arguments are specified. | ||||||
| 
 | 
 | ||||||
|  |   - astimezone(tz) | ||||||
|  |     Return a \class{datetimetz} with the same date and time fields, and | ||||||
|  |     with \member{tzinfo} member \var{tz}.  \var{tz} must be an instance | ||||||
|  |     of a \class{tzinfo} subclass. | ||||||
|  | 
 | ||||||
|   - timetuple() |   - timetuple() | ||||||
|     Return a 9-element tuple of the form returned by |     Return a 9-element tuple of the form returned by | ||||||
|     \function{time.localtime()}. |     \function{time.localtime()}. | ||||||
|  | @ -1083,6 +1088,23 @@ Instance methods: | ||||||
|     \code{tzinfo=None} can be specified to create a naive datetimetz from |     \code{tzinfo=None} can be specified to create a naive datetimetz from | ||||||
|     an aware datetimetz. |     an aware datetimetz. | ||||||
| 
 | 
 | ||||||
|  |   - astimezone(tz) | ||||||
|  |     Return a \class{datetimetz} with new tzinfo member \var{tz}.  \var{tz} | ||||||
|  |     must be an instance of a \class{tzinfo} subclass.  If self is naive, or | ||||||
|  |     if \code(tz.utcoffset(self)} returns \code{None}, | ||||||
|  |     \code{self.astimezone(tz)} is equivalent to | ||||||
|  |     \code{self.replace(tzinfo=tz)}:  a new timezone object is attached | ||||||
|  |     without any conversion of date or time fields.  If self is aware and | ||||||
|  |     \code{tz.utcoffset(self)} does not return \code{None}, the date and | ||||||
|  |     time fields are adjusted so that the result is local time in timezone | ||||||
|  |     tz, representing the same UTC time as self.  \code{self.astimezone(tz)} | ||||||
|  |     is then equivalent to | ||||||
|  |     \begin{verbatim} | ||||||
|  |         (self - (self.utcoffset() - tz.utcoffset(self)).replace(tzinfo=tz) | ||||||
|  |     \end{verbatim} | ||||||
|  |     where the result of \code{tz.uctcoffset(self)} is converted to a | ||||||
|  |     \class{timedelta} if it's an integer. | ||||||
|  | 
 | ||||||
|   - utcoffset() |   - utcoffset() | ||||||
|     If \member{tzinfo} is \code{None}, returns \code{None}, else |     If \member{tzinfo} is \code{None}, returns \code{None}, else | ||||||
|     \code{tzinfo.utcoffset(self)} converted to a \class{timedelta} |     \code{tzinfo.utcoffset(self)} converted to a \class{timedelta} | ||||||
|  |  | ||||||
|  | @ -1295,6 +1295,21 @@ def test_replace(self): | ||||||
|         base = cls(2000, 2, 29) |         base = cls(2000, 2, 29) | ||||||
|         self.assertRaises(ValueError, base.replace, year=2001) |         self.assertRaises(ValueError, base.replace, year=2001) | ||||||
| 
 | 
 | ||||||
|  |     def test_astimezone(self): | ||||||
|  |         # Pretty boring for a datetime!  datetimetz is more interesting here. | ||||||
|  |         dt = self.theclass.now() | ||||||
|  |         f = FixedOffset(44, "") | ||||||
|  |         for dtz in dt.astimezone(f), dt.astimezone(tz=f): | ||||||
|  |             self.failUnless(isinstance(dtz, datetimetz)) | ||||||
|  |             self.assertEqual(dt.date(), dtz.date()) | ||||||
|  |             self.assertEqual(dt.time(), dtz.time()) | ||||||
|  |             self.failUnless(dtz.tzinfo is f) | ||||||
|  |             self.assertEqual(dtz.utcoffset(), timedelta(minutes=44)) | ||||||
|  | 
 | ||||||
|  |         self.assertRaises(TypeError, dt.astimezone) # not enough args | ||||||
|  |         self.assertRaises(TypeError, dt.astimezone, f, f) # too many args | ||||||
|  |         self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class TestTime(unittest.TestCase): | class TestTime(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|  | @ -2308,6 +2323,44 @@ def test_replace(self): | ||||||
|         base = cls(2000, 2, 29) |         base = cls(2000, 2, 29) | ||||||
|         self.assertRaises(ValueError, base.replace, year=2001) |         self.assertRaises(ValueError, base.replace, year=2001) | ||||||
| 
 | 
 | ||||||
|  |     def test_more_astimezone(self): | ||||||
|  |         # The inherited test_astimezone covered some trivial and error cases. | ||||||
|  |         fnone = FixedOffset(None, "None") | ||||||
|  |         f44m = FixedOffset(44, "44") | ||||||
|  |         fm5h = FixedOffset(-timedelta(hours=5), "m300") | ||||||
|  | 
 | ||||||
|  |         dt = self.theclass.now(tzinfo=f44m) | ||||||
|  |         self.failUnless(dt.tzinfo is f44m) | ||||||
|  |         # Replacing with degenerate tzinfo doesn't do any adjustment. | ||||||
|  |         for x in dt.astimezone(fnone), dt.astimezone(tz=fnone): | ||||||
|  |             self.failUnless(x.tzinfo is fnone) | ||||||
|  |             self.assertEqual(x.date(), dt.date()) | ||||||
|  |             self.assertEqual(x.time(), dt.time()) | ||||||
|  |         # Ditt with None tz. | ||||||
|  |         x = dt.astimezone(tz=None) | ||||||
|  |         self.failUnless(x.tzinfo is None) | ||||||
|  |         self.assertEqual(x.date(), dt.date()) | ||||||
|  |         self.assertEqual(x.time(), dt.time()) | ||||||
|  |         # Ditto replacing with same tzinfo. | ||||||
|  |         x = dt.astimezone(dt.tzinfo) | ||||||
|  |         self.failUnless(x.tzinfo is f44m) | ||||||
|  |         self.assertEqual(x.date(), dt.date()) | ||||||
|  |         self.assertEqual(x.time(), dt.time()) | ||||||
|  | 
 | ||||||
|  |         # Replacing with different tzinfo does adjust. | ||||||
|  |         got = dt.astimezone(fm5h) | ||||||
|  |         self.failUnless(got.tzinfo is fm5h) | ||||||
|  |         self.assertEqual(got.utcoffset(), timedelta(hours=-5)) | ||||||
|  |         expected = dt - dt.utcoffset()  # in effect, convert to UTC | ||||||
|  |         expected += fm5h.utcoffset(dt)  # and from there to local time | ||||||
|  |         expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo | ||||||
|  |         self.assertEqual(got.date(), expected.date()) | ||||||
|  |         self.assertEqual(got.time(), expected.time()) | ||||||
|  |         self.assertEqual(got.timetz(), expected.timetz()) | ||||||
|  |         self.failUnless(got.tzinfo is expected.tzinfo) | ||||||
|  |         self.assertEqual(got, expected) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def test_suite(): | def test_suite(): | ||||||
|     allsuites = [unittest.makeSuite(klass, 'test') |     allsuites = [unittest.makeSuite(klass, 'test') | ||||||
|                  for klass in (TestModule, |                  for klass in (TestModule, | ||||||
|  |  | ||||||
|  | @ -600,6 +600,18 @@ get_tzinfo_member(PyObject *self) | ||||||
| 	return tzinfo; | 	return tzinfo; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* self is a datetimetz.  Replace its tzinfo member. */ | ||||||
|  | void | ||||||
|  | replace_tzinfo(PyObject *self, PyObject *newtzinfo) | ||||||
|  | { | ||||||
|  | 	assert(self != NULL); | ||||||
|  | 	assert(PyDateTimeTZ_Check(self)); | ||||||
|  | 	assert(check_tzinfo_subclass(newtzinfo) >= 0); | ||||||
|  | 	Py_INCREF(newtzinfo); | ||||||
|  | 	Py_DECREF(((PyDateTime_DateTimeTZ *)self)->tzinfo); | ||||||
|  | 	((PyDateTime_DateTimeTZ *)self)->tzinfo = newtzinfo; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* 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 | ||||||
|  | @ -2915,10 +2927,7 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) | ||||||
| 				    		TIME_GET_MICROSECOND(time)); | 				    		TIME_GET_MICROSECOND(time)); | ||||||
| 	if (result && PyTimeTZ_Check(time) && PyDateTimeTZ_Check(result)) { | 	if (result && PyTimeTZ_Check(time) && PyDateTimeTZ_Check(result)) { | ||||||
| 		/* Copy the tzinfo field. */ | 		/* Copy the tzinfo field. */ | ||||||
| 		PyObject *tzinfo = ((PyDateTime_TimeTZ *)time)->tzinfo; | 		replace_tzinfo(result, ((PyDateTime_TimeTZ *)time)->tzinfo); | ||||||
| 		Py_INCREF(tzinfo); |  | ||||||
| 		Py_DECREF(((PyDateTime_DateTimeTZ *)result)->tzinfo); |  | ||||||
| 		((PyDateTime_DateTimeTZ *)result)->tzinfo = tzinfo; |  | ||||||
| 	} | 	} | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  | @ -3246,6 +3255,24 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) | ||||||
| 	return clone; | 	return clone; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) | ||||||
|  | { | ||||||
|  | 	PyObject *tzinfo; | ||||||
|  | 	static char *keywords[] = {"tz", NULL}; | ||||||
|  | 
 | ||||||
|  | 	if (! PyArg_ParseTupleAndKeywords(args, kw, "O:astimezone", keywords, | ||||||
|  | 					  &tzinfo)) | ||||||
|  | 		return NULL; | ||||||
|  | 	if (check_tzinfo_subclass(tzinfo) < 0) | ||||||
|  | 		return NULL; | ||||||
|  | 	return new_datetimetz(GET_YEAR(self), GET_MONTH(self), GET_DAY(self), | ||||||
|  | 			      DATE_GET_HOUR(self), DATE_GET_MINUTE(self), | ||||||
|  | 			      DATE_GET_SECOND(self), | ||||||
|  | 			      DATE_GET_MICROSECOND(self), | ||||||
|  | 			      tzinfo); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| datetime_timetuple(PyDateTime_DateTime *self) | datetime_timetuple(PyDateTime_DateTime *self) | ||||||
| { | { | ||||||
|  | @ -3397,6 +3424,9 @@ static PyMethodDef datetime_methods[] = { | ||||||
| 	{"replace",     (PyCFunction)datetime_replace,	METH_KEYWORDS, | 	{"replace",     (PyCFunction)datetime_replace,	METH_KEYWORDS, | ||||||
| 	 PyDoc_STR("Return datetime with new specified fields.")}, | 	 PyDoc_STR("Return datetime with new specified fields.")}, | ||||||
| 
 | 
 | ||||||
|  | 	{"astimezone",  (PyCFunction)datetime_astimezone, METH_KEYWORDS, | ||||||
|  | 	 PyDoc_STR("tz -> datetimetz with same date & time, and tzinfo=tz\n")}, | ||||||
|  | 
 | ||||||
| 	{"__setstate__", (PyCFunction)datetime_setstate, METH_O, | 	{"__setstate__", (PyCFunction)datetime_setstate, METH_O, | ||||||
| 	 PyDoc_STR("__setstate__(state)")}, | 	 PyDoc_STR("__setstate__(state)")}, | ||||||
| 
 | 
 | ||||||
|  | @ -4398,20 +4428,6 @@ static PyGetSetDef datetimetz_getset[] = { | ||||||
|  * optional tzinfo argument. |  * optional tzinfo argument. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /* Internal helper.
 |  | ||||||
|  * self is a datetimetz.  Replace its tzinfo member. |  | ||||||
|  */ |  | ||||||
| void |  | ||||||
| replace_tzinfo(PyObject *self, PyObject *newtzinfo) |  | ||||||
| { |  | ||||||
| 	assert(self != NULL); |  | ||||||
| 	assert(newtzinfo != NULL); |  | ||||||
| 	assert(PyDateTimeTZ_Check(self)); |  | ||||||
| 	Py_INCREF(newtzinfo); |  | ||||||
| 	Py_DECREF(((PyDateTime_DateTimeTZ *)self)->tzinfo); |  | ||||||
| 	((PyDateTime_DateTimeTZ *)self)->tzinfo = newtzinfo; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static char *datetimetz_kws[] = { | static char *datetimetz_kws[] = { | ||||||
| 	"year", "month", "day", "hour", "minute", "second", | 	"year", "month", "day", "hour", "minute", "second", | ||||||
| 	"microsecond", "tzinfo", NULL | 	"microsecond", "tzinfo", NULL | ||||||
|  | @ -4696,6 +4712,53 @@ datetimetz_replace(PyDateTime_DateTimeTZ *self, PyObject *args, PyObject *kw) | ||||||
| 	return clone; | 	return clone; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static PyObject * | ||||||
|  | datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args, | ||||||
|  | 		      PyObject *kw) | ||||||
|  | { | ||||||
|  | 	int y = GET_YEAR(self); | ||||||
|  | 	int m = GET_MONTH(self); | ||||||
|  | 	int d = GET_DAY(self); | ||||||
|  | 	int hh = DATE_GET_HOUR(self); | ||||||
|  | 	int mm = DATE_GET_MINUTE(self); | ||||||
|  | 	int ss = DATE_GET_SECOND(self); | ||||||
|  | 	int us = DATE_GET_MICROSECOND(self); | ||||||
|  | 
 | ||||||
|  | 	PyObject *tzinfo; | ||||||
|  | 	static char *keywords[] = {"tz", NULL}; | ||||||
|  | 
 | ||||||
|  | 	if (! PyArg_ParseTupleAndKeywords(args, kw, "O:astimezone", keywords, | ||||||
|  | 					  &tzinfo)) | ||||||
|  | 		return NULL; | ||||||
|  | 	if (check_tzinfo_subclass(tzinfo) < 0) | ||||||
|  | 		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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static PyObject * | static PyObject * | ||||||
| datetimetz_timetuple(PyDateTime_DateTimeTZ *self) | datetimetz_timetuple(PyDateTime_DateTimeTZ *self) | ||||||
| { | { | ||||||
|  | @ -4908,6 +4971,9 @@ static PyMethodDef datetimetz_methods[] = { | ||||||
| 	{"replace",     (PyCFunction)datetimetz_replace,	METH_KEYWORDS, | 	{"replace",     (PyCFunction)datetimetz_replace,	METH_KEYWORDS, | ||||||
| 	 PyDoc_STR("Return datetimetz with new specified fields.")}, | 	 PyDoc_STR("Return datetimetz with new specified fields.")}, | ||||||
| 
 | 
 | ||||||
|  | 	{"astimezone",  (PyCFunction)datetimetz_astimezone, METH_KEYWORDS, | ||||||
|  | 	 PyDoc_STR("tz -> convert to local time in new timezone tz\n")}, | ||||||
|  | 
 | ||||||
| 	{"__setstate__", (PyCFunction)datetimetz_setstate, METH_O, | 	{"__setstate__", (PyCFunction)datetimetz_setstate, METH_O, | ||||||
| 	 PyDoc_STR("__setstate__(state)")}, | 	 PyDoc_STR("__setstate__(state)")}, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tim Peters
						Tim Peters