35

I have the following string:

mytime = "2009-03-08T00:27:31.807Z"

How do I convert it to epoch in python?

I tried:

import time
p = '%Y-%m-%dT%H:%M:%S'
int(time.mktime(time.strptime(s, p)))

But it does not work with the 31.807Z.

FObersteiner
  • 22,500
  • 8
  • 42
  • 72
Rolando
  • 58,640
  • 98
  • 266
  • 407

7 Answers7

39

There are two parts:

  1. Convert the time string into a broken-down time. See How to parse ISO formatted date in python?
  2. Convert the UTC time to "seconds since the Epoch" (POSIX timestamp).
#!/usr/bin/env python
from datetime import datetime

utc_time = datetime.strptime("2009-03-08T00:27:31.807Z", "%Y-%m-%dT%H:%M:%S.%fZ")
epoch_time = (utc_time - datetime(1970, 1, 1)).total_seconds()
# -> 1236472051.807

If you are sure that you want to ignore fractions of a second and to get an integer result:

#!/usr/bin/env python
import time
from calendar import timegm

utc_time = time.strptime("2009-03-08T00:27:31.807Z", "%Y-%m-%dT%H:%M:%S.%fZ")
epoch_time = timegm(utc_time)
# -> 1236472051

To support timestamps that correspond to a leap second such as Wed July 1 2:59:60 MSK 2015, you could use a combination of time.strptime() and datetime (if you care about leap seconds you should take into account the microseconds too).

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • what if you get "2009-03-08T00:27:31.807A" or any offset from zulu time? but +1 for locale aware time conversion :) – Joran Beasley May 27 '15 at 17:31
  • @JoranBeasley In practice, rfc 3339 works unless you have some special requirements. `Z` is the only allowed letter https://tools.ietf.org/html/rfc3339#section-5.6 – jfs Mar 04 '20 at 16:55
16

You are missing .%fZ from your format string.

p = '%Y-%m-%dT%H:%M:%S.%fZ'

The correct way to convert to epoch is to use datetime:

from datetime import datetime

p = '%Y-%m-%dT%H:%M:%S.%fZ'
mytime = "2009-03-08T00:27:31.807Z"
epoch = datetime(1970, 1, 1)
print((datetime.strptime(mytime, p) - epoch).total_seconds())

