Option: isoformat()

Python's datetime does not support the military timezone suffixes like 'Z' suffix for UTC. The following simple string replacement does the trick:

In [1]: import datetime

In [2]: d = datetime.datetime(2014, 12, 10, 12, 0, 0)

In [3]: str(d).replace('+00:00', 'Z')
Out[3]: '2014-12-10 12:00:00Z'

str(d) is essentially the same as d.isoformat(sep=' ')

See: Datetime, Python Standard Library

Option: strftime()

Or you could use strftime to achieve the same effect:

In [4]: d.strftime('%Y-%m-%dT%H:%M:%SZ')
Out[4]: '2014-12-10T12:00:00Z'

Note: This option works only when you know the date specified is in UTC.

See: datetime.strftime()


Additional: Human Readable Timezone

Going further, you may be interested in displaying human readable timezone information, pytz with strftime %Z timezone flag:

In [5]: import pytz

In [6]: d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=pytz.utc)

In [7]: d
Out[7]: datetime.datetime(2014, 12, 10, 12, 0, tzinfo=<UTC>)

In [8]: d.strftime('%Y-%m-%d %H:%M:%S %Z')
Out[8]: '2014-12-10 12:00:00 UTC'
Answer from Manav Kataria on Stack Overflow
Top answer
1 of 14
189

Option: isoformat()

Python's datetime does not support the military timezone suffixes like 'Z' suffix for UTC. The following simple string replacement does the trick:

In [1]: import datetime

In [2]: d = datetime.datetime(2014, 12, 10, 12, 0, 0)

In [3]: str(d).replace('+00:00', 'Z')
Out[3]: '2014-12-10 12:00:00Z'

str(d) is essentially the same as d.isoformat(sep=' ')

See: Datetime, Python Standard Library

Option: strftime()

Or you could use strftime to achieve the same effect:

In [4]: d.strftime('%Y-%m-%dT%H:%M:%SZ')
Out[4]: '2014-12-10T12:00:00Z'

Note: This option works only when you know the date specified is in UTC.

See: datetime.strftime()


Additional: Human Readable Timezone

Going further, you may be interested in displaying human readable timezone information, pytz with strftime %Z timezone flag:

In [5]: import pytz

In [6]: d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=pytz.utc)

In [7]: d
Out[7]: datetime.datetime(2014, 12, 10, 12, 0, tzinfo=<UTC>)

In [8]: d.strftime('%Y-%m-%d %H:%M:%S %Z')
Out[8]: '2014-12-10 12:00:00 UTC'
2 of 14
90

Python datetime objects don't have time zone info by default, and without it, Python actually violates the ISO 8601 specification (if no time zone info is given, assumed to be local time). You can use the pytz package to get some default time zones, or directly subclass tzinfo yourself:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
    def tzname(self,**kwargs):
        return "UTC"
    def utcoffset(self, dt):
        return timedelta(0)

Then you can manually add the time zone info to utcnow():

>>> datetime.utcnow().replace(tzinfo=simple_utc()).isoformat()
'2014-05-16T22:51:53.015001+00:00'

Note that this DOES conform to the ISO 8601 format, which allows for either Z or +00:00 as the suffix for UTC. Note that the latter actually conforms to the standard better, with how time zones are represented in general (UTC is a special case.)

