view Python/pytime.c @ 74832:f8409b3d6449

PEP 410
author Victor Stinner <victor.stinner@haypocalc.com>
date Wed, 08 Feb 2012 14:31:50 +0100
parents bee7943d38c6
children 90eda29a8e03
line wrap: on
line source

#include "Python.h"
#ifdef MS_WINDOWS
#include <windows.h>
#endif

#if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME)
  /*
   * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because the latter
   * might fail on some platforms. This fallback is unwanted on MacOSX because
   * that makes it impossible to use a binary build on OSX 10.4 on earlier
   * releases of the OS. Therefore claim we don't support ftime.
   */
# undef HAVE_FTIME
#endif

#if defined(HAVE_FTIME) && !defined(MS_WINDOWS)
#include <sys/timeb.h>
extern int ftime(struct timeb *);
#endif

#define MICROSECONDS    1000000

void
_PyTime_get(_PyTime_t *ts)
{
#ifdef MS_WINDOWS
    FILETIME system_time;
    ULARGE_INTEGER large;
    ULONGLONG value;

    GetSystemTimeAsFileTime(&system_time);
    large.u.LowPart = system_time.dwLowDateTime;
    large.u.HighPart = system_time.dwHighDateTime;
    /* 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). */
    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
      (3) time() -- resolution in seconds
      In all cases the return value in a timeval struct.
      Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
      fail, so we fall back on ftime() or time().
      Note: clock resolution does not imply clock accuracy! */

#ifdef HAVE_GETTIMEOFDAY
#ifdef GETTIMEOFDAY_NO_TZ
    err = gettimeofday(&tv);
#else /* !GETTIMEOFDAY_NO_TZ */
    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)
    ftime(&t);
    ts->seconds = t.time;
    ts->numerator = t.millitm;
    ts->denominator = 1000;
#else /* !HAVE_FTIME */
    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. */
}