314

I have a time in UTC from which I want the number of seconds since epoch.

I am using strftime to convert it to the number of seconds. Taking 1st April 2012 as an example.

>>>datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'

1st of April 2012 UTC from epoch is 1333238400 but this above returns 1333234800 which is different by 1 hour.

So it looks like that strftime is taking my system time into account and applies a timezone shift somewhere. I thought datetime was purely naive?

How can I get around that? If possible avoiding to import other libraries unless standard. (I have portability concerns).

5

8 Answers 8

589

In Python 3.3+ you can use timestamp():

>>> datetime.datetime(2012,4,1,0,0).timestamp()
1333234800.0

In Python 3.2 or earlier, you could do it explicitly:

 >>> (datetime.datetime(2012,4,1,0,0) - datetime.datetime(1970,1,1)).total_seconds()
 1333238400.0

Why you should not use datetime.strftime('%s')

Python doesn't actually support %s as an argument to strftime (if you check at http://docs.python.org/library/datetime.html#strftime-and-strptime-behavior it's not in the list), the only reason it's working is because Python is passing the information to your system's strftime, which uses your local timezone.

>>> datetime.datetime(2012,04,01,0,0).strftime('%s')
'1333234800'
Sign up to request clarification or add additional context in comments.

23 Comments

I have been going crazy trying to figure out why i see strftime("%s") a lot, yet it's not in the docs. Thank you for nothing this!
don't use .strftime("%s"): it is not supported, it is not portable, it may silently produce a wrong result for an aware datetime object, it fails if input is in UTC (as in the question) but local timezone is not UTC
@earthmeLon Your bracketing is wrong. Timedeltas (made by subtracting two datetimes) have total_seconds, but datetimes do not.
This is not working for me: AttributeError: 'datetime.timedelta' object has no attribute 'total_seconds'
@Michael That function is new in Python 2.7, you must be using an older version. For versions before 2.7 you can do td.seconds + td.days*24*3600. This discards the microseconds part.
|
112

I had serious issues with Timezones and such. The way Python handles all that happen to be pretty confusing (to me). Things seem to be working fine using the calendar module (see links 1, 2, 3 and 4).

>>> import datetime
>>> import calendar
>>> aprilFirst=datetime.datetime(2012, 04, 01, 0, 0)
>>> calendar.timegm(aprilFirst.timetuple())
1333238400

7 Comments

+1 because it is the only answer that works for the input in the question.
is this portable?
This should be marked as the rightful answer, since this answers the concern in question. vnice kudos
This 'works', but note the question stated: "I have a time in UTC" This method will always use the system's local timezone. There is no way to specify a timezone. If aprilFirst in this example were an 'aware' instance and used a timezone different from the system's timezone, the result would not be correct (the timezone gets lost in the timetuple() call). To get the right answer for an 'aware' datetime you can use awaredt.timestamp() on recent Python 3. For Python 2 it's harder; one way is to use the arrow library. arrow.get(awaredt).timestamp will get it right.
Good point, @AdamWilliamson, but the code in the example is not localizing the datetime object, so I assumed that the "I have a time in UTC" meant that the OP had an unaware datetime object which was assumed to be in UTC for which he wanted to get an epoch (if the datetime happened to be TZ-aware this might, indeed, change things). Also, keep in mind that this answer is almost 8 years old and a lot of things have happened since (arrow was released in 2013, for instance)
|
39
import time
from datetime import datetime
now = datetime.now()

time.mktime(now.timetuple())

2 Comments

it is an incorrect way to write time.time() (mktime() may fail during DST transitions while time.time() continues to work). And it doesn't answer the question unless the local timezone is UTC (the input in the question is in UTC). Even if the input would represent a local time then mktime() may also fail for past/future dates if it doesn't use the tz database and if the local timezone may have different utc offsets over the years e.g., Europe/Moscow in 2010-2015 -- use UTC time (as in the question) or timezone-aware datetime objects instead.
here're more issues with converting a local time (such as returned by .now()) to epoch timestamp (returned by mktime()). If you read it; you understand why UTC input (used in the question) is (much) more preferable than a naive datetime object representing local time
17
import time
from datetime import datetime
now = datetime.now()

# same as above except keeps microseconds
time.mktime(now.timetuple()) + now.microsecond * 1e-6

(Sorry, it wouldn't let me comment on existing answer)

5 Comments

That is because time.mktime does not take into consideration the microsecond part, right?
Correct. The time tuple struct (based on C strut) doesn't have a space for microseconds, so we need to grab the info from the datetime object and add it at the end.
On my machines, this works correctly even though my time zone is ET.
This will give you different timestamps on different systems based on the local time of the system.
7

if you just need a timestamp in unix /epoch time, this one line works:

created_timestamp = int((datetime.datetime.now() - datetime.datetime(1970,1,1)).total_seconds())
>>> created_timestamp
1522942073L

and depends only on datetime works in python2 and python3

Comments

5

For an explicit timezone-independent solution, use the pytz library.

import datetime
import pytz

pytz.utc.localize(datetime.datetime(2012,4,1,0,0), is_dst=False).timestamp()

Output (float): 1333238400.0

2 Comments

saw a similar answer at stackoverflow.com/a/21145908/4355695, but this one as a one-liner is great for my use case where the incoming data is in UTC and just .timestamp() was assuming it to be in local time.
This does work for dates less than 1970. Thank you. The accepted answer does not work for dates less than 1970. (Python 3.7.3 64-bit Anaconda3)
4

This works in Python 2 and 3:

>>> import time
>>> import calendar
>>> calendar.timegm(time.gmtime())
1504917998

Just following the official docs... https://docs.python.org/2/library/time.html#module-time

4 Comments

1) This assumes you want to convert now, not a random datetime object. 2) You don't need calendar. time.mktime(randomDateTime.timetuple()) + randomDateTime.microsecond * 1e-6
@CharlesPlager time.mktime is incorrect; it interprets the argument in the local timezone, whereas the OP wants the time interpreted in UTC (as calendar.timegm does).
This the most accurate answer to me, if you are looking to convert your time in GMT and you want to keep it that way on the conversion to epoch timestamp.
Just following your answer, calendar.timegm(datetime.strptime("2019-05-03T05:40:09.770494+00:00"[:16], '%Y-%m-%dT%H:%M').timetuple()) I have timestamp in utc and no matter what system you run on it gives me correct timestamp, trick is to use strptime.timetumple
1

In Python 3.7

Return a datetime corresponding to a date_string in one of the formats emitted by date.isoformat() and datetime.isoformat(). Specifically, this function supports strings in the format(s) YYYY-MM-DD[*HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]], where * can match any single character.

https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat

1 Comment

Be aware that this was introduced in Python 3.7.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.