🌐
Python.org
discuss.python.org › python help
Solved--Why does python not support the `Z` suffix for UTC timezone? - Python Help - Discussions on Python.org
January 1, 2024 - We can put Z at the end to express zero timezone according to ISO8601. [ISO 8601 - Wikipedia](https://iso8601 introduction) “2024-01-01T02:32:21Z” is the right format in ISO8601. >>> s = "2024-01-01T02:32:21Z" >>> dt …
Discussions

Parse "Z" timezone suffix in datetime - Ideas - Discussions on Python.org
This is already opened as BPO 35829 but I wanted to ask about it over here for discussion. Problem Statement The function datetime.fromisoformat() parses a datetime in ISO-8601, format: >>> datetime.fromisoformat('2019-08-28T14:34:25.518993+00:00') datetime.datetime(2019, 8, 28, 14, 34, 25, ... More on discuss.python.org
🌐 discuss.python.org
10
August 28, 2019
Add option to output UTC datetimes as "Z" in `.isoformat()`
BPO 46614 Nosy @brettcannon, @abalkin, @merwok, @pganssle, @godlygeek PRs #32041 Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current stat... More on github.com
🌐 github.com
18
February 2, 2022
datetime - ISO time (ISO 8601) in Python - Stack Overflow
I came across this question when ... (xs:dateTime). I needed to remove the microseconds from isoformat. ... I believe he means xs:date from XSD / XML stylesheets. 2018-11-16T22:00:08.12Z+00:00 ... The international standard ISO 8601 describes a string representation for dates and times. Two simple examples of this format are ... (which both stand for the 16th of December 2010), but the format also allows for sub-second resolution times and to specify time zones... More on stackoverflow.com
🌐 stackoverflow.com
ISO8601 time string with a Z comes out with +00:00 after load and then dump
When using the fields.DateTime and a value "2018-07-25T00:00:00Z". Loading and then dumping that data should result in the exact same value for that field. But it doesn't. What is dum... More on github.com
🌐 github.com
6
July 25, 2018
🌐
Reddit
reddit.com › r/python › psa: as of python 3.11, `datetime.fromisoformat` supports most iso 8601 formats (notably the "z" suffix)
r/Python on Reddit: PSA: As of Python 3.11, `datetime.fromisoformat` supports most ISO 8601 formats (notably the "Z" suffix)
August 28, 2023 -

In Python 3.10 and earlier, datetime.fromisoformat only supported formats outputted by datetime.isoformat. This meant that many valid ISO 8601 strings could not be parsed, including the very common "Z" suffix (e.g. 2000-01-01T00:00:00Z).

I discovered today that 3.11 supports most ISO 8601 formats. I'm thrilled: I'll no longer have to use a third-party library to ingest ISO 8601 and RFC 3339 datetimes. This was one of my biggest gripes with Python's stdlib.

It's not 100% standards compliant, but I think the exceptions are pretty reasonable:

  • Time zone offsets may have fractional seconds.

  • The T separator may be replaced by any single unicode character.

  • Ordinal dates are not currently supported.

  • Fractional hours and minutes are not supported.

https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat

🌐
Python
bugs.python.org › issue46614
Issue 46614: Add option to output UTC datetimes as "Z" in `.isoformat()` - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/90772
🌐
Python.org
discuss.python.org › ideas
Parse "Z" timezone suffix in datetime - Ideas - Discussions on Python.org
August 28, 2019 - This is already opened as BPO 35829 but I wanted to ask about it over here for discussion. Problem Statement The function datetime.fromisoformat() parses a datetime in ISO-8601, format: >>> datetime.fromisoformat('2019-08-28T14:34:25.518993+00:00') datetime.datetime(2019, 8, 28, 14, 34, 25, 518993, tzinfo=datetime.timezone.utc) The timezone offset in my example is +00:00, i.e. UTC.
🌐
Note.nkmk.me
note.nkmk.me › home › python
Convert Between Isoformat String and datetime in Python | note.nkmk.me
August 22, 2023 - To convert a basic format string to an extended format, first convert the basic format string to a datetime object, and then use the isoformat() method to obtain the extended format string. As mentioned above, use fromisoformat() for Python 3.11 and later, and strptime() for earlier versions.
🌐
GitHub
github.com › python › cpython › issues › 90772
Add option to output UTC datetimes as "Z" in `.isoformat()` · Issue #90772 · python/cpython
February 2, 2022 - python / cpython Public · There was an error while loading. Please reload this page. Notifications · You must be signed in to change notification settings · Fork 34.3k · Star 72k · New issueCopy link · New issueCopy link · Open · Open · Add option to output UTC datetimes as "Z" in .isoformat()#90772 ·
Author   pganssle
🌐
Ham1
blog.ham1.co.uk › 2023 › 05 › 29 › python-parse-iso-date-with-z-at-end
Parse ISO 8601 date ending in Z with Python - Graham Russell's Blog
May 29, 2023 - from datetime import datetime import pytz iso_date_string = "2023-05-29T10:30:00Z" parsed_date = datetime.fromisoformat(iso_date_string[:-1]) # Removing the 'Z' at the end utc_timezone = pytz.timezone('UTC') aware_date = parsed_date.replace(tzinfo=utc_timezone) print(aware_date)
Find elsewhere
🌐
GitHub
github.com › fastapi › fastapi › discussions › 9887
Converting datetime to UTC and ISO format ends up with Z instead of +00:00 · fastapi/fastapi · Discussion #9887
from fastapi import FastAPI from fastapi.testclient import TestClient from datetime import datetime, date import uvicorn import pytz import pytest app = FastAPI() @app.get("/") async def root(): test_date = datetime.now() test_date_tz = (datetime.now().replace(tzinfo=pytz.UTC)).isoformat() return { "date": test_date, "date_tz": test_date_tz } client = TestClient(app) def test_date(): response = client.get("/") print( response.json()) test_date = datetime.strptime(response.json()["date_tz"], "%Y-%m-%dT%H:%M:%S.%f+00:00").date() print(test_date) {'date': '2023-07-16T12:04:18.439371', 'date_tz': '2023-07-16T12:04:18.439375+00:00'} I do not get a Z in my response.
Author   fastapi
🌐
GeeksforGeeks
geeksforgeeks.org › python › isoformat-method-of-datetime-class-in-python
Isoformat() Method Of Datetime Class In Python - GeeksforGeeks
October 15, 2021 - Example 2: In the below example, the isoformat() function has been called on today's date and time and it returns the same today's date and time string in ISO 8601 format. ... # Python3 code to demonstrate # Getting date and time values # in ISO 8601 format # importing datetime and time module import datetime import time # Getting today's date and time todays_Date = datetime.datetime.now() # Calling the isoformat() function over the # today's date and time DateTime_in_ISOFormat = todays_Date.isoformat() # Printing Today's date and time in ISO format print("Today's date and time in ISO Format: %s" % DateTime_in_ISOFormat)
🌐
Joetsoi
joetsoi.github.io › fromisoformat-django-json-encoder-utc-datetimes
Avoid fromisoformat when parsing UTC date times serialized by DjangoJSONEncoder
class DjangoJSONEncoder(json.JSONEncoder): def default(self, o): # See "Date Time String Format" in the ECMA-262 specification. if isinstance(o, datetime.datetime): r = o.isoformat() if o.microsecond: r = r[:23] + r[26:] if r.endswith("+00:00"): r = r[:-6] + "Z" return r This makes sense as we're dealing with JSON here instead of the specifics of python's fromisoformat implementation.
🌐
Python
bugs.python.org › issue35829
Issue 35829: datetime: parse "Z" timezone suffix in fromisoformat() - Python tracker
January 25, 2019 - This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/80010
Top answer
1 of 14
1247

Local to ISO 8601:

import datetime
datetime.datetime.now().isoformat()
>>> '2024-08-01T14:38:32.499588'

UTC to ISO 8601:

import datetime
datetime.datetime.now(datetime.timezone.utc).isoformat()
>>> '2024-08-01T04:38:47.731215+00:00'

Local to ISO 8601 without microsecond:

import datetime
datetime.datetime.now().replace(microsecond=0).isoformat()
>>> '2024-08-01T14:38:57'

UTC to ISO 8601 with timezone information (Python 3):

import datetime
datetime.datetime.now(datetime.timezone.utc).isoformat()
>>> '2024-08-01T04:39:06.274874+00:00'

Local to ISO 8601 with timezone information (Python 3):

import datetime
datetime.datetime.now().astimezone().isoformat()
>>> '2024-08-01T14:39:16.698776+10:00'

Local to ISO 8601 with local timezone information without microsecond (Python 3):

import datetime
datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()
>>> '2024-08-01T14:39:28+10:00'

Notice there is a bug when using astimezone() on utcnow(). This gives an incorrect result:

datetime.datetime.utcnow().astimezone().isoformat() #Incorrect result, do not use.

.utcnow() is deprecated, use .now(datetime.timezome.utc) instead.

For Python 2, see and use pytz.

2 of 14
139

ISO 8601 allows a compact representation with no separators except for the T, so I like to use this one-liner to get a quick timestamp string:

>>> datetime.datetime.now(datetime.UTC).strftime("%Y%m%dT%H%M%S.%fZ")
'20180905T140903.591680Z'

If you don't need the microseconds, just leave out the .%f part:

>>> datetime.datetime.now(datetime.UTC).strftime("%Y%m%dT%H%M%SZ")
'20180905T140903Z'

For local time:

>>> datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=-5))).strftime("%Y-%m-%dT%H:%M:%S%:z")
'2018-09-05T14:09:03-05:00'

