summaryrefslogtreecommitdiffstats
path: root/Python/pytime.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@haypocalc.com>2012-02-08 13:31:50 (GMT)
committerVictor Stinner <victor.stinner@haypocalc.com>2012-02-08 13:31:50 (GMT)
commitccd5715a149388eec2f40e5efacac83d3fe357ca (patch)
tree69b6582c3ed63f6527e56ebdc996c99dccbba910 /Python/pytime.c
parent6f91ce74a04e958b2e5b1d1904110739eea66841 (diff)
downloadcpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.zip
cpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.tar.gz
cpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.tar.bz2
PEP 410
Diffstat (limited to 'Python/pytime.c')
-rw-r--r--Python/pytime.c343
1 files changed, 325 insertions, 18 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index bec1c71..8679bcc 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -18,24 +18,36 @@
extern int ftime(struct timeb *);
#endif
+#define MICROSECONDS 1000000
+
void
-_PyTime_gettimeofday(_PyTime_timeval *tp)
+_PyTime_get(_PyTime_t *ts)
{
#ifdef MS_WINDOWS
FILETIME system_time;
ULARGE_INTEGER large;
- ULONGLONG microseconds;
+ ULONGLONG value;
GetSystemTimeAsFileTime(&system_time);
large.u.LowPart = system_time.dwLowDateTime;
large.u.HighPart = system_time.dwHighDateTime;
- /* 11,644,473,600,000,000: number of microseconds between
+ /* 116,444,736,000,000,000: number of 100 ns between
the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
days). */
- microseconds = large.QuadPart / 10 - 11644473600000000;
- tp->tv_sec = microseconds / 1000000;
- tp->tv_usec = microseconds % 1000000;
+ value = large.QuadPart - 116444736000000000;
+ ts->seconds = 0;
+ ts->numerator = value;
+ ts->denominator = (_PyTime_fraction_t)10000000;
#else
+
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ int err;
+#endif
+#if defined(HAVE_FTIME)
+ struct timeb t;
+#endif
+
/* There are three ways to get the time:
(1) gettimeofday() -- resolution in microseconds
(2) ftime() -- resolution in milliseconds
@@ -47,30 +59,325 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_NO_TZ
- if (gettimeofday(tp) == 0)
- return;
+ err = gettimeofday(&tv);
#else /* !GETTIMEOFDAY_NO_TZ */
- if (gettimeofday(tp, (struct timezone *)NULL) == 0)
- return;
+ err = gettimeofday(&tv, (struct timezone *)NULL);
#endif /* !GETTIMEOFDAY_NO_TZ */
+ if (err == 0)
+ {
+ ts->seconds = tv.tv_sec;
+ ts->numerator = tv.tv_usec;
+ ts->denominator = MICROSECONDS;
+ return;
+ }
#endif /* !HAVE_GETTIMEOFDAY */
#if defined(HAVE_FTIME)
- {
- struct timeb t;
- ftime(&t);
- tp->tv_sec = t.time;
- tp->tv_usec = t.millitm * 1000;
- }
+ ftime(&t);
+ ts->seconds = t.time;
+ ts->numerator = t.millitm;
+ ts->denominator = 1000;
#else /* !HAVE_FTIME */
- tp->tv_sec = time(NULL);
- tp->tv_usec = 0;
+ ts->seconds = time(NULL);
+ ts->numerator = 0;
+ ts->denominator = 1;
#endif /* !HAVE_FTIME */
#endif /* MS_WINDOWS */
}
void
+_PyTime_gettimeofday(_PyTime_timeval *tv)
+{
+ _PyTime_t ts;
+ _PyTime_fraction_t k;
+ time_t sec;
+
+ _PyTime_get(&ts);
+ tv->tv_sec = ts.seconds;
+ if (ts.numerator) {
+ if (ts.numerator > ts.denominator) {
+ sec = Py_SAFE_DOWNCAST(ts.numerator / ts.denominator,
+ _PyTime_fraction_t, time_t);
+ /* ignore integer overflow because _PyTime_gettimeofday() has
+ no return value */
+ tv->tv_sec += sec;
+ ts.numerator = ts.numerator % ts.denominator;
+ }
+ if (MICROSECONDS >= ts.denominator) {
+ k = (_PyTime_fraction_t)MICROSECONDS / ts.denominator;
+ tv->tv_usec = (long)(ts.numerator * k);
+ }
+ else {
+ k = ts.denominator / (_PyTime_fraction_t)MICROSECONDS;
+ tv->tv_usec = (long)(ts.numerator / k);
+ }
+ }
+ else {
+ tv->tv_usec = 0;
+ }
+}
+
+static PyObject*
+_PyLong_FromTime_t(time_t value)
+{
+#if SIZEOF_TIME_T <= SIZEOF_LONG
+ return PyLong_FromLong(value);
+#else
+ assert(sizeof(time_t) <= sizeof(PY_LONG_LONG));
+ return PyLong_FromLongLong(value);
+#endif
+}
+
+#if defined(HAVE_LONG_LONG)
+# define _PyLong_FromTimeFraction_t PyLong_FromLongLong
+#else
+# define _PyLong_FromTimeFraction_t PyLong_FromSize_t
+#endif
+
+/* Convert a timestamp to a PyFloat object */
+static PyObject*
+_PyTime_AsFloat(_PyTime_t *ts)
+{
+ double d;
+ d = (double)ts->seconds;
+ d += (double)ts->numerator / (double)ts->denominator;
+ return PyFloat_FromDouble(d);
+}
+
+/* Convert a timestamp to a PyLong object */
+static PyObject*
+_PyTime_AsLong(_PyTime_t *ts)
+{
+ PyObject *a, *b, *c;
+
+ a = _PyLong_FromTime_t(ts->seconds);
+ if (a == NULL)
+ return NULL;
+ b = _PyLong_FromTimeFraction_t(ts->numerator / ts->denominator);
+ if (b == NULL)
+ {
+ Py_DECREF(a);
+ return NULL;
+ }
+ c = PyNumber_Add(a, b);
+ Py_DECREF(a);
+ Py_DECREF(b);
+ return c;
+}
+
+/* Convert a timestamp to a decimal.Decimal object */
+static PyObject*
+_PyTime_AsDecimal(_PyTime_t *ts)
+{
+ static PyObject* module = NULL;
+ static PyObject* decimal = NULL;
+ static PyObject* exponent_context = NULL;
+ static PyObject* context = NULL;
+ /* exponent cache, dictionary of:
+ int (denominator) => Decimal (1/denominator) */
+ static PyObject* exponent_cache = NULL;
+ PyObject *t = NULL;
+ PyObject *key, *exponent, *quantized;
+ _Py_IDENTIFIER(quantize);
+ _Py_IDENTIFIER(__truediv__);
+
+ 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)
+ {
+ /* 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 = decimal.Context(22, rounding=decimal.ROUND_HALF_EVEN)
+ exponent_context = decimal.Context(1, rounding=decimal.ROUND_HALF_EVEN)
+ */
+ PyObject *context_class, *rounding;
+ 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);
+ if (context == NULL)
+ {
+ Py_DECREF(context_class);
+ Py_DECREF(rounding);
+ return NULL;
+ }
+
+ exponent_context = PyObject_CallFunction(context_class, "iO", 1, rounding);
+ Py_DECREF(context_class);
+ Py_DECREF(rounding);
+ if (exponent_context == NULL)
+ {
+ Py_CLEAR(context);
+ return NULL;
+ }
+ }
+
+ /* t = decimal.Decimal(value) */
+ if (ts->seconds) {
+ PyObject *f = _PyLong_FromTime_t(ts->seconds);
+ t = PyObject_CallFunction(decimal, "O", f);
+ Py_CLEAR(f);
+ }
+ else {
+ t = PyObject_CallFunction(decimal, "iO", 0, context);
+ }
+ if (t == NULL)
+ return NULL;
+
+ if (ts->numerator)
+ {
+ /* t += decimal.Decimal(numerator, ctx) / decimal.Decimal(denominator, ctx) */
+ PyObject *a, *b, *c, *d, *x;
+
+ x = _PyLong_FromTimeFraction_t(ts->numerator);
+ if (x == NULL)
+ goto error;
+ a = PyObject_CallFunction(decimal, "OO", x, context);
+ Py_CLEAR(x);
+ if (a == NULL)
+ goto error;
+
+ x = _PyLong_FromTimeFraction_t(ts->denominator);
+ 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 = _PyObject_CallMethodId(a, &PyId___truediv__, "OO",
+ b, context);
+ 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 (exponent_cache == NULL) {
+ exponent_cache = PyDict_New();
+ if (exponent_cache == NULL)
+ goto error;
+ }
+
+ key = _PyLong_FromTimeFraction_t(ts->denominator);
+ if (key == NULL)
+ goto error;
+ exponent = PyDict_GetItem(exponent_cache, key);
+ if (exponent == NULL) {
+ /* exponent = decimal.Decimal(1) / decimal.Decimal(resolution) */
+ PyObject *one, *denominator;
+
+ one = PyObject_CallFunction(decimal, "i", 1);
+ if (one == NULL) {
+ Py_DECREF(key);
+ goto error;
+ }
+
+ denominator = PyObject_CallFunction(decimal, "O", key);
+ if (denominator == NULL) {
+ Py_DECREF(key);
+ Py_DECREF(one);
+ goto error;
+ }
+
+ exponent = _PyObject_CallMethodId(one, &PyId___truediv__, "OO",
+ denominator, exponent_context);
+ Py_DECREF(one);
+ Py_DECREF(denominator);
+ if (exponent == NULL) {
+ Py_DECREF(key);
+ goto error;
+ }
+
+ if (PyDict_SetItem(exponent_cache, key, exponent) < 0) {
+ Py_DECREF(key);
+ Py_DECREF(exponent);
+ goto error;
+ }
+ Py_DECREF(key);
+ }
+
+ /* t = t.quantize(exponent, None, context) */
+ quantized = _PyObject_CallMethodId(t, &PyId_quantize, "OOO",
+ exponent, Py_None, context);
+ if (quantized == NULL)
+ goto error;
+ Py_DECREF(t);
+ t = quantized;
+
+ return t;
+
+error:
+ Py_XDECREF(t);
+ return NULL;
+}
+
+PyObject*
+_PyTime_Convert(_PyTime_t *ts, PyObject *format)
+{
+ assert(ts->denominator != 0);
+
+ if (format == NULL || (PyTypeObject *)format == &PyFloat_Type)
+ return _PyTime_AsFloat(ts);
+ if ((PyTypeObject *)format == &PyLong_Type)
+ return _PyTime_AsLong(ts);
+
+ if (PyType_Check(format))
+ {
+ PyObject *module, *name;
+ _Py_IDENTIFIER(__name__);
+ _Py_IDENTIFIER(__module__);
+
+ module = _PyObject_GetAttrId(format, &PyId___module__);
+ name = _PyObject_GetAttrId(format, &PyId___name__);
+ if (module != NULL && PyUnicode_Check(module)
+ && name != NULL && PyUnicode_Check(name))
+ {
+ if (PyUnicode_CompareWithASCIIString(module, "decimal") == 0
+ && PyUnicode_CompareWithASCIIString(name, "Decimal") == 0)
+ return _PyTime_AsDecimal(ts);
+ }
+ else
+ PyErr_Clear();
+ }
+
+ PyErr_Format(PyExc_ValueError, "Unknown timestamp format: %R", format);
+ return NULL;
+}
+
+void
_PyTime_Init()
{
/* Do nothing. Needed to force linking. */