Generate RFC 3339 timestamp in Python
PythonDatetimeIso8601Rfc3339Python Problem Overview
I'm trying to generate an RFC 3339 UTC timestamp in Python. So far I've been able to do the following:
>>> d = datetime.datetime.now()
>>> print d.isoformat('T')
2011-12-18T20:46:00.392227
My problem is with setting the UTC offset.
According to the docs, the classmethod datetime.now([tz])
, takes an optional tz
argument where tz must be an instance of a class tzinfo subclass
, and datetime.tzinfo
is an abstract base class for time zone information objects.
This is where I get lost- How come tzinfo is an abstract class, and how am I supposed to implement it?
(NOTE: In PHP it's as simple as timestamp = date(DATE_RFC3339);
, which is why I can't understand why Python's approach is so convoluted...)
Python Solutions
Solution 1 - Python
UPDATE 2021
In Python 3.2 timezone
was added to the datetime module allowing you to easily assign a timezone to UTC.
>>> import datetime
>>> n = datetime.datetime.now(datetime.timezone.utc)
>>> n.isoformat()
'2021-07-13T15:28:51.818095+00:00'
previous answer:
Timezones are a pain, which is probably why they chose not to include them in the datetime library.
try pytz, it has the tzinfo your looking for: http://pytz.sourceforge.net/
You need to first create the datetime
object, then apply the timezone like as below, and then your .isoformat()
output will include the UTC offset as desired:
d = datetime.datetime.utcnow()
d_with_timezone = d.replace(tzinfo=pytz.UTC)
d_with_timezone.isoformat()
> '2017-04-13T14:34:23.111142+00:00'
Or, just use UTC, and throw a "Z" (for Zulu timezone) on the end to mark the "timezone" as UTC.
d = datetime.datetime.utcnow() # <-- get time in UTC
print d.isoformat("T") + "Z"
> '2017-04-13T14:34:23.111142Z'
Solution 2 - Python
In Python 3.3+:
>>> from datetime import datetime, timezone
>>> local_time = datetime.now(timezone.utc).astimezone()
>>> local_time.isoformat()
'2015-01-16T16:52:58.547366+01:00'
On older Python versions, if all you need is an aware datetime object representing the current time in UTC then you could define a simple tzinfo subclass as shown in the docs to represent UTC timezone:
from datetime import datetime
utc_now = datetime.now(utc)
print(utc_now.isoformat('T'))
# -> 2015-05-19T20:32:12.610841+00:00
You could also use tzlocal
module to get pytz
timezone representing your local timezone:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # $ pip install tzlocal
now = datetime.now(get_localzone())
print(now.isoformat('T'))
It works on both Python 2 and 3.
Solution 3 - Python
On modern (3.x) python, to get RFC 3339 UTC time, all you need to do is use datetime and this single line (no third-party modules necessary):
import datetime
datetime.datetime.now(datetime.timezone.utc).isoformat()
The result is something like: '2019-06-13T15:29:28.972488+00:00'
This ISO 8601 string is also RFC3339 compatible.
Solution 4 - Python
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.
Solution 5 - Python
Another useful utility I just started working with: dateutil library for timezone handling and date parsing. Recommended around SO, including this answer
Solution 6 - Python
The pytz package is available for Python 2.X and 3.X. It implements concrete subclasses of tzinfo
, among other services, so you don't have to.
To add a UTC offset: import datetime import pytz
dt = datetime.datetime(2011, 12, 18, 20, 46, 00, 392227)
utc_dt = pytz.UTC.localize(dt)
And now this:
print utc_dt.isoformat()
would print:
2011-12-18T20:46:00.392227+00:00
Solution 7 - Python
You can indeed use the built-in datetime module. As @ruakh mentions, there are examples in the page that show how. If you look into the section on tzinfo you'll see a long example showing many different use cases. Here's the code for the one you're looking for, which is to generate an RFC 3339 UTC timestamp.
from datetime import tzinfo, timedelta, datetime
import time as _time
ZERO = timedelta(0)
STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
DSTOFFSET = timedelta(seconds=-_time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class LocalTimezone(tzinfo):
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, 0)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
Local = LocalTimezone()
d = datetime.now(Local)
print d.isoformat('T')
# which returns
# 2014-04-28T15:44:45.758506-07:00