How do I parse an ISO 8601-formatted date?

467

Solution 1

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 fromisoformat is deliberately not. Please see the latter function's docs for this cautionary caveat. (See this answer).

Solution 2

The datetime standard library has, since Python 3.7, a function for inverting datetime.isoformat().

classmethod datetime.fromisoformat(date_string):

Return a datetime corresponding to a date_string in one of the formats emitted by date.isoformat() and datetime.isoformat().

Specifically, this function supports strings in the format(s):

YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]

where * can match any single character.

Caution: This does not support parsing arbitrary ISO 8601 strings - it is only intended as the inverse operation of datetime.isoformat().

Examples:

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)

Be sure to read the caution from the docs!

Solution 3

Note in Python 2.6+ and Py3K, the %f character catches microseconds.

>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

See issue here

Solution 4

Several answers here suggest using datetime.datetime.strptime to parse RFC 3339 or ISO 8601 datetimes with timezones, like the one exhibited in the question:

2008-09-03T20:56:35.450686Z

This is a bad idea.

Assuming that you want to support the full RFC 3339 format, including support for UTC offsets other than zero, then the code these answers suggest does not work. Indeed, it cannot work, because parsing RFC 3339 syntax using strptime is impossible. The format strings used by Python's datetime module are incapable of describing RFC 3339 syntax.

The problem is UTC offsets. The RFC 3339 Internet Date/Time Format requires that every date-time includes a UTC offset, and that those offsets can either be Z (short for "Zulu time") or in +HH:MM or -HH:MM format, like +05:00 or -10:30.

Consequently, these are all valid RFC 3339 datetimes:

  • 2008-09-03T20:56:35.450686Z
  • 2008-09-03T20:56:35.450686+05:00
  • 2008-09-03T20:56:35.450686-10:30

Alas, the format strings used by strptime and strftime have no directive that corresponds to UTC offsets in RFC 3339 format. A complete list of the directives they support can be found at https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior, and the only UTC offset directive included in the list is %z:

%z

UTC offset in the form +HHMM or -HHMM (empty string if the the object is naive).

Example: (empty), +0000, -0400, +1030

This doesn't match the format of an RFC 3339 offset, and indeed if we try to use %z in the format string and parse an RFC 3339 date, we'll fail:

>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'

(Actually, the above is just what you'll see in Python 3. In Python 2 we'll fail for an even simpler reason, which is that strptime does not implement the %z directive at all in Python 2.)

The multiple answers here that recommend strptime all work around this by including a literal Z in their format string, which matches the Z from the question asker's example datetime string (and discards it, producing a datetime object without a timezone):

>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

Since this discards timezone information that was included in the original datetime string, it's questionable whether we should regard even this result as correct. But more importantly, because this approach involves hard-coding a particular UTC offset into the format string, it will choke the moment it tries to parse any RFC 3339 datetime with a different UTC offset:

>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
    tt, fraction = _strptime(data_string, format)
  File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

Unless you're certain that you only need to support RFC 3339 datetimes in Zulu time, and not ones with other timezone offsets, don't use strptime. Use one of the many other approaches described in answers here instead.

Solution 5

Try the iso8601 module; it does exactly this.

There are several other options mentioned on the WorkingWithTime page on the python.org wiki.

Share:
467
Robin Green
Author by

Robin Green

Updated on July 08, 2022

