298

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).

Noel
  • 3,749
  • 3
  • 22
  • 21
  • related: [Converting datetime.date to UTC timestamp in Python](http://stackoverflow.com/q/8777753/4279) – jfs Jul 02 '13 at 22:04
  • 16
    Am I the only one noting that you use octal literals in the numbers? – Fish Monitor Oct 05 '13 at 14:58
  • related [Python issue: datetime.strftime('%s') should respect tzinfo](http://bugs.python.org/issue12750) – jfs Aug 22 '14 at 04:56
  • 4
    Newer Python 3.3+ has `datetime.datetime.timestamp(datetime.datetime.utcnow())` – MarkHu Feb 01 '18 at 20:20
  • for the opposite operation go here: [in-python-how-do-you-convert-seconds-since-epoch-to-a-datetime-object](https://stackoverflow.com/questions/3694487/in-python-how-do-you-convert-seconds-since-epoch-to-a-datetime-object) – MarkHu Mar 18 '21 at 19:20

8 Answers8

564

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'
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
jleahy
  • 16,149
  • 6
  • 47
  • 66
  • 17
    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! – Jonathan Vanasco Jul 02 '13 at 18:09
  • 65
    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 – jfs Jul 02 '13 at 22:03
  • Why does it fail in that case? I was having similar issues with mktime. – Joshua Olson Jul 19 '13 at 01:10
  • AttributeError: 'datetime.datetime' object has no attribute 'total_seconds' – earthmeLon Aug 20 '13 at 21:48
  • 2
    @earthmeLon Your bracketing is wrong. Timedeltas (made by subtracting two datetimes) have total_seconds, but datetimes do not. – jleahy Aug 21 '13 at 10:24
  • this is 3 times faster than the time.mktime approach :) – Tommaso Barbugli Nov 06 '13 at 15:24
  • 2
    This is not working for me: `AttributeError: 'datetime.timedelta' object has no attribute 'total_seconds'` – Michael Apr 18 '14 at 18:17
  • 3
    @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. – jleahy Apr 21 '14 at 12:31
  • @TommasoBarbugli: if the answer is **wrong**; it doesn't matter how fast it is. – jfs Aug 22 '14 at 04:50
  • @J.F.Sebastian well perhaps the answer is a bit too simplistic; but what if one adds TZ handling? that worked well for me, can't say much about portability issues though... – Tommaso Barbugli Aug 22 '14 at 11:53
  • 1
    @TommasoBarbugli: I meant `mktime(utc_dt)` is wrong unless local time is UTC. The second example `(utc_dt - epoch).total_seconds()` is correct (within float precision). No TZ handling is required. The result is not *elapsed* seconds since 1970 (due to leap seconds) but it is correct POSIX timestamp ('seconds since Epoch'). – jfs Sep 04 '14 at 12:07
  • How to get above epoch in milliseconds, definitely I can multiply by 1000 but is there any functionality so that strftime directly return epochs in milliseconds. – Khatri Jul 26 '16 at 05:35
  • @Khatri you just have to multiply it by 1000 – jleahy Jul 31 '16 at 16:32
  • 1
    Doing this sort of manual subtraction arithmetic seems like an anti-pattern to me. -1 – Linus Arver Nov 02 '16 at 01:25
  • Doesn't work on Windows 7 , Python 2.7.12, error: `invalid format string` – benjaminz Nov 10 '16 at 16:47
  • `%s` is not the proper way to format. You should always use `.timestamp()`. Learned this the hard way :( – Blairg23 Apr 17 '17 at 16:53
  • To get local epoch on Cywin with this method you need to do: (datetime.utcnow() - datetime(1970,1,1)).total_seconds(). Yes, the utcnow makes it local epoch(!); without it, you get a weird timezone on Cygwin! – 00prometheus Nov 12 '17 at 23:59
  • My data is in UTC, but the .timestamp() is assuming it to be in local timezone and converting thus. How to specify, or make this thing assume the data is in UTC by default? – Nikhil VJ Nov 03 '19 at 12:03
  • @Moobie 's answer fixes the timezone issue, if your data was in UTC and not in local time. – Nikhil VJ Nov 03 '19 at 12:36
  • I'm on Python 3.6 and `datetime.datetime(2012,4,1,0,0).timestamp()` does not give the correct epoch time. Since I needed the epoch in milliseconds, I ended up doing: `(datetime_obj - datetime.datetime(1970, 1, 1)) // datetime.timedelta(milliseconds=1)` – Seperman Nov 06 '19 at 00:01
  • This does not work if your date is less then 1970. `OSError: [Errno 22] Invalid argument` – canbax Apr 15 '20 at 05:54
  • instead of `datetime.datetime(1970,1,1)` one can also use `datetime.datetime.fromtimestamp(0)`. That may be a little more obvious. – jaaq Jan 12 '21 at 16:30
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
Community
  • 1
  • 1
Savir
  • 17,568
  • 15
  • 82
  • 136
  • 7
    +1 because it is the only answer that works for the input in the question. – jfs Aug 22 '14 at 04:51
  • is this portable? – benjaminz Nov 10 '16 at 16:51
  • It should be, yes – Savir Nov 10 '16 at 18:07
  • 1
    This should be marked as the rightful answer, since this answers the concern in question. vnice kudos – Leo Prince Feb 21 '18 at 06:28
  • 3
    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. – Adam Williamson Mar 03 '18 at 01:57
  • 1
    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) – Savir Mar 03 '18 at 03:28
  • Worked like a charm for getting the UNIX timestamp of now. – rimkashox Apr 23 '21 at 13:20
37
import time
from datetime import datetime
now = datetime.now()

time.mktime(now.timetuple())
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
srossross
  • 1,353
  • 11
  • 16
  • 2
    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. – jfs Feb 18 '15 at 21:09
  • here're [more issues with converting a local time (such as returned by `.now()`) to epoch timestamp (returned by `mktime()`)](http://stackoverflow.com/a/26163706/4279). 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 – jfs Feb 18 '15 at 22:34
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)

Charles Plager
  • 494
  • 8
  • 21
  • That is because time.mktime does not take into consideration the microsecond part, right? – Eduardo Feb 18 '14 at 09:40
  • 1
    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. – Charles Plager Feb 25 '14 at 14:02
  • [it doesn't answer the question unless the local timezone is UTC](http://stackoverflow.com/questions/11743019/convert-python-datetime-to-epoch-with-strftime#comment45494488_17203956) – jfs Feb 18 '15 at 21:09
  • On my machines, this works correctly even though my time zone is ET. – Charles Plager Feb 17 '16 at 14:42
  • 1
    This will give you different timestamps on different systems based on the local time of the system. – Yousaf May 07 '19 at 15:24
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

Marc Maxmeister
  • 4,191
  • 4
  • 40
  • 54
4

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

Moobie
  • 1,445
  • 14
  • 21
  • saw a similar answer at https://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. – Nikhil VJ Nov 03 '19 at 12:39
  • 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) – canbax Apr 15 '20 at 05:58
3

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

Luis Pollo
  • 31
  • 2
  • 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 – Charles Plager Feb 07 '18 at 17:38
  • @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). – stewbasic Jun 25 '18 at 23:45
  • 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. – Yousaf May 07 '19 at 15:19
  • 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 – Yousaf May 07 '19 at 15:28
0

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

SuperNova
  • 25,512
  • 7
  • 93
  • 64