Generate RFC 3339 timestamp in Python

87,488

Solution 1

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

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

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

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

Another useful utility I just started working with: dateutil library for timezone handling and date parsing. Recommended around SO, including this answer

Share:
87,488

Related videos on Youtube

Yarin
Author by

Yarin

Products PDF Buddy - Popular online PDF editor Gems Snappconfig - Smarter Rails app configuration

Updated on July 05, 2022

Comments

  • Yarin
    Yarin almost 2 years

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

    • ruakh
      ruakh over 12 years
      Further down in the same doc that you linked to, it explains how to implement it, giving some examples, including full code for a UTC class (representing UTC), a FixedOffset class (representing a timezone with a fixed offset from UTC, as opposed to a timezone with DST and whatnot), and a few others.
    • Yarin
      Yarin over 12 years
      @ruakh- thanks, I missed those examples- The LocalTimezone() class did the trick.
    • Yarin
      Yarin over 12 years
      Just found this similar question: ISO Time (ISO 8601) in Python?
  • Yarin
    Yarin over 12 years
    @monkut- thanks- The pytz class looks like another implementation that would work, but I ended up using the example included in the docs, per ruakh's answer.
  • jfs
    jfs about 9 years
    you don't need pytz module if all you want is UTC timezone. It is simple to define utc tzinfo
  • Eli_B
    Eli_B about 9 years
    Thanks. I'll save your answer for when I need an stdlib solution. UTC was just the shortest to demonstrate. OP didn't specifically ask for it. pytz knows about all timezones and their DST, which I find useful.
  • jfs
    jfs about 9 years
    Note: get_localzone() in my answer returns puts timezone that corresponds to the local timezone.
  • jfs
    jfs about 9 years
    Yes. It should be" pytz timezone", not "puts timezone"
  • markos.aivazoglou
    markos.aivazoglou over 7 years
    Hands down, best answer for Python 3.3+
  • villapx
    villapx over 7 years
    Technically, ISO 8601 and RFC 3339 aren't 100% compatible...some differences exist, such as that ISO 8601 allows either a minus sign or a hyphen for the UTC offset part, whereas RFC 3339 only allows a hyphen. Every .isoformat() implementation I've seen so far (only a couple) does overlap with RFC 3339, but it's good to make yourself aware of the differences
  • jfs
    jfs over 7 years
    @villapx: rfc 3339 is a profile of ISO 8601. It happens that datetime.isoformat() is RFC 3339 for a timezone-aware datetime (that is why I've used it).
  • villapx
    villapx over 7 years
    @J.F.Sebastian Yeah, it seems you're right...from the docs, isotime() [gives] the UTC offset in (signed) hours and minutes: YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM, which is RFC 3339. It doesn't explicitly say it will use a hyphen as opposed to a minus sign, but it's implied by the examples
  • villapx
    villapx over 7 years
    Although, isoformat() doesn't look at the daylight savings time offset for the tzinfo object--as I change the dst() return value, isoformat() still returns the same string
  • jfs
    jfs over 7 years
    @villapx: isoformat() returns the correct value It just reflects what datetime object contains. Namely, it shows its .utcoffset() value.
  • villapx
    villapx over 7 years
    @J.F.Sebastian Right, that's what I mean--just wanted to make potential users aware that isoformat() isn't aware of DST
  • jfs
    jfs over 7 years
    @villapx: no, it is wrong. You are misleading the "potential users". If .utcoffset() doesn't reflect .dst() value then your datetime class is broken.
  • villapx
    villapx over 7 years
    @J.F.Sebastian Oh wow, you're right. I was totally misled by the datetime.tzinfo.dst() documentation: Note that DST offset, if applicable, has already been added to the UTC offset returned by utcoffset(), so there’s no need to consult dst() unless you’re interested in obtaining DST info separately. I did not see directly above that, for utcoffset(), where it says, "Note that this is intended to be the total offset from UTC."
  • Zeinab Abbasimazar
    Zeinab Abbasimazar over 6 years
    it doesn't return microseconds for me :-/
  • SH7890
    SH7890 over 5 years
    "Heureka". Lose the 'H'.
  • asu
    asu almost 4 years
    Actually the Python datetime is not compatible with ISO 8601, as it doesn't allow strings ending with 'Z'
  • jrc
    jrc about 3 years
    Can't it be just datetime.datetime.now().astimezone().isoformat()? now() returns a naive object and astimezone() sets the local timezone.
  • jfs
    jfs about 3 years
    @jrc: now() by itself may be ambiguous (e.g., during a DST transition).
  • uwieuwe4
    uwieuwe4 almost 3 years
    Exactly. How can I get the Z ending though?
  • JJC
    JJC almost 3 years
    @uwieuwe4 you can just use .replace('+00:00', 'Z') on the above result. Or, just use .strftime('%Y-%m-%dT%H:%M:%SZ') instead of .isoformat().
  • FObersteiner
    FObersteiner over 2 years
    dateutil is a library that offers functionality for parsing dates to datetime, I don't think this is helpful in the context of generating date/time strings with a certain format.