Implemented datetime.astimezone() and datetimetz.astimezone().
This commit is contained in:
parent
6578dc925f
commit
80475bb4d2
|
@ -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 @@ class TestDateTime(TestDate):
|
||||||
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 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase):
|
||||||
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…
Reference in New Issue