summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Belopolsky <alexander.belopolsky@gmail.com>2012-06-08 16:33:09 (GMT)
committerAlexander Belopolsky <alexander.belopolsky@gmail.com>2012-06-08 16:33:09 (GMT)
commita4415141da431c74c405efc0ea2791183285f8ee (patch)
treeb0aed8191d2b8f94e08417e7ab53b4fc22abbfdd
parented36b2e55be884afb7517905e02da313973998d1 (diff)
downloadcpython-a4415141da431c74c405efc0ea2791183285f8ee.zip
cpython-a4415141da431c74c405efc0ea2791183285f8ee.tar.gz
cpython-a4415141da431c74c405efc0ea2791183285f8ee.tar.bz2
Issue #2736: Added datetime.timestamp() method.
-rw-r--r--Doc/library/datetime.rst44
-rw-r--r--Lib/datetime.py11
-rw-r--r--Lib/test/datetimetester.py36
-rw-r--r--Misc/NEWS2
-rw-r--r--Modules/_datetimemodule.c49
5 files changed, 130 insertions, 12 deletions
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 669236a..72e35df 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -752,17 +752,6 @@ Other constructors, all class methods:
datetime(1970, 1, 1) + timedelta(seconds=timestamp)
- There is no method to obtain the timestamp from a :class:`datetime`
- instance, but POSIX timestamp corresponding to a :class:`datetime`
- instance ``dt`` can be easily calculated as follows. For a naive
- ``dt``::
-
- timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)
-
- And for an aware ``dt``::
-
- timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)
-
.. versionchanged:: 3.3
Raise :exc:`OverflowError` instead of :exc:`ValueError` if the timestamp
is out of the range of values supported by the platform C
@@ -1054,6 +1043,39 @@ Instance methods:
Return the proleptic Gregorian ordinal of the date. The same as
``self.date().toordinal()``.
+.. method:: datetime.timestamp()
+
+ Return POSIX timestamp corresponding to the :class:`datetime`
+ instance. The return value is a :class:`float` similar to that
+ returned by :func:`time.time`.
+
+ Naive :class:`datetime` instances are assumed to represent local
+ time and this method relies on the platform C :c:func:`mktime`
+ function to perform the conversion. Since :class:`datetime`
+ supports wider range of values than :c:func:`mktime` on many
+ platforms, this method may raise :exc:`OverflowError` for times far
+ in the past or far in the future.
+
+ For aware :class:`datetime` instances, the return value is computed
+ as::
+
+ (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)).total_seconds()
+
+ .. versionadded:: 3.3
+
+ .. note::
+
+ There is no method to obtain the POSIX timestamp directly from a
+ naive :class:`datetime` instance representing UTC time. If your
+ application uses this convention and your system timezone is not
+ set to UTC, you can obtain the POSIX timestamp by supplying
+ ``tzinfo=timezone.utc``::
+
+ timestamp = dt.replace(tzinfo=timezone.utc).timestamp()
+
+ or by calculating the timestamp directly::
+
+ timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)
.. method:: datetime.weekday()
diff --git a/Lib/datetime.py b/Lib/datetime.py
index 59f3c68..5d8d9b3 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -1434,6 +1434,15 @@ class datetime(date):
self.hour, self.minute, self.second,
dst)
+ def timestamp(self):
+ "Return POSIX timestamp as float"
+ if self._tzinfo is None:
+ return _time.mktime((self.year, self.month, self.day,
+ self.hour, self.minute, self.second,
+ -1, -1, -1)) + self.microsecond / 1e6
+ else:
+ return (self - _EPOCH).total_seconds()
+
def utctimetuple(self):
"Return UTC time tuple compatible with time.gmtime()."
offset = self.utcoffset()
@@ -1889,7 +1898,7 @@ class timezone(tzinfo):
timezone.utc = timezone._create(timedelta(0))
timezone.min = timezone._create(timezone._minoffset)
timezone.max = timezone._create(timezone._maxoffset)
-
+_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
"""
Some time zone algebra. For a datetime x, let
x.n = x stripped of its timezone -- its naive time.
diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py
index 853806b..9d84b9d 100644
--- a/Lib/test/datetimetester.py
+++ b/Lib/test/datetimetester.py
@@ -1735,6 +1735,42 @@ class TestDateTime(TestDate):
got = self.theclass.utcfromtimestamp(ts)
self.verify_field_equality(expected, got)
+ # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
+ # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
+ @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
+ def test_timestamp_naive(self):
+ t = self.theclass(1970, 1, 1)
+ self.assertEqual(t.timestamp(), 18000.0)
+ t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
+ self.assertEqual(t.timestamp(),
+ 18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
+ # Missing hour defaults to standard time
+ t = self.theclass(2012, 3, 11, 2, 30)
+ self.assertEqual(self.theclass.fromtimestamp(t.timestamp()),
+ t + timedelta(hours=1))
+ # Ambiguous hour defaults to DST
+ t = self.theclass(2012, 11, 4, 1, 30)
+ self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)
+
+ # Timestamp may raise an overflow error on some platforms
+ for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]:
+ try:
+ s = t.timestamp()
+ except OverflowError:
+ pass
+ else:
+ self.assertEqual(self.theclass.fromtimestamp(s), t)
+
+ def test_timestamp_aware(self):
+ t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
+ self.assertEqual(t.timestamp(), 0.0)
+ t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
+ self.assertEqual(t.timestamp(),
+ 3600 + 2*60 + 3 + 4*1e-6)
+ t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
+ tzinfo=timezone(timedelta(hours=-5), 'EST'))
+ self.assertEqual(t.timestamp(),
+ 18000 + 3600 + 2*60 + 3 + 4*1e-6)
def test_microsecond_rounding(self):
for fts in [self.theclass.fromtimestamp,
self.theclass.utcfromtimestamp]:
diff --git a/Misc/NEWS b/Misc/NEWS
index caa31b3..c4dde35 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -21,6 +21,8 @@ Core and Builtins
Library
-------
+- Issue #2736: Added datetime.timestamp() method.
+
- Issue #13854: Make multiprocessing properly handle non-integer
non-string argument to SystemExit.
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 963a1ac..3150124 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -766,6 +766,8 @@ typedef struct
/* The interned UTC timezone instance */
static PyObject *PyDateTime_TimeZone_UTC;
+/* The interned Epoch datetime instance */
+static PyObject *PyDateTime_Epoch;
/* Create new timezone instance checking offset range. This
function does not check the name argument. Caller must assure
@@ -4748,6 +4750,44 @@ datetime_timetuple(PyDateTime_DateTime *self)
}
static PyObject *
+datetime_timestamp(PyDateTime_DateTime *self)
+{
+ PyObject *result;
+
+ if (HASTZINFO(self) && self->tzinfo != Py_None) {
+ PyObject *delta;
+ delta = datetime_subtract((PyObject *)self, PyDateTime_Epoch);
+ if (delta == NULL)
+ return NULL;
+ result = delta_total_seconds(delta);
+ Py_DECREF(delta);
+ }
+ else {
+ struct tm time;
+ time_t timestamp;
+ memset((void *) &time, '\0', sizeof(struct tm));
+ time.tm_year = GET_YEAR(self) - 1900;
+ time.tm_mon = GET_MONTH(self) - 1;
+ time.tm_mday = GET_DAY(self);
+ time.tm_hour = DATE_GET_HOUR(self);
+ time.tm_min = DATE_GET_MINUTE(self);
+ time.tm_sec = DATE_GET_SECOND(self);
+ time.tm_wday = -1;
+ time.tm_isdst = -1;
+ timestamp = mktime(&time);
+ /* Return value of -1 does not necessarily mean an error, but tm_wday
+ * cannot remain set to -1 if mktime succeeded. */
+ if (timestamp == (time_t)(-1) && time.tm_wday == -1) {
+ PyErr_SetString(PyExc_OverflowError,
+ "timestamp out of range");
+ return NULL;
+ }
+ result = PyFloat_FromDouble(timestamp + DATE_GET_MICROSECOND(self) / 1e6);
+ }
+ return result;
+}
+
+static PyObject *
datetime_getdate(PyDateTime_DateTime *self)
{
return new_date(GET_YEAR(self),
@@ -4894,6 +4934,9 @@ static PyMethodDef datetime_methods[] = {
{"timetuple", (PyCFunction)datetime_timetuple, METH_NOARGS,
PyDoc_STR("Return time tuple, compatible with time.localtime().")},
+ {"timestamp", (PyCFunction)datetime_timestamp, METH_NOARGS,
+ PyDoc_STR("Return POSIX timestamp as float.")},
+
{"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS,
PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")},
@@ -5151,6 +5194,12 @@ PyInit__datetime(void)
return NULL;
Py_DECREF(x);
+ /* Epoch */
+ PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0,
+ PyDateTime_TimeZone_UTC);
+ if (PyDateTime_Epoch == NULL)
+ return NULL;
+
/* module initialization */
PyModule_AddIntConstant(m, "MINYEAR", MINYEAR);
PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR);