Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 80475bb

Browse files
committed
Implemented datetime.astimezone() and datetimetz.astimezone().
1 parent 6578dc9 commit 80475bb

3 files changed

Lines changed: 159 additions & 18 deletions

File tree

Doc/lib/libdatetime.tex

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,11 @@ \subsection{\class{datetime} \label{datetime-datetime}}
601601
Return a datetime with the same value, except for those fields given
602602
new values by whichever keyword arguments are specified.
603603

604+
- astimezone(tz)
605+
Return a \class{datetimetz} with the same date and time fields, and
606+
with \member{tzinfo} member \var{tz}. \var{tz} must be an instance
607+
of a \class{tzinfo} subclass.
608+
604609
- timetuple()
605610
Return a 9-element tuple of the form returned by
606611
\function{time.localtime()}.
@@ -1083,6 +1088,23 @@ \subsection{ \class{datetimetz} \label{datetime-datetimetz}}
10831088
\code{tzinfo=None} can be specified to create a naive datetimetz from
10841089
an aware datetimetz.
10851090

1091+
- astimezone(tz)
1092+
Return a \class{datetimetz} with new tzinfo member \var{tz}. \var{tz}
1093+
must be an instance of a \class{tzinfo} subclass. If self is naive, or
1094+
if \code(tz.utcoffset(self)} returns \code{None},
1095+
\code{self.astimezone(tz)} is equivalent to
1096+
\code{self.replace(tzinfo=tz)}: a new timezone object is attached
1097+
without any conversion of date or time fields. If self is aware and
1098+
\code{tz.utcoffset(self)} does not return \code{None}, the date and
1099+
time fields are adjusted so that the result is local time in timezone
1100+
tz, representing the same UTC time as self. \code{self.astimezone(tz)}
1101+
is then equivalent to
1102+
\begin{verbatim}
1103+
(self - (self.utcoffset() - tz.utcoffset(self)).replace(tzinfo=tz)
1104+
\end{verbatim}
1105+
where the result of \code{tz.uctcoffset(self)} is converted to a
1106+
\class{timedelta} if it's an integer.
1107+
10861108
- utcoffset()
10871109
If \member{tzinfo} is \code{None}, returns \code{None}, else
10881110
\code{tzinfo.utcoffset(self)} converted to a \class{timedelta}

Lib/test/test_datetime.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,21 @@ def test_replace(self):
12951295
base = cls(2000, 2, 29)
12961296
self.assertRaises(ValueError, base.replace, year=2001)
12971297

1298+
def test_astimezone(self):
1299+
# Pretty boring for a datetime! datetimetz is more interesting here.
1300+
dt = self.theclass.now()
1301+
f = FixedOffset(44, "")
1302+
for dtz in dt.astimezone(f), dt.astimezone(tz=f):
1303+
self.failUnless(isinstance(dtz, datetimetz))
1304+
self.assertEqual(dt.date(), dtz.date())
1305+
self.assertEqual(dt.time(), dtz.time())
1306+
self.failUnless(dtz.tzinfo is f)
1307+
self.assertEqual(dtz.utcoffset(), timedelta(minutes=44))
1308+
1309+
self.assertRaises(TypeError, dt.astimezone) # not enough args
1310+
self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1311+
self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
1312+
12981313

12991314
class TestTime(unittest.TestCase):
13001315

@@ -2308,6 +2323,44 @@ def test_replace(self):
23082323
base = cls(2000, 2, 29)
23092324
self.assertRaises(ValueError, base.replace, year=2001)
23102325

2326+
def test_more_astimezone(self):
2327+
# The inherited test_astimezone covered some trivial and error cases.
2328+
fnone = FixedOffset(None, "None")
2329+
f44m = FixedOffset(44, "44")
2330+
fm5h = FixedOffset(-timedelta(hours=5), "m300")
2331+
2332+
dt = self.theclass.now(tzinfo=f44m)
2333+
self.failUnless(dt.tzinfo is f44m)
2334+
# Replacing with degenerate tzinfo doesn't do any adjustment.
2335+
for x in dt.astimezone(fnone), dt.astimezone(tz=fnone):
2336+
self.failUnless(x.tzinfo is fnone)
2337+
self.assertEqual(x.date(), dt.date())
2338+
self.assertEqual(x.time(), dt.time())
2339+
# Ditt with None tz.
2340+
x = dt.astimezone(tz=None)
2341+
self.failUnless(x.tzinfo is None)
2342+
self.assertEqual(x.date(), dt.date())
2343+
self.assertEqual(x.time(), dt.time())
2344+
# Ditto replacing with same tzinfo.
2345+
x = dt.astimezone(dt.tzinfo)
2346+
self.failUnless(x.tzinfo is f44m)
2347+
self.assertEqual(x.date(), dt.date())
2348+
self.assertEqual(x.time(), dt.time())
2349+
2350+
# Replacing with different tzinfo does adjust.
2351+
got = dt.astimezone(fm5h)
2352+
self.failUnless(got.tzinfo is fm5h)
2353+
self.assertEqual(got.utcoffset(), timedelta(hours=-5))
2354+
expected = dt - dt.utcoffset() # in effect, convert to UTC
2355+
expected += fm5h.utcoffset(dt) # and from there to local time
2356+
expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
2357+
self.assertEqual(got.date(), expected.date())
2358+
self.assertEqual(got.time(), expected.time())
2359+
self.assertEqual(got.timetz(), expected.timetz())
2360+
self.failUnless(got.tzinfo is expected.tzinfo)
2361+
self.assertEqual(got, expected)
2362+
2363+
23112364
def test_suite():
23122365
allsuites = [unittest.makeSuite(klass, 'test')
23132366
for klass in (TestModule,

Modules/datetimemodule.c

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,18 @@ get_tzinfo_member(PyObject *self)
600600
return tzinfo;
601601
}
602602

603+
/* self is a datetimetz. Replace its tzinfo member. */
604+
void
605+
replace_tzinfo(PyObject *self, PyObject *newtzinfo)
606+
{
607+
assert(self != NULL);
608+
assert(PyDateTimeTZ_Check(self));
609+
assert(check_tzinfo_subclass(newtzinfo) >= 0);
610+
Py_INCREF(newtzinfo);
611+
Py_DECREF(((PyDateTime_DateTimeTZ *)self)->tzinfo);
612+
((PyDateTime_DateTimeTZ *)self)->tzinfo = newtzinfo;
613+
}
614+
603615
/* Internal helper.
604616
* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the
605617
* 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)
29152927
TIME_GET_MICROSECOND(time));
29162928
if (result && PyTimeTZ_Check(time) && PyDateTimeTZ_Check(result)) {
29172929
/* Copy the tzinfo field. */
2918-
PyObject *tzinfo = ((PyDateTime_TimeTZ *)time)->tzinfo;
2919-
Py_INCREF(tzinfo);
2920-
Py_DECREF(((PyDateTime_DateTimeTZ *)result)->tzinfo);
2921-
((PyDateTime_DateTimeTZ *)result)->tzinfo = tzinfo;
2930+
replace_tzinfo(result, ((PyDateTime_TimeTZ *)time)->tzinfo);
29222931
}
29232932
return result;
29242933
}
@@ -3246,6 +3255,24 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
32463255
return clone;
32473256
}
32483257

3258+
static PyObject *
3259+
datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
3260+
{
3261+
PyObject *tzinfo;
3262+
static char *keywords[] = {"tz", NULL};
3263+
3264+
if (! PyArg_ParseTupleAndKeywords(args, kw, "O:astimezone", keywords,
3265+
&tzinfo))
3266+
return NULL;
3267+
if (check_tzinfo_subclass(tzinfo) < 0)
3268+
return NULL;
3269+
return new_datetimetz(GET_YEAR(self), GET_MONTH(self), GET_DAY(self),
3270+
DATE_GET_HOUR(self), DATE_GET_MINUTE(self),
3271+
DATE_GET_SECOND(self),
3272+
DATE_GET_MICROSECOND(self),
3273+
tzinfo);
3274+
}
3275+
32493276
static PyObject *
32503277
datetime_timetuple(PyDateTime_DateTime *self)
32513278
{
@@ -3397,6 +3424,9 @@ static PyMethodDef datetime_methods[] = {
33973424
{"replace", (PyCFunction)datetime_replace, METH_KEYWORDS,
33983425
PyDoc_STR("Return datetime with new specified fields.")},
33993426

3427+
{"astimezone", (PyCFunction)datetime_astimezone, METH_KEYWORDS,
3428+
PyDoc_STR("tz -> datetimetz with same date & time, and tzinfo=tz\n")},
3429+
34003430
{"__setstate__", (PyCFunction)datetime_setstate, METH_O,
34013431
PyDoc_STR("__setstate__(state)")},
34023432

@@ -4398,20 +4428,6 @@ static PyGetSetDef datetimetz_getset[] = {
43984428
* optional tzinfo argument.
43994429
*/
44004430

4401-
/* Internal helper.
4402-
* self is a datetimetz. Replace its tzinfo member.
4403-
*/
4404-
void
4405-
replace_tzinfo(PyObject *self, PyObject *newtzinfo)
4406-
{
4407-
assert(self != NULL);
4408-
assert(newtzinfo != NULL);
4409-
assert(PyDateTimeTZ_Check(self));
4410-
Py_INCREF(newtzinfo);
4411-
Py_DECREF(((PyDateTime_DateTimeTZ *)self)->tzinfo);
4412-
((PyDateTime_DateTimeTZ *)self)->tzinfo = newtzinfo;
4413-
}
4414-
44154431
static char *datetimetz_kws[] = {
44164432
"year", "month", "day", "hour", "minute", "second",
44174433
"microsecond", "tzinfo", NULL
@@ -4696,6 +4712,53 @@ datetimetz_replace(PyDateTime_DateTimeTZ *self, PyObject *args, PyObject *kw)
46964712
return clone;
46974713
}
46984714

4715+
static PyObject *
4716+
datetimetz_astimezone(PyDateTime_DateTimeTZ *self, PyObject *args,
4717+
PyObject *kw)
4718+
{
4719+
int y = GET_YEAR(self);
4720+
int m = GET_MONTH(self);
4721+
int d = GET_DAY(self);
4722+
int hh = DATE_GET_HOUR(self);
4723+
int mm = DATE_GET_MINUTE(self);
4724+
int ss = DATE_GET_SECOND(self);
4725+
int us = DATE_GET_MICROSECOND(self);
4726+
4727+
PyObject *tzinfo;
4728+
static char *keywords[] = {"tz", NULL};
4729+
4730+
if (! PyArg_ParseTupleAndKeywords(args, kw, "O:astimezone", keywords,
4731+
&tzinfo))
4732+
return NULL;
4733+
if (check_tzinfo_subclass(tzinfo) < 0)
4734+
return NULL;
4735+
4736+
if (tzinfo != Py_None && self->tzinfo != Py_None) {
4737+
int none;
4738+
int selfoffset;
4739+
selfoffset = call_utcoffset(self->tzinfo,
4740+
(PyObject *)self,
4741+
&none);
4742+
if (selfoffset == -1 && PyErr_Occurred())
4743+
return NULL;
4744+
if (! none) {
4745+
int tzoffset;
4746+
tzoffset = call_utcoffset(tzinfo,
4747+
(PyObject *)self,
4748+
&none);
4749+
if (tzoffset == -1 && PyErr_Occurred())
4750+
return NULL;
4751+
if (! none) {
4752+
mm -= selfoffset - tzoffset;
4753+
if (normalize_datetime(&y, &m, &d,
4754+
&hh, &mm, &ss, &us) < 0)
4755+
return NULL;
4756+
}
4757+
}
4758+
}
4759+
return new_datetimetz(y, m, d, hh, mm, ss, us, tzinfo);
4760+
}
4761+
46994762
static PyObject *
47004763
datetimetz_timetuple(PyDateTime_DateTimeTZ *self)
47014764
{
@@ -4908,6 +4971,9 @@ static PyMethodDef datetimetz_methods[] = {
49084971
{"replace", (PyCFunction)datetimetz_replace, METH_KEYWORDS,
49094972
PyDoc_STR("Return datetimetz with new specified fields.")},
49104973

4974+
{"astimezone", (PyCFunction)datetimetz_astimezone, METH_KEYWORDS,
4975+
PyDoc_STR("tz -> convert to local time in new timezone tz\n")},
4976+
49114977
{"__setstate__", (PyCFunction)datetimetz_setstate, METH_O,
49124978
PyDoc_STR("__setstate__(state)")},
49134979

0 commit comments

Comments
 (0)