Comments

  • Robin Green
    Robin Green almost 2 years

    I am running a specs2 test suite from sbt, using the test command. When a ScalaCheck property fails, I just get to see the filename and line number in my code where the specs2 match fails - which is not very useful when that happens to be a utility method which does a common type of check that I am frequently doing. A stack trace would be better.

    I've tried the last command in sbt, but that doesn't display the stack trace I'm looking for. The only stack trace last displays is this generic one:

    java.lang.RuntimeException: Tests unsuccessful
            at scala.sys.package$.error(package.scala:27)
            at scala.Predef$.error(Predef.scala:66)
            at sbt.Tests$.showResults(Tests.scala:168)
            at sbt.Defaults$$anonfun$testTasks$5.apply(Defaults.scala:279)
            at sbt.Defaults$$anonfun$testTasks$5.apply(Defaults.scala:279)
            at sbt.Scoped$$anonfun$hf2$1.apply(Structure.scala:473)
            at sbt.Scoped$$anonfun$hf2$1.apply(Structure.scala:473)
            at scala.Function1$$anonfun$compose$1.apply(Function1.scala:41)
            at sbt.Scoped$Reduced$$anonfun$combine$1$$anonfun$apply$11.apply(Structure.scala:295)
            at sbt.Scoped$Reduced$$anonfun$combine$1$$anonfun$apply$11.apply(Structure.scala:295)
            at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
            at sbt.std.Transform$$anon$5.work(System.scala:67)
            at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:221)
            at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:221)
            at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
            at sbt.Execute.work(Execute.scala:227)
            at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:221)
            at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:221)
            at sbt.CompletionService$$anon$1$$anon$2.call(CompletionService.scala:26)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
            at java.util.concurrent.FutureTask.run(FutureTask.java:138)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
            at java.util.concurrent.FutureTask.run(FutureTask.java:138)
            at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
            at java.lang.Thread.run(Thread.java:662)
    

    I also have FINEST logging level enabled in the java.util.logging properties file.

    For now I am working around this issue using the Eclipse debugger, but that's unnecessarily heavyweight in some cases.

  • Alexander Artemenko
    Alexander Artemenko almost 16 years
    You can't just strip .Z because it means timezone and can be different. I need to convert date to the UTC timezone.
  • Ishbir
    Ishbir almost 16 years
    A plain datetime object has no concept of timezone. If all your times are ending in "Z", all the datetimes you get are UTC (Zulu time).
  • SingleNegationElimination
    SingleNegationElimination almost 13 years
    if the timezone is anything other than "" or "Z", then it must be an offset in hours/minutes, which can be directly added to/subtracted from the datetime object. you could create a tzinfo subclass to handle it, but that's probably not reccomended.
  • umbrae
    umbrae over 12 years
    I disagree, this is practically unreadable and as far as I can tell does not take into account the Zulu (Z) which makes this datetime naive even though time zone data was provided.
  • Pakman
    Pakman about 12 years
    Simple as iso8601.parse_date("2008-09-03T20:56:35.450686Z")
  • quodlibetor
    quodlibetor almost 12 years
    Additionally, "%f" is the microsecond specifier, so a (timezone-naive) strptime string looks like: "%Y-%m-%dT%H:%M:%S.%f" .
  • Nicholas Riley
    Nicholas Riley almost 12 years
    The question wasn't "how do I parse ISO 8601 dates", it was "how do I parse this exact date format."
  • Robin Green
    Robin Green over 11 years
    Unfortunately, I added both of these but now I'm encountering a different failed match, and I could not see where the match fails. I think the stack trace is generated after the match actually fails.
  • Tobia
    Tobia over 11 years
    I find it quite readable. In fact, it's probably the easiest and most performing way to do the conversion without installing additional packages.
  • Tobia
    Tobia over 11 years
    @tiktak The OP asked "I need to parse strings like X" and my reply to that, having tried both libraries, is to use another one, because iso8601 has important issues still open. My involvement or lack thereof in such a project is completely unrelated to the answer.
  • gatoatigrado
    gatoatigrado over 11 years
    This just ignores the timezone '2013-01-28T14:01:01.335612-08:00' --> parsed as UTC, not PDT
  • Xuan
    Xuan about 11 years
    This is equivalent of d=datetime.datetime(*map(int, re.split('\D', s)[:-1])) i suppose.
  • cod3monk3y
    cod3monk3y over 10 years
    For the lazy, it's installed via python-dateutil not dateutil, so: pip install python-dateutil.
  • enchanter
    enchanter about 10 years
    def from_utc(date_str): """ Convert UTC time data string to time.struct_time """ UTC_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" return time.strptime(date_str, UTC_FORMAT)
  • jfs
    jfs about 10 years
    a variation: datetime.datetime(*map(int, re.findall('\d+', s))
  • w00t
    w00t about 10 years
    This results in a naive datetime object without timezone, right? So the UTC bit gets lost in translation?
  • jfs
    jfs over 9 years
    @w00t: aware_d = d.replace(tzinfo=timezone.utc)
  • Dave Hein
    Dave Hein over 9 years
    iso8601, a.k.a. pyiso8601, has been updated as recently as Feb 2014. The latest version supports a much broader set of ISO 8601 strings. I've been using to good effect in some of my projects.
  • Eric
    Eric over 9 years
    This has the benefit of working with incomplete iso strings including dates and second-less datetimes
  • Danny Staple
    Danny Staple over 9 years
    Note - if using Naive datetimes - I think you get no TZ at all - Z may not match anything.
  • ivan_pozdeev
    ivan_pozdeev about 9 years
    Be warned that the dateutil.parser is intentionally hacky: it tries to guess the format and makes inevitable assumptions (customizable by hand only) in ambiguous cases. So ONLY use it if you need to parse input of unknown format and are okay to tolerate occasional misreads.
  • Mark Amery
    Mark Amery about 9 years
    This answer (in its current, edited form) relies upon hard-coding a particular UTC offset (namely "Z", which means +00:00) into the format string. This is a bad idea because it will fail to parse any datetime with a different UTC offset and raise an exception. See my answer that describes how parsing RFC 3339 with strptime is in fact impossible.
  • Mark Amery
    Mark Amery about 9 years
    This will raise an exception if the given datetime string has a UTC offset other than "Z". It does not support the entire RFC 3339 format and is an inferior answer to others that handle UTC offsets properly.
  • Mark Amery
    Mark Amery about 9 years
    This answer relies upon hard-coding a particular UTC offset (namely "Z", which means +00:00) into the format string passed to strptime. This is a bad idea because it will fail to parse any datetime with a different UTC offset and raise an exception. See my answer that describes how parsing RFC 3339 with strptime is in fact impossible.
  • Mark Amery
    Mark Amery about 9 years
    This answer relies upon hard-coding a particular UTC offset (namely "Z", which means +00:00) into the format string passed to strptime. This is a bad idea because it will fail to parse any datetime with a different UTC offset and raise an exception. See my answer that describes how parsing RFC 3339 with strptime is in fact impossible.
  • Sasha
    Sasha almost 9 years
    It's hard-coded but its sufficient for case when you need to parse zulu only.
  • Mark Amery
    Mark Amery almost 9 years
    @alexander yes - which may be the case if, for instance, you know that your date string was generated with JavaScript's toISOString method. But there's no mention of the limitation to Zulu time dates in this answer, nor did the question indicate that that's all that's needed, and just using dateutil is usually equally convenient and less narrow in what it can parse.
  • Csaba Toth
    Csaba Toth almost 9 years
    It's mind bogging why strptime doesn't have a directive for ISO format timezone info, and why it cannot be parsed. Incredible.
  • Mark Amery
    Mark Amery almost 9 years
    @CsabaToth Entirely agreed - if I have some time to kill, perhaps I'll try to add it into the language. Or you could do so, if you were so inclined - I see you have some C experience, unlike me.
  • Benjamin Riggs
    Benjamin Riggs over 8 years
    In theory, yes, this fails. In practice, I've never encountered an ISO 8601-formatted date that wasn't in Zulu time. For my very-occasional need, this works great and isn't reliant on some external library.
  • jfs
    jfs over 8 years
    you could use timezone.utc instead of timezone(timedelta(0)). Also, the code works in Python 2.6+ (at least) if you supply utc tzinfo object
  • boxed
    boxed over 8 years
    Sadly that lib called "iso8601" on pypi is trivially incomplete. It clearly states it doesn't handle dates based on week numbers just to pick one example.
  • Peter M. - stands for Monica
    Peter M. - stands for Monica over 8 years
    @CsabaToth - Why incredible? It works good enough for most people, or they found easy enough workaround. If you need the feature, it is opensource and you can add it. Or pay someone to do it for you. Why someone should volunteer his own free time to solve your specific problems? Let source be with you.
  • ashim888
    ashim888 over 8 years
    in my case %f caught microseconds rather than Z, datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') so this did the trick
  • GajananB
    GajananB about 8 years
    Agreed. An example is passing a "date" of 9999. This will return the same as datetime(9999, current month, current day). Not a valid date in my view.
  • Stefan
    Stefan almost 8 years
    @Tobia: iso8601 seems to be getting updates again.
  • Joris
    Joris over 7 years
    As this basically means you can't reliably parse ISO 8601 dates using pure python (definitely not across python 2 and 3), I ended up using the popular arrow library instead: arrow.readthedocs.io/en/latest
  • cnk
    cnk about 7 years
    @MarkAmery thank you so much for this explanation. Saved me a ton of time looking for something that isn't in Python.
  • Jacktose
    Jacktose about 7 years
    Thank you. I thought I was crazy because I couldn't figure this out. I've ended up using %z, I just run my string through s = s[:-3] + s[-2:] first to remove the colon.
  • leo
    leo over 6 years
    Isn't this exactly @Flimms answer above?
  • Blairg23
    Blairg23 over 6 years
    Where do you see him parsing in seconds? I found this article by trying to get epoch time so I figured someone else would be as well.
  • Robino
    Robino over 6 years
    Does Py3K mean Python 3000?!?
  • Robino
    Robino over 6 years
    @PeterMasiar Incredible because usually one discovers that things in python have been implemented thoughtfully and fully. We have been spoilt by this attention to detail and so when we stumble across something in the language that is "unpythonic" we throw our toys out the pram, as I am about to do so right now. Whaaaaaaaaaa Whaa wahaaaaa :-(
  • Robino
    Robino over 6 years
    Fails if no ms or tz.
  • Elliot
    Elliot over 6 years
    This not UTC on my system. Rather, the output in seconds is the unix epoch time as if the date was in my local time zone.
  • bgusach
    bgusach over 6 years
    @ivan_pozdeev what package would you recommend for non-guessing parsing?
  • ivan_pozdeev
    ivan_pozdeev over 6 years
    @bgusach iso8601 as another answer suggests.
  • bgusach
    bgusach over 6 years
    @ivan_pozdeev but that's for iso8601 not rfc3339. Although the question is kind of confusing, seems to treat both as the same. I though we were talking only about the rfc3339
  • ivan_pozdeev
    ivan_pozdeev over 6 years
    @bgusach RFC 3339, right in the abstract: "This document defines a date and time format for use in Internet protocols that is a profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar."
  • bgusach
    bgusach over 6 years
    @ivan_pozdeev I stand corrected then, thanks. I took a look at the doc, but did not understand that a profile of the ISO 8601 means a strict subset of ISO 8601 (I'm not a native speaker). BTW, there seems to be an minor incompatibility between the both with the TZ -00:00, but I don't think that can cause any trouble in my case.
  • hamx0r
    hamx0r almost 6 years
    This looks like a great library! For those wanting to optimize ISO8601 parsing on Google App Engine, sadly, we can't use it since it's a C library, but your benchmarks were insightful to show that native datetime.strptime() is the next fastest solution. Thanks for putting all that info together!
  • movermeyer
    movermeyer almost 6 years
    @hamx0r, be aware that datetime.strptime() is not a full ISO 8601 parsing library. If you are on Python 3.7, you can use the datetime.fromisoformat() method, which is a little more flexible. You might be interested in this more complete list of parsers which should be merged into the ciso8601 README soon.
  • Hendy Irawan
    Hendy Irawan almost 6 years
    That's weird. Because a datetime may contain a tzinfo, and thus output a timezone, but datetime.fromisoformat() doesn't parse the tzinfo ? seems like a bug ..
  • d_-
    d_- almost 6 years
    ciso8601 works quite nice, but one have to first do "pip install pytz", because one cannot parse a timestamp with time zone information without the pytz dependency. Example would look like: dob = ciso8601.parse_datetime(result['dob']['date'])
  • movermeyer
    movermeyer almost 6 years
    @Dirk, only in Python 2. But even that should be removed in the next release.
  • Flimm
    Flimm almost 6 years
    Don't miss that note in the documentation, this doesn't accept all valid ISO 8601 strings, only ones generated by isoformat. It doesn't accept the example in the question "2008-09-03T20:56:35.450686Z" because of the trailing Z, but it does accept "2008-09-03T20:56:35.450686".
  • Samuel Liew
    Samuel Liew almost 6 years
  • Throw Away Account
    Throw Away Account over 5 years
    In Python 3, the parser always uses the tzlocal time zone, regardless of Z appearing at the end of the time string, on systems that are configured to use UTC as their default time zone. Numeric offsets produce a tzoffset tzinfo object.
  • Throw Away Account
    Throw Away Account over 5 years
    @Robino IIRC, "Python 3000" is an old name for what is now known as Python 3.
  • SergiyKolesnikov
    SergiyKolesnikov over 5 years
    strptime() in Python 3.7 now supports everything described as impossible in this answer ('Z' literal and ':' in the timezone offset). Unfortunately, there is another corner case that makes RFC 3339 fundamentally incompatible with ISO 8601, namely, the former allows a negative null timezone offset -00:00 and the later not.
  • tripleee
    tripleee over 5 years
    This answer is buggy and should not be accepted. Probably the whole question should be marked as a duplicate of stackoverflow.com/questions/11743019/…
  • Blairg23
    Blairg23 over 5 years
    @tripleee Actually I just checked the code and it does appear to return the correct answer: 455051100 (checked at epochconverter.com),,, unless I'm missing something?
  • Blairg23
    Blairg23 over 5 years
    That being said, datetime.datetime.timestamp() is probably a better solution.
  • tripleee
    tripleee over 5 years
    @Blairg Like the duplicate explains, this depends on your C library, and of course on your time zone. I for one can reliably repro; I get the wrong answer on MacOS with sprintf("%s"). Code which doesn't work everywhere for everyone is, by definition, buggy.
  • jox
    jox over 5 years
    To properly support the Z the input script can be modified with date_string.replace("Z", "+00:00").
  • djvg
    djvg over 5 years
    Django's DateTimeField uses this when you set a string value.
  • Martijn Pieters
    Martijn Pieters over 5 years
    But in 3.7, you also have datetime.fromisoformat() which handles strings like your input automatically: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:0‌​0').
  • wchargin
    wchargin over 5 years
    Thanks. This is disgusting. I love it.
  • danizen
    danizen over 5 years
    Just use python-dateutil - arrow requires python-dateutil.
  • theannouncer
    theannouncer over 5 years
    Doesn't matter if you've encountered it, it doesn't match the spec.
  • gitaarik
    gitaarik over 5 years
    For a shorter way to write it down you can do: from dateutil.parser import parse as parsedate and then use parsedate() instead of dateutil.parser.parse()
  • sventechie
    sventechie over 5 years
    You can use the %Z for timezone in the most recent versions of Python.
  • Felk
    Felk about 5 years
    Note that for seconds it only handles either exactly 0, 3 or 6 decimal places. If the input data has 1, 2, 4, 5, 7 or more decimal places, parsing will fail!
  • Prahlad Yeri
    Prahlad Yeri about 5 years
    May I ask why did you do frozenset(('+', '-'))? Shouldn't a normal tuple like ('+', '-') be able to accomplish the same thing?
  • A T
    A T about 5 years
    Sure, but isn't that a linear scan rather than a perfectly hashed lookup?
  • Andreas Profous
    Andreas Profous about 5 years
    Good point. I agree, I recommend to use datetime.fromisoformat() and datetime.isoformat()
  • JDOaktown
    JDOaktown almost 5 years
    Felk: It doesn't fail for me. import dateutil.parser as dp #Felk: this string has 7 decimal places x:str = '2019-08-19T17:56:37.5820007Z' dp.parse(x) Out[4]: datetime.datetime(2019, 8, 19, 17, 56, 37, 582000, tzinfo=tzutc())
  • Taku
    Taku almost 5 years
    @JDOaktown This example uses native Python’s datetime library, not dateutil’s parser. It actually will fail if the decimal places are not 0, 3, or 6 with this approach.
  • ingyhere
    ingyhere over 4 years
    I'm running Python 3.7.3 and I get AttributeError: type object 'datetime.time' has no attribute 'fromisoformat' when I run it in a framework I'm using.
  • theEpsilon
    theEpsilon over 4 years
    @ivan_pozdeev there's an update to the module that reads iso8601 dates: dateutil.readthedocs.io/en/stable/…
  • Havok
    Havok over 4 years
    What an incredible, awesome, beautiful hack! Thanks!
  • Alec Gerona
    Alec Gerona over 4 years
    Arrow now supports ISO8601. The issues referenced are now closed.
  • Kiran
    Kiran almost 4 years
    fromisoformat is located at datetime.datetime.fromisoformat() - I know it's confusing!
  • user1596707
    user1596707 almost 4 years
    As noted, this method will only successfully parse the output of isoformat, and is not fully ISO-8601 compliant, but very few languages are fully compliant given how large and arcane that standard is. Yes Java will accept timezones and date offsets, but anything further than that will fall over as well
  • Danielo515
    Danielo515 over 3 years
    This is the only answer that actually meets the question criteria. If you have to use strptime this is the correct answer
  • ᐅdevrimbaris
    ᐅdevrimbaris over 3 years
    It is a pity, you have to install a third party library for a very common use of a date format, i mean the notation ending with Z.
  • FObersteiner
    FObersteiner about 3 years
    @mikerodent: the point is that fromisoformat parses +00:00 but not Z to aware datetime with tzinfo being UTC. If your input e.g. ends with Z+00:00, you can just remove the Z before feeding it into fromisoformat. Other UTC offsets like e.g. +05:30 will then be parsed to a static UTC offset (not an actual time zone).
  • Eric
    Eric about 3 years
    You example fails on Python 3.6 with: ValueError: time data '2018-01-31T09:24:31.488670+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z' that's due to %z not matching +00:00. However +0000 matches %z see python doc docs.python.org/3.6/library/…
  • Flimm
    Flimm about 3 years
    @Eric Yes, this answer only works in Python 3.7 or newer.
  • Flimm
    Flimm about 3 years
    I think it will throw an exception sometimes, it is not guaranteed to throw an exception if it can make a best effort at guessing what the datetime is.
  • jtlz2
    jtlz2 almost 3 years
    @jox the .replace obviously fails if and when None is returned.
  • jox
    jox almost 3 years
    @jtlz2 yeah... but this does not change my point, does it? date_string is something you provide, so just make sure it's not None.
  • Wolfgang Kuehn
    Wolfgang Kuehn almost 3 years
    pandas is strongly discouraged for this simple case: It depends on pytz, which violates the python standard, and pd.Timestamp is subtly not a compatible datetime object.
  • Wolfgang Kuehn
    Wolfgang Kuehn almost 3 years
    Welcome to the Bad and the Ugly section.
  • Wolfgang Kuehn
    Wolfgang Kuehn almost 3 years
    90% dead code, 10% buggy: do not use!
  • Wolfgang Kuehn
    Wolfgang Kuehn almost 3 years
    Error hiding is in top three of anti-patterns: Don't.
  • Michael Dorner
    Michael Dorner almost 3 years
    Thanks for your comment. Do you have some pointers for me? I was not able to find pytz: github.com/pandas-dev/pandas/blob/… and I’m not sure what Python standard and its violation you are referring to.
  • Wolfgang Kuehn
    Wolfgang Kuehn almost 3 years
    See the rant by Paul Ganssle. As for incompatibility, execute both datetime.fromisoformat('2021-01-01T00:00:00+01:00').tzinfo.u‌​tc and pandas.Timestamp('2021-01-01T00:00:00+01:00').tzinfo.utc : Not the same at all.
  • Michael Dorner
    Michael Dorner almost 3 years
    Thank you for pointers to this ongoing work. I didn’t know about that issue, but I really hope they fix it soon! But again: I can’t believe that time parsing is still an issue. :-)
  • azro
    azro over 2 years
    Why not use the %f I don't get it ? I just saw this post because of it was used as a duplicate on stackoverflow.com/questions/69953076/… but that seems not easy regarding juts use "%Y-%m-%dT%H:%M:%S.%fZ"
  • Mike
    Mike over 2 years
    Wild that in the python-docker library the objects attribute 'Created' timestamp where microseconds is expected with 6 digits, it has 9, removed the 3 to get this to work.
  • kevinarpe
    kevinarpe over 2 years
    @jox: Do you mean "+0000" instead of "+00:00"? I am looking at the docs for datetime.strptime() and %z here: docs.python.org/3/library/…
  • jox
    jox over 2 years
    @kevinarpe no, datetime.fromisoformat seems to expect another format. I just tested both versions and while it works fine with +00:00, I get "ValueError: Invalid isoformat string" with +0000.
  • kevinarpe
    kevinarpe over 2 years
    @jox Great feedback. So datetime.fromisoformat is even more insane that I thought! How can Python be such a great language and ecosystem, but have such horrible date/time handling? My Python date/time code is usually littered with "gotcha" comments and links to SO.com answers / comments!
  • BitLauncher
    BitLauncher over 2 years
    Not all formats of RFC3339 work with this code sample, only if the second fraction part has 6 digits! So the first example on page 9 section 5.8 of RFC 3339 version July 2002 would not work: 1985-04-12T23:20:50.52Z --> false: 1985-04-12T23:20:50.**0000**52 I mention this, because the question seems related to RFC3339 and only provides an 6 digit second fraction number as a 'like' example not telling that all date times contain always 6 digits, or always trailing zeros in the second fraction part (...59.999000Z or ...59.999Z ?).