среда, 15 апреля 2009 г.

UTC datetime to UNIX timestamp

UNIX timestamp is somewhat reliable value which does not depend on timezone or daylight saving time.

There are number of posts in the "Internets" on how to convert timestamp to datetime. But all of them are either mistaken or consider converting timestamp to local timezoned datetime object.

The correct (and awfully akward) mean to convert timestamp to UTC datetime object:

from datetime import datetime
from time import mktime, timezone

def utcdatetime_to_ts(dt):
return mktime(dt.utctimetuple()) - timezone

Then you can always:

assert utcdatetime_to_ts(datetime.utcnow()) - time() <= 1

Check also a better and shorter version in the comments.

5 комментариев:

Tuure Laurinolli комментирует...

Your assertion will most likely fail, since the resolution of time tuples is 1 second, and time.time() in your assertion most likely has much higher resolution.

Also, you could just do calendar.timegm(dt.utctimetuple()), and have a function that works for non-UTC datetimes as well.

Marius Gedminas комментирует...

I don't think this code is correct.

>>> datetime.datetime.fromtimestamp(1000000000)
datetime.datetime(2001, 9, 9, 3, 46, 40)
>>> calendar.timegm(datetime.datetime(2001, 9, 9, 3, 46, 40).utctimetuple())
1000007200
>>> mktime(datetime.datetime(2001, 9, 9, 3, 46, 40).utctimetuple())-timezone
1000007200.0

My local timezone is Europe/Vilnius. time.timezone is -3600 in summer, and -7200 in winter (which implies that it's never correct to add or subtract the current offset because it may not match the one that was correct for your particular datetime object).

This appears to work right, but I haven't tested it with many values or with different timezones (or with datetime objects with a timezone set):

>>> mktime(datetime.datetime(2001, 9, 9, 3, 46, 40).timetuple())
1000000000.0

Timezones are hard.

Anton комментирует...

Marius, you've used fromtimestamp instead of utcfromtimestamp, that is why you've found the discrepancy.

>>> datetime.datetime.utcfromtimestamp(1000000000)
datetime.datetime(2001, 9, 9, 1, 46, 40)
>>> calendar.timegm(datetime.datetime(2001, 9, 9, 1, 46, 40).utctimetuple())
1000000000
>>>
>>> mktime(datetime.datetime(2001, 9, 9, 1, 46, 40).utctimetuple()) - timezone
1000000000.0

Tim комментирует...

Careful, it looks like you've imported the timezone (an int) so it'll freeze to whatever timezone you're in when the program starts. For long-running processes, this can be a problem.

Anton комментирует...

Wow, Tim, thanks! I didnt even aware of this problem!
So, solution by Tuure Laurinolli is the best:
calendar.timegm(dt.utctimetuple())