Or call int if you want to ignore fractions.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 1
    @JoranBeasley. In my infinite wisdom I predicted it would always end in `Z`, I suppose you could always ignore `Z` and rstrip("Z") – Padraic Cunningham May 26 '15 at 20:45
  • 1
    don't use `time.mktime()` on UTC time! – jfs May 27 '15 at 07:37
  • here are the possible terminal characters http://en.wikipedia.org/wiki/List_of_military_time_zones .... its usually Z for most things but that letter has meaning that this discards :/ still solid answer of coarse :) – Joran Beasley May 27 '15 at 17:32
  • @J.F.Sebastian, yes, I read the post when you commented on joran's answer. – Padraic Cunningham May 30 '15 at 21:48
  • note: `mktime()` will return a wrong result here for any local timezone with non-zero UTC offset (most of them). – jfs May 30 '15 at 22:02
  • @J.F.Sebastian, yes, I answered using mktime as that was what the OP was using and corrected the formatting mistake which was causing the OP's problem, I was going to add the datetime solution but there were already an answer using a datetime approach so I thought it was pointless repeating the same answer until some mysterious stranger downvoted so I said I better add the datetime approach. – Padraic Cunningham May 30 '15 at 22:22
  • 1
    I've downvoted and left the comment about `mktime()`. "OP uses broken code in the question" is not a valid excuse for putting it in your answer. Personally, I add something like `#XXX WRONG, DON'T USE` at the top of a code fragment if I have to include the broken code. It is not enough to put a valid answer somewhere at the bottom. – jfs May 30 '15 at 22:40
  • You sir, are a gentleman! Thank you, this worked excellently for me, though I have the exact same time format as the OP. – agrippa Jan 17 '19 at 01:11
14

dateutil has recently been added back to python packages, it's an easy one liner that handles formatting on its own.

from dateutil import parser
strtime = '2009-03-08T00:27:31.807Z'
epoch = parser.parse(strtime).timestamp()
aSaffary
  • 793
  • 9
  • 22
  • Only answers with no context and extra text are not the best. You are basically asking the OP to copy and paste your answer without knowing what the code does. Please provide an explanation. Thank You! – Buddy Bob May 10 '21 at 21:25
5

dateutil is the only library i have found that correctly deals with the timezone offset identitifier (Z)

pip install python-dateutil

then

from dateutil.parser import parse as date_parse
print date_parse("2009-03-08T00:27:31.807Z")
#get timestamp

import calendar
dt =  date_parse("2009-03-08T00:27:31.807Z")
timestamp1 = calendar.timegm(dt.timetuple())
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • 1
    probably a more robust solution. – Padraic Cunningham May 26 '15 at 21:09
  • @PadraicCunningham: it is less robust if you want to reject timestamps that use a wrong format. `dateutil` may accept too much. – jfs May 27 '15 at 07:42
  • it is an overkill to use `dateutil` for that. You can parse the time string using only stdlib in both Python 2 and 3. `timegm()` is ok if you don't care about fractions of a second. – jfs May 27 '15 at 08:05
  • not if you dont want to lose the information imparted by the terminal letter http://en.wikipedia.org/wiki/List_of_military_time_zones – Joran Beasley May 27 '15 at 17:33
  • @JoranBeasley: The only allowed letter is Z. Read [RFC 3339](https://tools.ietf.org/html/rfc3339). – jfs May 27 '15 at 18:36
2

Code:

import datetime
epoch = datetime.datetime(1970, 1, 1)

mytime = "2009-03-08T00:27:31.807Z"
myformat = "%Y-%m-%dT%H:%M:%S.%fZ"
mydt = datetime.datetime.strptime(mytime, myformat)
val = (mydt - epoch).total_seconds()

print(val)
> 1236472051.81
repr(val)
> '1236472051.807'

Notes:

  • When using time.strptime(), the returned time.struct_time does not support sub-second precision.
  • The %f format is for microseconds. When parsing it need not be the full 6 digits.
Nayuki
  • 17,911
  • 6
  • 53
  • 80
1

This code works in Python 3.6 to convert a datetime string to epoch in UTC or local timezone.

from datetime import datetime, timedelta
from dateutil.tz import tzutc, tzlocal

mydate = '2020-09-25'
mytime = '06:00:00'

epoch1970 = datetime(1970, 1, 1, 0, 0, 0, tzinfo=tzutc())

myepochutc = int((datetime.strptime(mydate + ' ' + mytime, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzutc()) - epoch1970).total_seconds()*1000)

myepochlocal = int((datetime.strptime(mydate + ' ' + mytime, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzlocal()) - epoch1970).total_seconds()*1000)

#epoch will be in milliseconds
print(myepochutc)   #if mydate/mytime was in utc
print(myepochlocal) #if mydate/mytime was in local timezone

ebeb
  • 429
  • 3
  • 12
0

Python 3.7+ The string format in question can be parsed by strptime:

from datetime import datetime
datetime.strptime("2009-03-08T00:27:31.807Z", '%Y-%m-%dT%H:%M:%S.%f%z')
>>> datetime.datetime(2009, 3, 8, 0, 27, 31, 807000, tzinfo=datetime.timezone.utc)

Another option using the built-in datetime.fromisoformat(): As mentioned in this thread linked by @jfs, fromisoformat() doesn't parse the 'Z' character to UTC although this is part of the RFC3339 definitions. A little work-around can make it work - some will consider this nasty but it's efficient after all.

from datetime import datetime
mytime = "2009-03-08T00:27:31.807Z"
datetime.fromisoformat(mytime.replace("Z", "+00:00")).timestamp()
>>> 1236472051.807
Community
  • 1
  • 1
FObersteiner
  • 22,500
  • 8
  • 42
  • 72