To be clear, it is certainly possible to maintain the “fromisoformat
is only the inverse of isoformat
” without making isoformat
emit Z
instead of +00:00
by default since isoformat()
could always grow the capability to optionally emit Z
in place of +00:00
with a feature flag, in which case fromisoformat()
would be required to implement Z
parsing. I wouldn’t want to add a feature flag to isoformat()
just to maintain an arbitrary contract, though, so I would only consider this option if there’s strong demand for emitting Z
in isoformat
- and I have not seen any issues on BPO requesting this, so it’s probably not that important to people.
That said, in some ways it would violate the spirit of the contract, which is that, as of right now, fromisoformat
is intended to be used only on the output of .isoformat
, which means that all the people who want it to parse Z
are in a sense using it in an unsupported way. Modifying it to start accepting these would probably lead to more people hitting bugs in production when parsing a valid ISO 8601 string generated by something other than datetime.datetime.isoformat
that happens to be in an unsupported format.
To be clear, currently the idea is that you should parse this name as “from isoformat
” rather than “from ISO format”, meaning that it constructs a datetime from the output of fromisoformat
. The same goes for datetime.fromisocalendar
and datetime.fromtimestamp
.
I strongly disagree with this idea, for the same reasons that @jdemeyer identifies.
This is not true, I just do not want to half-change the contract. If you look at the original issue in which I added fromisoformat
, the intention was always that we would start with “reverses isoformat
” because it is well-scoped and incredibly easy to explain what it does (it parses the result of isoformat()
). I think it would be acceptable for it to eventually grow something like a full ISO 8601 parser, but there are many UI challenges and decisions to be made there.
There is no requirement that strings can be round-tripped from str
→ datetime
→ str
, the only guarantee is in the other direction, so dt == datetime.fromisoformat(dt.isoformat())
must be true. We are free to expand what fromisoformat()
parses and the main reason we have chosen not to is that it’s much more complicated to get it right.
I strongly disagree with this sentiment in most library code, as it tends to take clear specifications and make them very fuzzy and implementation-defined. In this case failing loudly on common mistakes that we can still interpret as a datetime is an early warning that you are using the function in an unsupported way. As of today, if you are not parsing the output of a dt.isoformat()
call or a string guaranteed to be in an equivalent format, you should not be using datetime.fromisoformat
, and if it works it only does so by accident.
My goals for a “general-purpose” ISO 8601 parser:
- It should support the entire
datetime
portion of the spec (or as nearly so as we can) - It should have a way to specify which deviations from the spec are not allowed (e.g. no sub-minute offsets)
- It should be possible to specify that you want to support certain subsets of all supported functionality (e.g. RFC 3339, which is in some ways a subset and a superset of ISO 8601).
- It should support a minimum of deviations from ISO 8601 - essentially those that are specified to be changeable “by agreement” plus support for sub-minute time zone offsets.
- It should continue to “just work” on the output of
datetime.isoformat
.
We will also need to decide what to do with the --MMDD
and --MM-DD
formats, since they represent a concept that cannot be represented with datetime.datetime
. I believe the options are “don’t support at all”, “fill in the missing year from the current year” and “allow the user to specify the default value for the year”. The last two can also be combined (e.g. use current year by default but allow users to override it). I suspect if we didn’t support it no one would care, since most of the people who even know it exists are people who have tried implementing the spec.