Convert an RFC 3339 time to a standard Python timestamp
Asked Answered
I

15

36

Is there an easy way to convert an RFC 3339 time into a regular Python timestamp?

I've got a script which is reading an ATOM feed and I'd like to be able to compare the timestamp of an item in the ATOM feed to the modification time of a file.

I notice from the ATOM spec, that ATOM dates include a time zone offset (Z<a number>) but, in my case, there's nothing after the Z so I guess we can assume GMT.

I suppose I could parse the time with a regex of some sort but I was hoping Python had a built-in way of doing it that I just haven't been able to find.

Instigate answered 21/12, 2009 at 19:0 Comment(3)
If you read the RFC 3339 spec, you will see that the only valid time zone offset values are: (0) a bare "Z", meaning the time stamp is UTC; or (1) an offset of the form "[+-]\d\d:\d\d" such as "+02:00" or "-08:00". Note that an offset of "+00:00" would mean the same thing as "Z". For more details, read the RFC 3339 spec: tools.ietf.org/html/rfc3339Redeemable
Python bug: issue15873: datetime: add ability to parse RFC 3339 dates and timesHelladic
related: Convert timestamps with offset to datetime obj using strptimeHelladic
A
14

No builtin, afaik.

feed.date.rfc3339 This is a Python library module with functions for converting timestamp strings in RFC 3339 format to Python time float values, and vice versa. RFC 3339 is the timestamp format used by the Atom feed syndication format.

It is BSD-licensed.

http://home.blarg.net/~steveha/pyfeed.html

(Edited so it's clear I didn't write it. :-)

Alithia answered 21/12, 2009 at 19:6 Comment(5)
PyFeed does exactly what I need, courtesy of the tf_from_timestamp() function in feed.date.rfc3339Instigate
Also, I wrote the PyFeed (and Xe) libraries, and I hang out here on StackOverflow, so if you have any questions about it, I would be happy to answer them.Redeemable
Note that PyFeed can be used to parse an Atom feed. It uses xml.dom.minidom to do the actual parsing, and then unpacks the XML tree structure into nice convenient classes. Hmm, I ought to put Xe and PyFeed up on PyPI.Redeemable
@Redeemable Excellent, thanks for the offer. The libraries seem pretty easy to use so far but I'll remember you're here if I run into anything weird.Instigate
@Alex Brasetvik, it was already clear that you were not claiming to have written it. If you were claiming credit you wouldn't have included the direct link to my web page! P.S. I was happy to see you recommending my library; thank you.Redeemable
M
38

You don't include an example, but if you don't have a Z-offset or timezone, and assuming you don't want durations but just the basic time, then maybe this will suit you:

import datetime as dt
>>> dt.datetime.strptime('1985-04-12T23:20:50.52', '%Y-%m-%dT%H:%M:%S.%f')
datetime.datetime(1985, 4, 12, 23, 20, 50, 520000)

The strptime() function was added to the datetime module in Python 2.5 so some people don't yet know it's there.

Edit: The time.strptime() function has existed for a while though, and works about the same to give you a struct_time value:

>>> ts = time.strptime('1985-04-12T23:20:50.52', '%Y-%m-%dT%H:%M:%S.%f')
>>> ts
time.struct_time(tm_year=1985, tm_mon=4, tm_mday=12, tm_hour=23, tm_min=20, tm_sec=50, tm_wday=4, tm_yday=102, tm_isdst=-1)
>>> time.mktime(ts)
482210450.0
Modernistic answered 21/12, 2009 at 19:20 Comment(5)
This won't work- Methods that are not time-zone aware are not RFC 3339 compatible.Jana
Yarin, clearly, but your complaint should be with the original question's use of "RFC 3339" then, as my answer did address his actual question, where he notes he doesn't have a time zone...Modernistic
Peter- The way I read his question he's trying to compare an ATOM feed RFC 3999 date with timezone to another date with assumed GMT, but maybe i don't get itJana
@Yarin: "there's nothing after the Z" -- it means the input is UTC and strptime() could be used. Though mktime() that expects time in the local timezone should not be used. calendar.timegm() could be used instead (to compare with the result of os.path.getmtime(): "I'd like to be able to compare the timestamp of an item in the ATOM feed to the modification time of a file.")Helladic
Add %z to the format argument of datetime.strptime() for RFC3339 with time-zone, see official documentAyah
L
16

I struggled with RFC3339 datetime format a lot, but I found a suitable solution to convert date_string <=> datetime_object in both directions.

You need two different external modules, because one of them is is only able to do the conversion in one direction (unfortunately):

first install:

sudo pip install rfc3339
sudo pip install iso8601

then include:

import datetime     # for general datetime object handling
import rfc3339      # for date object -> date string
import iso8601      # for date string -> date object

For not needing to remember which module is for which direction, I wrote two simple helper functions:

def get_date_object(date_string):
  return iso8601.parse_date(date_string)

def get_date_string(date_object):
  return rfc3339.rfc3339(date_object)

which inside your code you can easily use like this:

input_string = '1989-01-01T00:18:07-05:00'
test_date = get_date_object(input_string)
# >>> datetime.datetime(1989, 1, 1, 0, 18, 7, tzinfo=<FixedOffset '-05:00' datetime.timedelta(-1, 68400)>)

test_string = get_date_string(test_date)
# >>> '1989-01-01T00:18:07-05:00'

test_string is input_string # >>> True

Heureka! Now you can easily (haha) use your date strings and date strings in a useable format.

Lafleur answered 13/3, 2016 at 0:30 Comment(0)
A
14

No builtin, afaik.

feed.date.rfc3339 This is a Python library module with functions for converting timestamp strings in RFC 3339 format to Python time float values, and vice versa. RFC 3339 is the timestamp format used by the Atom feed syndication format.

It is BSD-licensed.

http://home.blarg.net/~steveha/pyfeed.html

(Edited so it's clear I didn't write it. :-)

Alithia answered 21/12, 2009 at 19:6 Comment(5)
PyFeed does exactly what I need, courtesy of the tf_from_timestamp() function in feed.date.rfc3339Instigate
Also, I wrote the PyFeed (and Xe) libraries, and I hang out here on StackOverflow, so if you have any questions about it, I would be happy to answer them.Redeemable
Note that PyFeed can be used to parse an Atom feed. It uses xml.dom.minidom to do the actual parsing, and then unpacks the XML tree structure into nice convenient classes. Hmm, I ought to put Xe and PyFeed up on PyPI.Redeemable
@Redeemable Excellent, thanks for the offer. The libraries seem pretty easy to use so far but I'll remember you're here if I run into anything weird.Instigate
@Alex Brasetvik, it was already clear that you were not claiming to have written it. If you were claiming credit you wouldn't have included the direct link to my web page! P.S. I was happy to see you recommending my library; thank you.Redeemable
D
14

The new datetime.fromisoformat(date_string) method which was added in Python 3.7 will parse most RFC 3339 timestamps, including those with time zone offsets. It's not a full implementation, so be sure to test your use case.

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('2011-11-04T00:05:23')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283+00:00')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('2011-11-04T00:05:23+04:00')   
datetime.datetime(2011, 11, 4, 0, 5, 23,
    tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))
Democratize answered 25/1, 2020 at 21:4 Comment(1)
Python 3.10.4 reported Invalid isoformat string: '2022-05-08T02:47:35.839Z'Souvenir
S
11

If you're using Django, you could use Django's function parse_datetime:

>>> from django.utils.dateparse import parse_datetime
>>> parse_datetime("2016-07-19T07:30:36+05:00")
datetime.datetime(2016, 7, 19, 7, 30, 36, tzinfo=<django.utils.timezone.FixedOffset object at 0x101c0c1d0>)
Schonfeld answered 26/8, 2016 at 14:25 Comment(0)
C
9

The simplest solution for me has been dateutil python standart library.

from dateutil.parser import parse

dt = "2020-11-23T11:08:23.022277705Z"
print(parse(dt))

Output:

2020-11-23 11:08:23.022277+00:00

If you don't need the timezone element, just simply set timezone info to None

print(parse(t).replace(tzinfo=None))

The output is a nice and clean datetime object:

2020-11-23 11:08:23.022277
Cheke answered 23/11, 2020 at 11:39 Comment(1)
dateutil is not part of the python standard library. It is installable via pip.Coe
A
8

http://pypi.python.org/pypi/iso8601/ seems to be able to parse iso 8601, which RFC 3339 is a subset of, maybe this could be useful, but again, not built-in.

Alluvion answered 21/12, 2009 at 19:13 Comment(1)
Time floats and struct_time aren't timezone aware. Since RFC 3339 requires UTC-compatible time zones, which in Python means non-naive datetime objects, this is the only sane option so far.Sowell
S
6

http://bugs.python.org/issue15873 (duplicate of http://bugs.python.org/issue5207 )

Looks like there isn't a built-in as of yet.

Stevens answered 21/12, 2009 at 19:5 Comment(0)
H
5

feedparser.py provides robust/extensible way to parse various date formats that may be encountered in real-world atom/rss feeds:

>>> from feedparser import _parse_date as parse_date
>>> parse_date('1985-04-12T23:20:50.52Z')
time.struct_time(tm_year=1985, tm_mon=4, tm_mday=12, tm_hour=23, tm_min=20,
                 tm_sec=50, tm_wday=4, tm_yday=102, tm_isdst=1)
Helladic answered 21/12, 2009 at 21:56 Comment(5)
This seems much nicer than the "time floats" offered by PyFeed.Implosion
cool-NR, if you have a time float value, you can call time.gmtime() and get the struct_time value. And it is a lot easier to do relative times with a time float value; two days from now is simply tf + 2 * seconds_per_day (where seconds_per_day is 24 * 60 * 60). Python's struct_time is great for inspecting (what day of the week is it?) but terribly inconvenient for computing.Redeemable
link's broken- should it be code.google.com/p/feedparser/source/browse/trunk/feedparser/…?Jana
It does not keep milliseconds?Selia
@LennartRolland: yes. time.struct_time doesn't store fractions of a second.Helladic
T
5

try this, it works fine for me

datetime_obj =  datetime.strptime("2014-01-01T00:00:00Z", '%Y-%m-%dT%H:%M:%SZ')

or

datetime_obj = datetime.strptime("Mon, 01 Jun 2015 16:41:40 GMT", '%a, %d %b %Y %H:%M:%S GMT')
Taster answered 19/8, 2015 at 14:13 Comment(1)
it doesn't support numeric utc offset: +HHMM. The second example is not rfc 3339; it is rfc 5322Helladic
B
2

Came across the awesome dateutil.parser module in another question, and tried it on my RFC3339 problem, and it appears to handle everything I throw at it with more sanity that any of the other responses in this question.

Bumper answered 27/8, 2012 at 13:20 Comment(1)
The only problem being that it also parses non-date values like "now"Songful
E
2

Using Python 3, you can use RegEx to break the RFC 3339 timestamp into its components. Then, directly create the datetime object, no additional modules needed:

import re
import datetime

def parse_rfc3339(dt):
    broken = re.search(r'([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]+))?(Z|([+-][0-9]{2}):([0-9]{2}))', dt)
    return(datetime.datetime(
        year = int(broken.group(1)),
        month = int(broken.group(2)),
        day = int(broken.group(3)),
        hour = int(broken.group(4)),
        minute = int(broken.group(5)),
        second = int(broken.group(6)),
        microsecond = int(broken.group(8) or "0"),
        tzinfo = datetime.timezone(datetime.timedelta(
            hours = int(broken.group(10) or "0"),
            minutes = int(broken.group(11) or "0")))))

This example theads missing timezones or microseconds as "0" but might need additional error checking. Cheers, Alex

Entomophilous answered 7/6, 2016 at 9:31 Comment(0)
D
2

You could use a Google API Core package. They have a really straightforward Datetime to RFC 3339 conversion function. You can find more info in their docs.

Its usage is as simple as:

from google.api_core.datetime_helpers import to_rfc3339

rfc3339_str = to_rfc3339(datetime.now())

They even have a function that works the other way around from_rfc3339 and from_rfc3339_nanos.

Dicot answered 19/1, 2021 at 19:14 Comment(0)
D
0

rfc3339 library: http://henry.precheur.org/python/rfc3339

Dendrology answered 30/8, 2011 at 13:46 Comment(0)
H
0

I have been doing a deep dive in dateimes and RFC3339 and recently come across the arrow library and have just used and solved my problem:

import arrow

date_string = "2015-11-24 00:00:00+00:00"
my_datetime = arrow.get(date_string).datetime
Hanshaw answered 21/8, 2020 at 15:33 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.