diff --git a/Lib/datetime.py b/Lib/datetime.py --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1434,6 +1434,15 @@ 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 @@ 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 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1735,6 +1735,21 @@ got = self.theclass.utcfromtimestamp(ts) self.verify_field_equality(expected, got) + @support.run_with_tz('EST+05EDT,M4.1.0,M10.5.0') + def test_timestamp_naive(self): + t = datetime(1970, 1, 1) + self.assertEqual(t.timestamp(), 18000.0) + t = datetime(1970, 1, 1, 1, 2, 3, 4) + self.assertEqual(t.timestamp(), + 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) + + def test_timestamp_aware(self): + t = datetime(1970, 1, 1, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), 0.0) + t = datetime(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc) + self.assertEqual(t.timestamp(), + 3600 + 2*60 + 3 + 4*1e-6) + def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -766,6 +766,8 @@ /* 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,39 @@ } 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; + double 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_yday = -1; + time.tm_isdst = -1; + timestamp = mktime(&time); + timestamp += DATE_GET_MICROSECOND(self) / 1e6; + result = PyFloat_FromDouble(timestamp); + } + return result; +} + +static PyObject * datetime_getdate(PyDateTime_DateTime *self) { return new_date(GET_YEAR(self), @@ -4894,6 +4929,9 @@ {"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.")}, + {"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS, PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")}, @@ -5151,6 +5189,12 @@ 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);