In general, I recommend you leave the punctuation in. RFC 3339 recommends that style because if everyone uses punctuation, there isn't a risk of things like multiple ISO 8601 strings being sorted in groups on their punctuation. So the one liner for a compliant string would be:

>>> datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
'2018-09-05T14:09:03Z'
🌐
Runebook.dev
runebook.dev › en › docs › python › library › datetime › datetime.datetime.isoformat
Troubleshooting Python's isoformat(): Timezones, Microseconds, and strftime()
import datetime import pytz # 1. Using UTC (Recommended for most backend tasks) utc_now = datetime.datetime.now(datetime.timezone.utc) utc_iso = utc_now.isoformat() print(f"UTC Aware ISO: {utc_iso}") # Output includes '+00:00' or 'Z' # 2.
🌐
Pythontic
pythontic.com › datetime › datetime › isoformat
The isoformat() method of datetime class in Python | Pythontic.com
The isoformat() method of datetime class returns a date and time string which contains the following information: Date · Time · UTC offset to corresponding time zone · as specified in the standard ISO 8601. The separator character will be printed between the date and time fields.
🌐
Python documentation
docs.python.org › 3 › library › datetime.html
datetime — Basic date and time types
The datetime module supplies classes for manipulating dates and times. While date and time arithmetic is supported, the focus of the implementation is on efficient attribute extraction for output formatting and manipulation. ... Skip to the format codes. ... General calendar related functions. ... Time access and conversions. ... Concrete time zones representing the IANA time zone database.
🌐
GitHub
github.com › marshmallow-code › marshmallow › issues › 889
ISO8601 time string with a Z comes out with +00:00 after load and then dump · Issue #889 · marshmallow-code/marshmallow
July 25, 2018 - When using the fields.DateTime and a value "2018-07-25T00:00:00Z". Loading and then dumping that data should result in the exact same value for that field. But it doesn't. What is dumped back out is "2018-07-25T00:00:00+00:00". This is n...
Author   nackjicholson
🌐
Python
bugs.python.org › issue23332
datetime.isoformat() -> explicitly mark UTC string as such
January 27, 2015 - This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/67521
🌐
GitHub
github.com › python › cpython › issues › 80010
datetime: Expand scope of fromisoformat to include all of ISO 8601. · Issue #80010 · python/cpython
January 25, 2019 - assignee = None closed_at = None created_at = <Date 2019-01-25.19:36:47.536> labels = ['type-feature', 'library', '3.11'] title = 'datetime: parse "Z" timezone suffix in fromisoformat()' updated_at = <Date 2022-02-02.17:32:27.477> user = 'https://github.com/rdb' bugs.python.org fields: activity = <Date 2022-02-02.17:32:27.477> actor = 'p-ganssle' assignee = 'none' closed = False closed_date = None closer = None components = ['Library (Lib)'] creation = <Date 2019-01-25.19:36:47.536> creator = 'rdb' dependencies = [] files = [] hgrepos = [] issue_num = 35829 keywords = [] message_count = 14.0 m
Author   rdb
Top answer
1 of 16
700

