STINNER Victor <victor.stin...@haypocalc.com> added the comment:
Attached patch adds an optional format argument to time.time():
time.time("float") is the same than time.time(), but
time.time("decimal") returns a Decimal object. The Decimal object
stores the resolution of the clock and doesn't loose lower bits for
big numbers. I configured the Decimal context to be able to store
10,000 years in seconds with a resolution of 1 nanosecond and
ROUND_HALF_EVEN rounding method.
Example: time.time("decimal") returns Decimal('1327495951.346024').
It is really cool for have directly the resolution in the result,
because the resolution may change at each call: time.time() has 3
different implementations (with fallbacks), each has a different
resolution. time.clock() has also 2 implementations (one is used as a
fallback) with different resolution.
The internal time_to_format() takes integer arguments: the floating
part is written as (floatpart, divisor).
If you like the idea, I will also write a patch for time.clock(),
time.wallclock() and time.clock_gettime(), and also maybe for
time.clock_getres().
We may use a registry to allow to add user defined formats, but I
prefer to keep the patch simple (only allow "float" and "decimal"
right now).
----------
Added file: http://bugs.python.org/file24321/time_decimal.patch
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue11457>
_______________________________________
changeset: 74495:6fcdb1d46abb
tag: tip
user: Victor Stinner <vstin...@wyplay.com>
date: Tue Jan 24 14:27:20 2012 +0100
files: Doc/library/time.rst Modules/timemodule.c
description:
Add a format optional argument to time.time() to return the current time
as a decimal.Decimal object, instead of a float.
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -444,9 +444,10 @@ The module defines the following functio
:exc:`TypeError` is raised.
-.. function:: time()
+.. function:: time(format="float")
- Return the time as a floating point number expressed in seconds since the
epoch,
+ Return the time as a floating point number, or a :class:`decimal.Decimal` if
+ format is ``"decimal"``, expressed in seconds since the epoch,
in UTC. Note that even though the time is always returned as a floating
point
number, not all systems provide time with a better precision than 1 second.
While this function normally returns non-decreasing values, it can return a
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -44,16 +44,246 @@
static int floatsleep(double);
static double floattime(void);
+typedef unsigned long floatpart_t;
+
+static PyObject*
+PyLong_FromTimeT(time_t value)
+{
+#ifdef HAVE_LONG_LONG
+ return PyLong_FromUnsignedLongLong(value);
+#else
+ assert(sizeof(time_t) <= sizeof(unsigned long));
+ return PyLong_FromUnsignedLong(value);
+#endif
+}
+
+static PyObject*
+PyLong_FromFloatpartT(floatpart_t value)
+{
+#ifdef HAVE_LONG_LONG
+ return PyLong_FromUnsignedLongLong(value);
+#else
+ assert(sizeof(floatpart_t) <= sizeof(unsigned long));
+ return PyLong_FromUnsignedLong(value);
+#endif
+}
+
+/* Convert a timestamp to a decimal.Decimal object of the requested
+ resolution. */
+static PyObject*
+time_to_decimal(time_t value,
+ floatpart_t floatpart, floatpart_t divisor,
+ int resolution)
+{
+ static PyObject* module = NULL;
+ static PyObject* decimal = NULL;
+ static PyObject* context = NULL;
+ /* 10^resolution cache, dictionary of int=>Decimal */
+ static PyObject* exponents = NULL;
+ PyObject *t = NULL;
+
+ if (!module) {
+ module = PyImport_ImportModuleNoBlock("decimal");
+ if (module == NULL)
+ return NULL;
+ }
+
+ if (!decimal) {
+ decimal = PyObject_GetAttrString(module, "Decimal");
+ if (decimal == NULL)
+ return NULL;
+ }
+
+ if (context == NULL)
+ {
+ /* context = decimal.Context(22, rounding=decimal.ROUND_HALF_EVEN) */
+ PyObject *context_class, *rounding;
+ /* Use 12 decimal digits to store 10,000 years in seconds + 9
+ decimal digits for the floating part in nanoseconds + 1 decimal
+ digit to round correctly */
+ context_class = PyObject_GetAttrString(module, "Context");
+ if (context_class == NULL)
+ return NULL;
+ rounding = PyObject_GetAttrString(module, "ROUND_HALF_EVEN");
+ if (rounding == NULL)
+ {
+ Py_DECREF(context_class);
+ return NULL;
+ }
+ context = PyObject_CallFunction(context_class, "iO", 22, rounding);
+ Py_DECREF(context_class);
+ Py_DECREF(rounding);
+ if (context == NULL)
+ return NULL;
+ }
+
+ /* t = decimal.Decimal(value, context) */
+ if (value) {
+ PyObject *f = PyLong_FromTimeT(value);
+ t = PyObject_CallFunction(decimal, "OO", f, context);
+ Py_CLEAR(f);
+ }
+ else {
+ t = PyObject_CallFunction(decimal, "iO", 0, context);
+ }
+ if (t == NULL)
+ return NULL;
+
+ if (floatpart)
+ {
+ /* t += decimal.Decimal(floatpart, ctx) / decimal.Decimal(divisor,
ctx) */
+ PyObject *a, *b, *c, *d, *x;
+
+ x = PyLong_FromFloatpartT(floatpart);
+ if (x == NULL)
+ goto error;
+ a = PyObject_CallFunction(decimal, "OO", x, context);
+ Py_CLEAR(x);
+ if (a == NULL)
+ goto error;
+
+ x = PyLong_FromFloatpartT(divisor);
+ if (x == NULL)
+ {
+ Py_DECREF(a);
+ goto error;
+ }
+ b = PyObject_CallFunction(decimal, "OO", x, context);
+ Py_CLEAR(x);
+ if (b == NULL)
+ {
+ Py_DECREF(a);
+ goto error;
+ }
+
+ c = PyNumber_TrueDivide(a, b);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ if (c == NULL)
+ goto error;
+
+ d = PyNumber_Add(t, c);
+ Py_DECREF(c);
+ if (d == NULL)
+ goto error;
+ Py_DECREF(t);
+ t = d;
+ }
+
+ if (resolution != 0) {
+ PyObject *key, *exponent, *quantized;
+
+ if (exponents == NULL) {
+ exponents = PyDict_New();
+ if (exponents == NULL)
+ goto error;
+ }
+
+ key = PyLong_FromLong(resolution);
+ if (key == NULL)
+ goto error;
+ exponent = PyDict_GetItem(exponents, key);
+ if (exponent == NULL) {
+ /* exponent = decimal.Decimal(10) ** decimal.Decimal(resolution) */
+ PyObject *ten, *pow;
+
+ ten = PyObject_CallFunction(decimal, "i", 10);
+ if (ten == NULL) {
+ Py_DECREF(key);
+ goto error;
+ }
+
+ pow = PyObject_CallFunction(decimal, "O", key);
+ if (pow == NULL) {
+ Py_DECREF(key);
+ Py_DECREF(ten);
+ goto error;
+ }
+
+ exponent = PyNumber_Power(ten, pow, Py_None);
+ Py_DECREF(ten);
+ Py_DECREF(pow);
+ if (exponent == NULL) {
+ Py_DECREF(key);
+ goto error;
+ }
+
+ if (PyDict_SetItem(exponents, key, exponent) < 0) {
+ Py_DECREF(key);
+ Py_DECREF(exponent);
+ goto error;
+ }
+ Py_DECREF(key);
+ }
+
+ /* t = t.quantize(exponent, None, context) */
+ quantized = PyObject_CallMethod(t, "quantize", "OOO", exponent,
Py_None, context);
+ if (quantized == NULL)
+ goto error;
+ Py_DECREF(t);
+ t = quantized;
+ }
+ return t;
+
+error:
+ Py_XDECREF(t);
+ return NULL;
+}
+
+static PyObject*
+time_to_float(time_t value,
+ floatpart_t floatpart, floatpart_t divisor,
+ int resolution)
+{
+ double t;
+ t = (double)value;
+ t += (double)floatpart / (double)divisor;
+ return PyFloat_FromDouble(t);
+}
+
+/* Convert an integer timestamp to the requested format. Arguments:
+ - value: int or float
+ - floatpart: floating part, int or float, can be zero
+ - divisor: divisor of the floating part, int or float, cannot be zero
+ - format: "float", "decimal", "tuple"
+
+ The result type depends on the requested format.
+
+ Raise a ValueError if the format is unknown. */
+static PyObject*
+time_to_format(time_t value,
+ floatpart_t floatpart, floatpart_t divisor,
+ int resolution,
+ const char *format)
+{
+ assert(divisor != 0);
+ if (!format || strcmp(format, "float") == 0) {
+ return time_to_float(value, floatpart, divisor, resolution);
+ }
+ else if (strcmp(format, "decimal") == 0) {
+ return time_to_decimal(value, floatpart, divisor, resolution);
+ }
+ else {
+ PyErr_Format(PyExc_ValueError, "Unknown format: %s", format);
+ return NULL;
+ }
+}
+
static PyObject *
-time_time(PyObject *self, PyObject *unused)
+time_time(PyObject *self, PyObject *args)
{
- double secs;
- secs = floattime();
- if (secs == 0.0) {
+ const char *format = NULL;
+ _PyTime_timeval t;
+
+ if (!PyArg_ParseTuple(args, "|s:time", &format))
+ return NULL;
+
+ _PyTime_gettimeofday(&t);
+ if (t.tv_sec == 0 && t.tv_usec == 0) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
- return PyFloat_FromDouble(secs);
+ return time_to_format(t.tv_sec, t.tv_usec, 1000000, -6, format);
}
PyDoc_STRVAR(time_doc,
@@ -893,7 +1123,7 @@ PyInit_timezone(PyObject *m) {
static PyMethodDef time_methods[] = {
- {"time", time_time, METH_NOARGS, time_doc},
+ {"time", time_time, METH_VARARGS, time_doc},
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
{"clock", time_clock, METH_NOARGS, clock_doc},
#endif
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com