diff options
author | Victor Stinner <victor.stinner@haypocalc.com> | 2012-02-08 13:31:50 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@haypocalc.com> | 2012-02-08 13:31:50 (GMT) |
commit | ccd5715a149388eec2f40e5efacac83d3fe357ca (patch) | |
tree | 69b6582c3ed63f6527e56ebdc996c99dccbba910 /Python/pytime.c | |
parent | 6f91ce74a04e958b2e5b1d1904110739eea66841 (diff) | |
download | cpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.zip cpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.tar.gz cpython-ccd5715a149388eec2f40e5efacac83d3fe357ca.tar.bz2 |
PEP 410
Diffstat (limited to 'Python/pytime.c')
-rw-r--r-- | Python/pytime.c | 343 |
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. */ |