isoparse function from python-dateutil

The python-dateutil package has dateutil.parser.isoparse to parse not only RFC 3339 datetime strings like the one in the question, but also other ISO 8601 date and time strings that don't comply with RFC 3339 (such as ones with no UTC offset, or ones that represent only a date).

>>> import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)

The python-dateutil package also has dateutil.parser.parse. Compared with isoparse, it is presumably less strict, but both of them are quite forgiving and will attempt to interpret the string that you pass in. If you want to eliminate the possibility of any misreads, you need to use something stricter than either of these functions.

Comparison with Python 3.7+’s built-in datetime.datetime.fromisoformat

dateutil.parser.isoparse is a full ISO-8601 format parser, but in Python ≤ 3.10 fromisoformat is deliberately not. In Python 3.11, fromisoformat supports almost all strings in valid ISO 8601. See fromisoformat's docs for this cautionary caveat. (See this answer).

2 of 16
504

Since Python 3.11, the standard library’s datetime.datetime.fromisoformat supports most valid ISO 8601 input (and some non-valid input, see docs). In earlier versions it only parses a specific subset, see the cautionary note at the end of the docs. If you are using Python 3.10 or earlier on strings that don't fall into that subset (like in the question), see other answers for functions from outside the standard library.

The current docs (so exceptions listed are still valid for Python 3.13):

classmethod datetime.fromisoformat(date_string):

Return a datetime corresponding to a date_string in any valid ISO 8601 format, with the following exceptions:

  1. Time zone offsets may have fractional seconds.
  2. The T separator may be replaced by any single unicode character.
  3. Fractional hours and minutes are not supported.
  4. Reduced precision dates are not currently supported (YYYY-MM, YYYY).
  5. Extended date representations are not currently supported (±YYYYYY-MM-DD).
  6. Ordinal dates are not currently supported (YYYY-OOO).

Examples:

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('20111104')
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-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)
>>> 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)))

New in version 3.7.

Changed in version 3.11: Previously, this method only supported formats that could be emitted by date.isoformat() or datetime.isoformat().

If you only need dates, and not datetimes, you can use datetime.date.fromisoformat:

>>> from datetime import date
>>> date.fromisoformat("2024-01-31")
datetime.date(2024, 1, 31)