get UTC timestamp in python with datetime
Solution 1:
Naïve datetime
versus aware datetime
Default datetime
objects are said to be "naïve": they keep time information without the time zone information. Think about naïve datetime
as a relative number (ie: +4
) without a clear origin (in fact your origin will be common throughout your system boundary).
In contrast, think about aware datetime
as absolute numbers (ie: 8
) with a common origin for the whole world.
Without timezone information you cannot convert the "naive" datetime towards any non-naive time representation (where does +4
targets if we don't know from where to start ?). This is why you can't have a datetime.datetime.toutctimestamp()
method. (cf: http://bugs.python.org/issue1457227)
To check if your datetime
dt
is naïve, check dt.tzinfo
, if None
, then it's naïve:
datetime.now() ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1) ## returns naïve datetime pointing on user given time
I have naïve datetimes, what can I do ?
You must make an assumption depending on your particular context:
The question you must ask yourself is: was your datetime
on UTC ? or was it local time ?
-
If you were using UTC (you are out of trouble):
import calendar def dt2ts(dt): """Converts a datetime object to UTC timestamp naive datetime will be considered UTC. """ return calendar.timegm(dt.utctimetuple())
-
If you were NOT using UTC, welcome to hell.
You have to make your
datetime
non-naïve prior to using the former function, by giving them back their intended timezone.You'll need the name of the timezone and the information about if DST was in effect when producing the target naïve datetime (the last info about DST is required for cornercases):
import pytz ## pip install pytz mytz = pytz.timezone('Europe/Amsterdam') ## Set your timezone dt = mytz.normalize(mytz.localize(dt, is_dst=True)) ## Set is_dst accordingly
Consequences of not providing
is_dst
:Not using
is_dst
will generate incorrect time (and UTC timestamp) if target datetime was produced while a backward DST was put in place (for instance changing DST time by removing one hour).Providing incorrect
is_dst
will of course generate incorrect time (and UTC timestamp) only on DST overlap or holes. And, when providing also incorrect time, occuring in "holes" (time that never existed due to forward shifting DST),is_dst
will give an interpretation of how to consider this bogus time, and this is the only case where.normalize(..)
will actually do something here, as it'll then translate it as an actual valid time (changing the datetime AND the DST object if required). Note that.normalize()
is not required for having a correct UTC timestamp at the end, but is probably recommended if you dislike the idea of having bogus times in your variables, especially if you re-use this variable elsewhere.and AVOID USING THE FOLLOWING: (cf: Datetime Timezone conversion using pytz)
dt = dt.replace(tzinfo=timezone('Europe/Amsterdam')) ## BAD !!
Why? because
.replace()
replaces blindly thetzinfo
without taking into account the target time and will choose a bad DST object. Whereas.localize()
uses the target time and youris_dst
hint to select the right DST object.
OLD incorrect answer (thanks @J.F.Sebastien for bringing this up):
Hopefully, it is quite easy to guess the timezone (your local origin) when you create your naive datetime
object as it is related to the system configuration that you would hopefully NOT change between the naive datetime object creation and the moment when you want to get the UTC timestamp. This trick can be used to give an imperfect question.
By using time.mktime
we can create an utc_mktime
:
def utc_mktime(utc_tuple):
"""Returns number of seconds elapsed since epoch
Note that no timezone are taken into consideration.
utc tuple must be: (year, month, day, hour, minute, second)
"""
if len(utc_tuple) == 6:
utc_tuple += (0, 0, 0)
return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))
def datetime_to_timestamp(dt):
"""Converts a datetime object to UTC timestamp"""
return int(utc_mktime(dt.timetuple()))
You must make sure that your datetime
object is created on the same timezone than the one that has created your datetime
.
This last solution is incorrect because it makes the assumption that the UTC offset from now is the same than the UTC offset from EPOCH. Which is not the case for a lot of timezones (in specific moment of the year for the Daylight Saving Time (DST) offsets).
Solution 2:
Another possibility is:
d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()
This works as both "d" and "epoch" are naive datetimes, making the "-" operator valid, and returning an interval. total_seconds()
turns the interval into seconds. Note that total_seconds()
returns a float, even d.microsecond == 0
Solution 3:
Also note the calendar.timegm() function as described by this blog entry:
import calendar
calendar.timegm(utc_timetuple)
The output should agree with the solution of vaab.