Convert python datetime to epoch with strftime
Asked Answered
D

8

306

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

Demonstrative answered 31/7, 2012 at 14:59 Comment(5)
related: Converting datetime.date to UTC timestamp in PythonPesach
Am I the only one noting that you use octal literals in the numbers?Ununa
related Python issue: datetime.strftime('%s') should respect tzinfoPesach
Newer Python 3.3+ has datetime.datetime.timestamp(datetime.datetime.utcnow())Benisch
for the opposite operation go here: in-python-how-do-you-convert-seconds-since-epoch-to-a-datetime-objectBenisch
P
575

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'
Pasto answered 31/7, 2012 at 15:10 Comment(23)
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!Laktasic
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 UTCPesach
Why does it fail in that case? I was having similar issues with mktime.Ventricose
AttributeError: 'datetime.datetime' object has no attribute 'total_seconds'Sheik
@Sheik Your bracketing is wrong. Timedeltas (made by subtracting two datetimes) have total_seconds, but datetimes do not.Pasto
this is 3 times faster than the time.mktime approach :)Schoof
This is not working for me: AttributeError: 'datetime.timedelta' object has no attribute 'total_seconds'Pinelli
@Pinelli 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.Pasto
@TommasoBarbugli: if the answer is wrong; it doesn't matter how fast it is.Pesach
@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...Schoof
@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').Pesach
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.Ectoenzyme
@Ectoenzyme you just have to multiply it by 1000Pasto
Doing this sort of manual subtraction arithmetic seems like an anti-pattern to me. -1Strained
Doesn't work on Windows 7 , Python 2.7.12, error: invalid format stringSeleneselenious
%s is not the proper way to format. You should always use .timestamp(). Learned this the hard way :(Sightly
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!Emelina
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?Hepatitis
@Moobie 's answer fixes the timezone issue, if your data was in UTC and not in local time.Hepatitis
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)Brahmin
This does not work if your date is less then 1970. OSError: [Errno 22] Invalid argumentAdamski
instead of datetime.datetime(1970,1,1) one can also use datetime.datetime.fromtimestamp(0). That may be a little more obvious.Ofay
Isn't there a problem with this answer: the OP says the date+time they want to convert is in UTC. The current version of the docs for .timestamp() states that "Naive datetime instances are assumed to represent local time". Thus datetime.datetime(2012,4,1,0,0).timestamp() gives the epoch for 2012-04-01 00:00 local time, not for 2012-04-01 00:00 UTC (unless your local time happens to be UTC). I think it needs to be datetime.datetime(2012,4,1,0,0, tzinfo=datetime.timezone.utc).timestamp() to be independent of your local timezone. Please correct me if I'm wrong :)Pansy
M
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
Mustachio answered 31/7, 2012 at 15:4 Comment(7)
+1 because it is the only answer that works for the input in the question.Pesach
is this portable?Seleneselenious
It should be, yesMustachio
This should be marked as the rightful answer, since this answers the concern in question. vnice kudosCurbing
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.Sweater
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)Mustachio
Worked like a charm for getting the UNIX timestamp of now.Arabelle
R
38
import time
from datetime import datetime
now = datetime.now()

time.mktime(now.timetuple())
Roble answered 20/6, 2013 at 1:31 Comment(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.Pesach
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 timePesach
L
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)

Linskey answered 23/9, 2013 at 19:20 Comment(5)
That is because time.mktime does not take into consideration the microsecond part, right?Pericycle
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.Linskey
it doesn't answer the question unless the local timezone is UTCPesach
On my machines, this works correctly even though my time zone is ET.Linskey
This will give you different timestamps on different systems based on the local time of the system.Elegancy
A
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

Angeloangelology answered 5/4, 2018 at 19:27 Comment(0)
E
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

Endorse answered 21/3, 2019 at 7:6 Comment(2)
saw a similar answer at https://mcmap.net/q/25358/-how-to-make-a-datetime-object-aware-not-naive, 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.Hepatitis
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)Adamski
D
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

Dundalk answered 9/9, 2017 at 0:49 Comment(4)
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-6Linskey
@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).Flexile
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.Elegancy
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.timetumpleElegancy
D
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

Discontinuance answered 25/11, 2018 at 13:51 Comment(1)
Be aware that this was introduced in Python 3.7.Imes

© 2022 - 2024 — McMap. All rights reserved.