I'm having some trouble with datetime in Python. I tried converting a datetime to a timestamp and then back again and no matter how I try the end result is not the same. I always end up with a datetime of datetime(2014, 1, 30, 23, 59, 40, 1998).
import datetime
a = datetime.datetime.timestamp(datetime.datetime(2014, 1, 30, 23, 59, 40, 1999))
b = datetime.datetime.fromtimestamp(a)
print(b)
It is a known Python 3.4 issue:
>>> from datetime import datetime
>>> local = datetime(2014, 1, 30, 23, 59, 40, 1999)
>>> datetime.fromtimestamp(local.timestamp())
datetime.datetime(2014, 1, 30, 23, 59, 40, 1998)
Note: the microsecond is gone. The .timestamp()
already returns result that is slightly less than 1999
microseconds:
>>> from decimal import Decimal
>>> local.timestamp()
1391126380.001999
>>> Decimal(local.timestamp())
Decimal('1391126380.0019989013671875')
The rounding is fixed in the next 3.4, 3.5, 3.6 releases:
>>> from datetime import datetime
>>> local = datetime(2014, 1, 30, 23, 59, 40, 1999)
>>> datetime.fromtimestamp(local.timestamp())
datetime.datetime(2014, 1, 30, 23, 59, 40, 1999)
To workaround the issue, you could use the explicit formula:
>>> from datetime import datetime, timedelta
>>> local = datetime(2014, 1, 30, 23, 59, 40, 1999)
>>> datetime.utcfromtimestamp(local.timestamp())
datetime.datetime(2014, 1, 30, 23, 59, 40, 1998) # UTC time
>>> datetime(1970, 1, 1) + timedelta(seconds=local.timestamp())
datetime.datetime(2014, 1, 30, 23, 59, 40, 1999) # UTC time
Note: the input in all examples is the local time but the result is UTC time in the last one.
That last number is microseconds ... are the internals that accurate? Let's find out.
counter={}
for i in range(0,1000000,43):
# fuzz up some random-ish dates
d = datetime.datetime( 1990+(i%20), 1+(i%12), 1+(i%28), i%24, i%60, i%60, i)
ts=datetime.datetime.timestamp( d)
b = b=datetime.datetime.fromtimestamp(ts)
msdif=d.microsecond-b.microsecond
if msdif in counter:
counter[msdif] += 1
else:
counter[msdif]=1
assert b.day==d.day and b.hour==d.hour and b.minute==d.minute and b.second=d.second
>>>
>>> counter
{1: 23256}
>>>
I do believe you have found an off-by-one-microsecond error in the datetime library, unless there is something perverse buried in the specifications.
(I was expecting a spread around zero, reflecting rounding errors of some sort)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With