Raise ValueError when value being stored in a time_t variable will result in
more than a second of precision.  Primarily affects ctime, localtime, and
gmtime.

Closes bug #919012 thanks to Tim Peters' code.

Tim suggests that the new funciton being introduced, _PyTime_DoubletoTimet(),
should be added to the internal C API and then used in datetime where
appropriate.  Not being done now for lack of time.
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index ef6ee3e..0783f7f 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -84,6 +84,35 @@
 /* For Y2K check */
 static PyObject *moddict;
 
+/* Cast double x to time_t, but raise ValueError if x is too large
+ * to fit in a time_t.  ValueError is set on return iff the return
+ * value is (time_t)-1 and PyErr_Occurred().
+ */
+static time_t
+_PyTime_DoubleToTimet(double x)
+{
+	time_t result;
+	double diff;
+
+	result = (time_t)x;
+	/* How much info did we lose?  time_t may be an integral or
+	 * floating type, and we don't know which.  If it's integral,
+	 * we don't know whether C truncates, rounds, returns the floor,
+	 * etc.  If we lost a second or more, the C rounding is
+	 * unreasonable, or the input just doesn't fit in a time_t;
+	 * call it an error regardless.  Note that the original cast to
+	 * time_t can cause a C error too, but nothing we can do to
+	 * worm around that.
+	 */
+	diff = x - (double)result;
+	if (diff <= -1.0 || diff >= 1.0) {
+		PyErr_SetString(PyExc_ValueError,
+		                "timestamp out of range for platform time_t");
+		result = (time_t)-1;
+	}
+	return result;
+}
+
 static PyObject *
 time_time(PyObject *self, PyObject *args)
 {
@@ -231,11 +260,15 @@
 }
 
 static PyObject *
-time_convert(time_t when, struct tm * (*function)(const time_t *))
+time_convert(double when, struct tm * (*function)(const time_t *))
 {
 	struct tm *p;
+	time_t whent = _PyTime_DoubleToTimet(when);
+
+	if (whent == (time_t)-1 && PyErr_Occurred())
+		return NULL;
 	errno = 0;
-	p = function(&when);
+	p = function(&whent);
 	if (p == NULL) {
 #ifdef EINVAL
 		if (errno == 0)
@@ -254,7 +287,7 @@
 		when = floattime();
 	if (!PyArg_ParseTuple(args, "|d:gmtime", &when))
 		return NULL;
-	return time_convert((time_t)when, gmtime);
+	return time_convert(when, gmtime);
 }
 
 PyDoc_STRVAR(gmtime_doc,
@@ -272,7 +305,7 @@
 		when = floattime();
 	if (!PyArg_ParseTuple(args, "|d:localtime", &when))
 		return NULL;
-	return time_convert((time_t)when, localtime);
+	return time_convert(when, localtime);
 }
 
 PyDoc_STRVAR(localtime_doc,
@@ -480,7 +513,9 @@
 	else {
 		if (!PyArg_ParseTuple(args, "|d:ctime", &dt))
 			return NULL;
-		tt = (time_t)dt;
+		tt = _PyTime_DoubleToTimet(dt);
+		if (tt == (time_t)-1 && PyErr_Occurred())
+			return NULL;
 	}
 	p = ctime(&tt);
 	if (p == NULL) {