[Python-Dev] PEP: New timestamp formats

Victor Stinner victor.stinner at haypocalc.com
Thu Feb 2 13:16:33 CET 2012


> I'd add datetime.timedelta to this list. It's exactly what timestamps
> are, after all - the difference between the current time and the
> relevant epoch value.

Ah yes, I forgot to mention it, whereas it is listed in the "final
timestamp formats list" :-)

>>  * a) (sec, nsec): C timespec structure, useful for os.futimens() for example
>>  * b) (sec, floatpart, exponent): value = sec + floatpart * 10**exponent
>>  * c) (sec, floatpart, divisor): value = sec + floatpart / divisor
>>
>> The format (a) and (b) may loose precision if the clock divisor is not a
>> power of 10.
>
> Format (b) only loses precision if the exponent chosen for a given
> value is too small relative to the precision of the underlying timer
> (it's the same as using decimal.Decimal in that respect).

Let's take an NTP timestamp in format (c): (sec=0,
floatpart=100000000, divisor=2**32):

>>> Decimal(100000000) * Decimal(10)**-10
Decimal('0.0100000000')
>>> Decimal(100000000) / Decimal(2)**32
Decimal('0.023283064365386962890625')

You have an error of 57%. Or do you mean that not only 2**32 should be
modified, but also 100000000? How do you adapt 100000000 (floatpart)
when changing the divisor (2**32 => 10**-10)? The format (c) avoids an
operation (base^exponent) and avoids loosing precision.

There is the same issue with QueryPerformanceFrequency and
QueryPerformanceCounter  used by time.clock(), the frequency is not a
power of any base.

I forgot to mention another advantage of (c), used by my patch for the
Decimal format: you can get the exact resolution of the clock
directly: 1/divisor. It works for any divisor (not only
base^exponent).

By the way, the format (c) can be simplified as a fraction:
(numerator, denominator) using (seconds * divisor + floatpart,
divisor). But this format is less practical to implement a function
creating a timestamp.

>> Callback and creating a new module to convert timestamps
> (...)
> Such an API could only become limiting if
> timestamps ever become something other than "the difference in time
> between right now and the relevant epoch value", and that's a
> sufficiently esoteric possibility that it really doesn't seem
> worthwhile to take it into account.

It may be interesting to support a different start date (other than
1970.1.1), if we choose to support broken-down timestamps (e.g.
datetime.datetime).

> The PEP should also mention PJE's suggestion of creating a new named
> protocol specifically for the purpose (with a signature based on one
> of the proposed tuple formats) (...)

Ok, I will add it.

> Rather than being timestamp specific, such a protocol would be a
> general numeric protocol. If (integer, numerator, denominator) is used
> (i.e. a "mixed number" in mathematical terms), then "__from_mixed__"
> would be an appropriate name. If (integer, fractional, exponent) is
> used (i.e. a fixed point notation), then "__from_fixed__" would work.
>
>    # Algorithm for a "from mixed numbers" protocol, assuming division
> doesn't lose precision...
>    def __from_mixed__(cls, integer, numerator, denominator):
>        return cls(integer) + cls(numerator) / cls(denominator)

Even if I like the idea, I don't think that we need all this machinery
to support nanosecond resolution. I should maybe forget my idea of
using datetime.datetime or datetime.timedelta, or only only support
int, float and decimal.Decimal.

datetime.datetime and datetime.timedelta are already compatible with
Decimal (except that they may loose precision because of an internal
conversion to float): datetime.datetime.fromtimestamp(t) and
datetime.timedelta(seconds=t).

If we only support int, float and Decimal, we don't need to add a new
protocol, hardcoded functions are enough :-)

>> os.stat: add new fields
>> -----------------------
>>
>> It was proposed to add 3 fields to os.stat() structure to get nanoseconds of
>> timestamps.
>
> It's worth noting that the challenge with this is that it's
> potentially time consuming to populating the extra fields, and that
> this approach doesn't help with the time APIs that return timestamps
> directly.

New fields can be optional (add a flag to get them), but I don't like
the idea of a structure with a variable number of fields, especially
because os.stat() structure can be used as a tuple (get a field by its
index).

Patching os.stat() doesn't solve the problem for the time module anyway.

>> Add an argument to change the result type
>> -----------------------------------------
>
> There should also be a description of the "set a boolean flag to
> request high precision output" approach.

You mean something like: time.time(hires=True)? Or time.time(decimal=True)?

Victor


More information about the Python-Dev mailing list