Convert Python datetime to rfc 2822
Question:
I want to convert a Python datetime to an RFC 2822 datetime. I’ve tried these methods to no avail:
>>> from email.Utils import formatdate
>>> import datetime
>>> formatdate(datetime.datetime.now())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/email /utils.py", line 159, in formatdate
now = time.gmtime(timeval)
TypeError: a float is required
Answers:
If you indeed want the current time, just call formatdate
with no arguments:
>>> from email.utils import formatdate
>>> formatdate()
'Tue, 10 Aug 2010 20:40:23 -0000'
But, if you must pass it an argument, you want the output of time.time
(a number of seconds since 01/01/1970):
>>> import time
>>> formatdate(time.time())
'Tue, 10 Aug 2010 20:41:43 -0000'
FWIW, datetime.datetime.now()
returns a datetime
object, which is not what formatdate
expects.
Edited to add: if you already have a datetime object, you can format it appropriately for formatdate:
>>> import datetime
>>> dt = datetime.datetime.now()
>>> formatdate(float(dt.strftime('%s')))
'Tue, 10 Aug 2010 20:46:16 -0000'
Edited: Alex Martelli noted that the ‘%s’ format string for strftime may not be portable across platforms. A possible alternative would be, as he himself suggested,
>>> formatdate(time.mktime(dt.timetuple()))
'Tue, 10 Aug 2010 20:46:16 -0000'
Here’s some working code, broken down into simple pieces just for clarity:
>>> import datetime
>>> import time
>>> from email import utils
>>> nowdt = datetime.datetime.now()
>>> nowtuple = nowdt.timetuple()
>>> nowtimestamp = time.mktime(nowtuple)
>>> utils.formatdate(nowtimestamp)
'Tue, 10 Aug 2010 20:43:53 -0000'
Explanation: email.utils.formatdate
wants a timestamp — i.e., a float with seconds (and fraction thereof) since the epoch. A datetime
instance doesn’t give you a timestamp directly — but, it can give you a time-tuple with the timetuple
method, and time.mktime
of course can then make a timestamp from such a tuple.
EDIT: In Python 3.3 and newer you can do the same in less steps:
>>> import datetime
>>> from email import utils
>>> nowdt = datetime.datetime.now()
>>> utils.format_datetime(nowdt)
'Tue, 10 Feb 2020 10:06:53 -0000'
See format_datetime
docs for details on usage.
Python 3.3, in addition to the methods mentioned by other commenters, also added a format_datetime
method, which is much cleaner:
>>> import datetime
>>> import email.utils
>>> dt = datetime.datetime.now()
>>> email.utils.format_datetime(dt)
'Thu, 09 Mar 2017 10:50:00 -0000'
Simple method using strftime. Python 2.7. Note: timezone = EST
>>> from datetime import datetime
>>> my_date = datetime.now()
>>> my_date.strftime('%a, %d %b %Y %H:%M:%S -0500')
'Wed, 22 Apr 2020 10:52:11 -0500'
If you’re using this for e.g. a HTTP header, and want GMT
rather than -0000
:
-
From a datetime
, use format_datetime
(emphasis mine):
If it is an aware timezone with offset zero, then usegmt may be set to
True
, in which case the string GMT
is used instead of the numeric
timezone offset.
>>> from datetime import datetime, timezone
>>> from email.utils import format_datetime
>>> format_datetime(datetime.now(timezone.utc), usegmt=True)
'Wed, 27 Oct 2021 11:00:46 GMT'
-
From a timestamp, use formatdate
:
Optional usegmt is a flag that when True
, outputs a date string
with the timezone as an ascii string GMT
, rather than a numeric
-0000
… This only applies when localtime is False
.
>>> from email.utils import formatdate
>>> from time import time
>>> formatdate(time(), usegmt=True)
'Wed, 27 Oct 2021 11:05:29 GMT'
I want to convert a Python datetime to an RFC 2822 datetime. I’ve tried these methods to no avail:
>>> from email.Utils import formatdate
>>> import datetime
>>> formatdate(datetime.datetime.now())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/email /utils.py", line 159, in formatdate
now = time.gmtime(timeval)
TypeError: a float is required
If you indeed want the current time, just call formatdate
with no arguments:
>>> from email.utils import formatdate
>>> formatdate()
'Tue, 10 Aug 2010 20:40:23 -0000'
But, if you must pass it an argument, you want the output of time.time
(a number of seconds since 01/01/1970):
>>> import time
>>> formatdate(time.time())
'Tue, 10 Aug 2010 20:41:43 -0000'
FWIW, datetime.datetime.now()
returns a datetime
object, which is not what formatdate
expects.
Edited to add: if you already have a datetime object, you can format it appropriately for formatdate:
>>> import datetime
>>> dt = datetime.datetime.now()
>>> formatdate(float(dt.strftime('%s')))
'Tue, 10 Aug 2010 20:46:16 -0000'
Edited: Alex Martelli noted that the ‘%s’ format string for strftime may not be portable across platforms. A possible alternative would be, as he himself suggested,
>>> formatdate(time.mktime(dt.timetuple()))
'Tue, 10 Aug 2010 20:46:16 -0000'
Here’s some working code, broken down into simple pieces just for clarity:
>>> import datetime
>>> import time
>>> from email import utils
>>> nowdt = datetime.datetime.now()
>>> nowtuple = nowdt.timetuple()
>>> nowtimestamp = time.mktime(nowtuple)
>>> utils.formatdate(nowtimestamp)
'Tue, 10 Aug 2010 20:43:53 -0000'
Explanation: email.utils.formatdate
wants a timestamp — i.e., a float with seconds (and fraction thereof) since the epoch. A datetime
instance doesn’t give you a timestamp directly — but, it can give you a time-tuple with the timetuple
method, and time.mktime
of course can then make a timestamp from such a tuple.
EDIT: In Python 3.3 and newer you can do the same in less steps:
>>> import datetime
>>> from email import utils
>>> nowdt = datetime.datetime.now()
>>> utils.format_datetime(nowdt)
'Tue, 10 Feb 2020 10:06:53 -0000'
See format_datetime
docs for details on usage.
Python 3.3, in addition to the methods mentioned by other commenters, also added a format_datetime
method, which is much cleaner:
>>> import datetime
>>> import email.utils
>>> dt = datetime.datetime.now()
>>> email.utils.format_datetime(dt)
'Thu, 09 Mar 2017 10:50:00 -0000'
Simple method using strftime. Python 2.7. Note: timezone = EST
>>> from datetime import datetime
>>> my_date = datetime.now()
>>> my_date.strftime('%a, %d %b %Y %H:%M:%S -0500')
'Wed, 22 Apr 2020 10:52:11 -0500'
If you’re using this for e.g. a HTTP header, and want GMT
rather than -0000
:
-
From a
datetime
, useformat_datetime
(emphasis mine):If it is an aware timezone with offset zero, then usegmt may be set to
True
, in which case the stringGMT
is used instead of the numeric
timezone offset.>>> from datetime import datetime, timezone >>> from email.utils import format_datetime >>> format_datetime(datetime.now(timezone.utc), usegmt=True) 'Wed, 27 Oct 2021 11:00:46 GMT'
-
From a timestamp, use
formatdate
:Optional usegmt is a flag that when
True
, outputs a date string
with the timezone as an ascii stringGMT
, rather than a numeric
-0000
… This only applies when localtime isFalse
.>>> from email.utils import formatdate >>> from time import time >>> formatdate(time(), usegmt=True) 'Wed, 27 Oct 2021 11:05:29 GMT'