mirror of
https://github.com/python/cpython.git
synced 2025-09-25 17:59:57 +00:00
SF bug 660872: datetimetz constructors behave counterintuitively (2.3a1).
This gives much the same treatment to datetime.fromtimestamp(stamp, tz) as the last batch of checkins gave to datetime.now(tz): do "the obvious" thing with the tz argument instead of a senseless thing.
This commit is contained in:
parent
10cadce41e
commit
2a44a8d332
4 changed files with 58 additions and 23 deletions
|
@ -534,9 +534,9 @@ Other constructors, all class methods:
|
||||||
\cfunction{gettimeofday()} function).
|
\cfunction{gettimeofday()} function).
|
||||||
|
|
||||||
Else \var{tz} must be an instance of a class \class{tzinfo} subclass,
|
Else \var{tz} must be an instance of a class \class{tzinfo} subclass,
|
||||||
and the current date and time are translated to \var{tz}'s time
|
and the current date and time are converted to \var{tz}'s time
|
||||||
zone. In this case the result is equivalent to
|
zone. In this case the result is equivalent to
|
||||||
\code{\var{tz}.fromutc(datetime.utcnow().replace(tzinfo=\var{tz})}.
|
\code{\var{tz}.fromutc(datetime.utcnow().replace(tzinfo=\var{tz}))}.
|
||||||
See also \method{today()}, \method{utcnow()}.
|
See also \method{today()}, \method{utcnow()}.
|
||||||
\end{methoddesc}
|
\end{methoddesc}
|
||||||
|
|
||||||
|
@ -547,13 +547,22 @@ Other constructors, all class methods:
|
||||||
See also \method{now()}.
|
See also \method{now()}.
|
||||||
\end{methoddesc}
|
\end{methoddesc}
|
||||||
|
|
||||||
\begin{methoddesc}{fromtimestamp}{timestamp}
|
\begin{methoddesc}{fromtimestamp}{timestamp, tz=None}
|
||||||
Return the local \class{datetime} corresponding to the \POSIX{}
|
Return the local date and time corresponding to the \POSIX{}
|
||||||
timestamp, such as is returned by \function{time.time()}. This
|
timestamp, such as is returned by \function{time.time()}.
|
||||||
may raise \exception{ValueError}, if the timestamp is out of the
|
If optional argument \var{tz} is \code{None} or not specified, the
|
||||||
range of values supported by the platform C
|
timestamp is converted to the platform's local date and time, and
|
||||||
\cfunction{localtime()} function. It's common for this to be
|
the returned \class{datetime} object is naive.
|
||||||
restricted to years in 1970 through 2038.
|
|
||||||
|
Else \var{tz} must be an instance of a class \class{tzinfo} subclass,
|
||||||
|
and the timestamp is converted to \var{tz}'s time zone. In this case
|
||||||
|
the result is equivalent to
|
||||||
|
\code{\var{tz}.fromutc(datetime.utcfromtimestamp(\var{timestamp}).replace(tzinfo=\var{tz}))}.
|
||||||
|
|
||||||
|
\method{fromtimestamp()} may raise \exception{ValueError}, if the
|
||||||
|
timestamp is out of the range of values supported by the platform C
|
||||||
|
\cfunction{localtime()} or \cfunction(gmtime()} functions. It's common
|
||||||
|
for this to be restricted to years in 1970 through 2038.
|
||||||
Note that on non-POSIX systems that include leap seconds in their
|
Note that on non-POSIX systems that include leap seconds in their
|
||||||
notion of a timestamp, leap seconds are ignored by
|
notion of a timestamp, leap seconds are ignored by
|
||||||
\method{fromtimestamp()}, and then it's possible to have two timestamps
|
\method{fromtimestamp()}, and then it's possible to have two timestamps
|
||||||
|
|
|
@ -2266,7 +2266,7 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase):
|
||||||
# Try with and without naming the keyword.
|
# Try with and without naming the keyword.
|
||||||
off42 = FixedOffset(42, "42")
|
off42 = FixedOffset(42, "42")
|
||||||
another = meth(ts, off42)
|
another = meth(ts, off42)
|
||||||
again = meth(ts, tzinfo=off42)
|
again = meth(ts, tz=off42)
|
||||||
self.failUnless(another.tzinfo is again.tzinfo)
|
self.failUnless(another.tzinfo is again.tzinfo)
|
||||||
self.assertEqual(another.utcoffset(), timedelta(minutes=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.
|
||||||
|
@ -2279,6 +2279,20 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase):
|
||||||
# Too few args.
|
# Too few args.
|
||||||
self.assertRaises(TypeError, meth)
|
self.assertRaises(TypeError, meth)
|
||||||
|
|
||||||
|
# Try to make sure tz= actually does some conversion.
|
||||||
|
timestamp = 1000000000 # 2001-09-09 01:46:40 UTC, give or take
|
||||||
|
utc = FixedOffset(0, "utc", 0)
|
||||||
|
expected = datetime(2001, 9, 9, 1, 46, 40)
|
||||||
|
got = datetime.utcfromtimestamp(timestamp)
|
||||||
|
# We don't support leap seconds, but maybe the platfrom insists
|
||||||
|
# on using them, so don't demand exact equality).
|
||||||
|
self.failUnless(abs(got - expected) < timedelta(minutes=1))
|
||||||
|
|
||||||
|
est = FixedOffset(-5*60, "est", 0)
|
||||||
|
expected -= timedelta(hours=5)
|
||||||
|
got = datetime.fromtimestamp(timestamp, est).replace(tzinfo=None)
|
||||||
|
self.failUnless(abs(got - expected) < timedelta(minutes=1))
|
||||||
|
|
||||||
def test_tzinfo_utcnow(self):
|
def test_tzinfo_utcnow(self):
|
||||||
meth = self.theclass.utcnow
|
meth = self.theclass.utcnow
|
||||||
# Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
|
# Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
|
||||||
|
|
|
@ -93,6 +93,10 @@ Extension modules
|
||||||
a tz argument, now() continues to return the current local date and time,
|
a tz argument, now() continues to return the current local date and time,
|
||||||
as a naive datetime object.
|
as a naive datetime object.
|
||||||
|
|
||||||
|
datetime.fromtimestamp(): Like datetime.now() above, this had less than
|
||||||
|
useful behavior when the optional tinzo argument was specified. See
|
||||||
|
also SF bug report <http://www.python.org/sf/660872>.
|
||||||
|
|
||||||
The constructors building a datetime from a timestamp could raise
|
The constructors building a datetime from a timestamp could raise
|
||||||
ValueError if the platform C localtime()/gmtime() inserted "leap
|
ValueError if the platform C localtime()/gmtime() inserted "leap
|
||||||
seconds". Leap seconds are ignored now. On such platforms, it's
|
seconds". Leap seconds are ignored now. On such platforms, it's
|
||||||
|
|
|
@ -3682,8 +3682,7 @@ datetime_now(PyObject *cls, PyObject *args, PyObject *kw)
|
||||||
if (self != NULL && tzinfo != Py_None) {
|
if (self != NULL && tzinfo != Py_None) {
|
||||||
/* Convert UTC to tzinfo's zone. */
|
/* Convert UTC to tzinfo's zone. */
|
||||||
PyObject *temp = self;
|
PyObject *temp = self;
|
||||||
self = PyObject_CallMethod(tzinfo, "fromutc",
|
self = PyObject_CallMethod(tzinfo, "fromutc", "O", self);
|
||||||
"O", self);
|
|
||||||
Py_DECREF(temp);
|
Py_DECREF(temp);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -3702,17 +3701,26 @@ datetime_utcnow(PyObject *cls, PyObject *dummy)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
|
datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw)
|
||||||
{
|
{
|
||||||
PyObject *self = NULL;
|
PyObject *self;
|
||||||
double timestamp;
|
double timestamp;
|
||||||
PyObject *tzinfo = Py_None;
|
PyObject *tzinfo = Py_None;
|
||||||
static char *keywords[] = {"timestamp", "tzinfo", NULL};
|
static char *keywords[] = {"timestamp", "tz", NULL};
|
||||||
|
|
||||||
if (PyArg_ParseTupleAndKeywords(args, kw, "d|O:fromtimestamp",
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "d|O:fromtimestamp",
|
||||||
keywords, ×tamp, &tzinfo)) {
|
keywords, ×tamp, &tzinfo))
|
||||||
|
return NULL;
|
||||||
if (check_tzinfo_subclass(tzinfo) < 0)
|
if (check_tzinfo_subclass(tzinfo) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
self = datetime_from_timestamp(cls, localtime, timestamp,
|
|
||||||
|
self = datetime_from_timestamp(cls,
|
||||||
|
tzinfo == Py_None ? localtime : gmtime,
|
||||||
|
timestamp,
|
||||||
tzinfo);
|
tzinfo);
|
||||||
|
if (self != NULL && tzinfo != Py_None) {
|
||||||
|
/* Convert UTC to tzinfo's zone. */
|
||||||
|
PyObject *temp = self;
|
||||||
|
self = PyObject_CallMethod(tzinfo, "fromutc", "O", self);
|
||||||
|
Py_DECREF(temp);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -4404,7 +4412,7 @@ static PyMethodDef datetime_methods[] = {
|
||||||
|
|
||||||
{"now", (PyCFunction)datetime_now,
|
{"now", (PyCFunction)datetime_now,
|
||||||
METH_KEYWORDS | METH_CLASS,
|
METH_KEYWORDS | METH_CLASS,
|
||||||
PyDoc_STR("[tzinfo] -> new datetime with local day and time.")},
|
PyDoc_STR("[tz] -> new datetime with tz's locl day and time.")},
|
||||||
|
|
||||||
{"utcnow", (PyCFunction)datetime_utcnow,
|
{"utcnow", (PyCFunction)datetime_utcnow,
|
||||||
METH_NOARGS | METH_CLASS,
|
METH_NOARGS | METH_CLASS,
|
||||||
|
@ -4412,7 +4420,7 @@ static PyMethodDef datetime_methods[] = {
|
||||||
|
|
||||||
{"fromtimestamp", (PyCFunction)datetime_fromtimestamp,
|
{"fromtimestamp", (PyCFunction)datetime_fromtimestamp,
|
||||||
METH_KEYWORDS | METH_CLASS,
|
METH_KEYWORDS | METH_CLASS,
|
||||||
PyDoc_STR("timestamp[, tzinfo] -> local time from POSIX timestamp.")},
|
PyDoc_STR("timestamp[, tz] -> tz's local time from POSIX timestamp.")},
|
||||||
|
|
||||||
{"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp,
|
{"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp,
|
||||||
METH_VARARGS | METH_CLASS,
|
METH_VARARGS | METH_CLASS,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue