简体   繁体   中英

Python Timestamp + Datetime Conversion

I have a test record with a UNIX millisecond timestamp since epoch value of 1459952824358

I am writing Python 2.x program using the fastavro library. That takes the 1459952824358 integer UNIX millisecond timestamp and converts it to a local datetime value of 2016-04-06T09:27:04.358000 . When I try to convert that back to a integer UNIX millisecond timestamp value I end up one hour off. I can replicate this conversion problem outside of the fastavro library with plain Python datetime:

import datetime
import time

raw = 1459952824358

local_dt = datetime.datetime.fromtimestamp(raw / 1000.0)
local_dt.isoformat() # '2016-04-06T09:27:04.358000'

back = int((local_dt - datetime.datetime.fromtimestamp(0)).total_seconds() * 1.0e3)
back - raw # 3600000

I can convert to a UTC datetime and back successfully:

import datetime
import time

raw = 1459952824358

utc_dt = datetime.datetime.utcfromtimestamp(raw / 1000.0)
utc_dt.isoformat() # '2016-04-06T14:27:04.358000'

back = int((utc_dt - datetime.datetime.utcfromtimestamp(0)).total_seconds() * 1.0e3)
back - raw # 0

What is odd is that my system is set to CST or UTC-6, yet Python produces a local datetime that is five hours off of the UTC value rather than six.

While, my specific project requires Python 2 compatibility, I try the Python 3 timestamp function which correctly gives me back the original timestamp value without the hour drift, but the local datetime is still bizarrely 5 hours off of the UTC time, even when my system is set to CST or UTC-6.

import datetime
import time

raw = 1459952824358

local_dt = datetime.datetime.fromtimestamp(raw / 1000.0)
local_dt.isoformat() # '2016-04-06T09:27:04.358000'

back = int(local_dt.timestamp() * 1000)
back - raw # 0

I try the same example in Java 9 on my same laptop, just to see what it produces:

import java.time.*

long raw = 1459952824358L;

Instant i = Instant.ofEpochMilli(raw)
ZonedDateTime zdtUtc = ZonedDateTime.ofInstant(i, ZoneOffset.UTC)
// UPDATE: This line was my original attempt. It was wrong because it uses the current daylight savings setting, not the one of the given date.
ZonedDateTime zdtLocal = zdtUtc.withZoneSameInstant(ZonedDateTime.now().getOffset())
// This next line will choose the current geography, but choose the daylight savings setting based on the date, which is what I expected.
ZonedDateTime zdtLocal = zdtUtc.withZoneSameInstant(ZoneId.systemDefault())

long deltaFromUtc = zdtUtc.toInstant().toEpochMilli() - raw
long deltaFromLocal = zdtLocal.toInstant().toEpochMilli() - raw

It returns back the exact original timestamp value regardless of utc/local.

UPDATE: Basically, Python 2.x can't properly convert a local datetime value to UTC without extra libraries. Python 3.x can, and Python 2.x has extra libraries to do this, but out of the box, it can't.

tl;dr : Your measurement is off, not the datetimes: You're comparing two times from different timezones, so the result should be the difference between the timezones, and it is.


So this is why it's better to work in UTC:

In Python 2.7:

Using your raw:

>>> print datetime.datetime.utcfromtimestamp(raw/1000.0)
... print datetime.datetime.fromtimestamp(raw/1000.0)
... 
2016-04-06 14:27:04.358000
2016-04-06 07:27:04.358000

7 hrs different

Using a modified date outside Daylight Savings Time:

>>> print datetime.datetime.utcfromtimestamp(1456952824)
... print datetime.datetime.fromtimestamp(1456952824)
... 
2016-03-02 21:07:04
2016-03-02 13:07:04

8 hours different

This is happening because raw and 0 in the local time are in different timezones (Daylight Savings Time vs Standard):

>>> print datetime.datetime.utcfromtimestamp(raw/1000.0)
... print datetime.datetime.fromtimestamp(raw/1000.0)
... 
2016-04-06 14:27:04.358000
2016-04-06 07:27:04.358000

>>> print datetime.datetime.utcfromtimestamp(0)
... print datetime.datetime.fromtimestamp(0)
...
1970-01-01 00:00:00
1969-12-31 16:00:00

If you stick to utc, no issues.

but the local datetime is still bizarrely 5 hours off of the UTC time, even when my system is set to CST or UTC-6.

Because on that raw date, your local time would be CDT (-5):

user@TS-E31:~$ date --date @1459952824
Wed Apr  6 07:27:04 PDT 2016
user@TS-E31:~$ date --date @0
Wed Dec 31 16:00:00 PST 1969

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM