-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
isoformat() / fromisoformat() for datetime.timedelta #86260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Python 3.7 gained support for parsing ISO 8601 formatted time, date and datetime strings via the fromisoformat() methods. Python has seen improved support for ISO 8601 in general; ISO calendar format codes were added in Python 3.6, and fromisocalendar() was added in Python 3.8. ISO 8601 also has a standard for durations: https://en.wikipedia.org/wiki/ISO_8601#Durations For consistency with the other objects in the datetime module, I suggest adding isoformat()/fromisoformat() methods for datetime.timedelta that implement ISO 8601 durations. ISO 8601 durations support years and months that are not valid timedelta arguments because they are non-precise durations. I suggest throwing an exception if the conversion to or from timedelta cannot be done safely. https://pypi.org/project/isodate/ implements a parse_duration() method that could be used for inspiration. |
Among other things, ISO 8601 duration strings are commonly used to communicate offset values in timezone definitions. |
There is related discussion in bpo-41254, about duration formats more generally. |
This is probably more feasible than the proposal in bpo-41254 since it's a well-defined spec (mostly — it includes an optional alternative format and the number of digits allowed is defined "by agreement", thus defeating the purpose of using a spec in the first place) that's not even particularly difficult to implement, but there are still a few problems (and one reason I've never implemented this, despite desperately wanting a better string representation for time deltas). Two minor problems first:
The biggest problem, however, is that A better target for parsing ISO 8601 durations would be something like I am also not entirely clear on whether "weeks" is just an alias for "7 days" or if it means something related to weeks in the ISO calendar (and if that makes a difference for durations). I imagine that generating these formats is a bit more forgiving, because you would simply never generate the forbidden formats, and we can offer configuration options in the formatter method to allow the user to tweak the various ambiguities in the spec. |
There are two conflicting interests: ISO 8601 that allows non-precise durations, and timedelta that assumes precise durations. For me, the non-precise durations only make sense in date arithmetic - to a human, it's pretty clear what adding 3 months or a year will do to the date. There may be edge cases when crossing DST, but normal arithmetic with timezone also have those cases. Regarding ISO weeks, I'm pretty sure that they are only special in regards to calculating week numbers and the weekday they start. They still have a duration of 7 days. Apart from being able to parse ISO durations coming from other systems, the non-precise durations would be useful e.g. when implementing recurring events. Calculating a series of dates for something that happens on the 12th day of every 2nd month is doable in Python, but not with the aid of timedelta. I see four options here:
|
After learning about this ticket, I've attempted an implementation of It's freshly-prepared and unreviewed so far and I'd welcome any feedback on it. The library provides a subclass of The library has no external dependencies and has been developed with performance in mind, albeit not as the primary goal. Test coverage is included in the source repository.
The library has some limitations, and absence of support for representation of |
Go's Java differentiates between time-durations implemented as .NET uses a similar concept as |
My apologies here: the license terms that this library is currently under may have caused a license violation, and so I plan to yank the PyPi libraries and make the GitHub repository private until questions about those can be resolved. |
For reference in the absence of @jayaddison's code, here is an implementation of I believe strongly that |
Apologies for what might be a slightly repetitive message here, but: given some concerns about the The updated library is available under an AGPLv3 license in source form as |
@benkehoe any chance you could re-run your benchmark comparison against |
As a heads-up for anyone following along (and please speak up if this is noise - I'll adjust and find a better way to communicate): In particular, two important bugs have been addressed since that version:
Additionally: an intentional decision was made to handle all parsing of values-to-numbers using the |
Here's a Python implementation for |
I don't think we need any more implementations. The implementation here was never the problem. The big unaddressed issues are about who wants this thing and why. If people want a human-friendly way to print |
There definitively is! To parse delays, timeouts, lifetimes. The project https://github.com/caddyserver/caddy (not Python) has 35 usages of datetime.timedelta(days=int(match_days.group("days")))
datetime.timedelta(hours=int(match_hours.group("hours")))
datetime.timedelta(seconds=ini.getint(section, "min_mod_diff"))
datetime.timedelta(days=float(ini.get(section, "days")))
datetime.timedelta(hours=float(ini.get(section, "hours")))
datetime.timedelta(days=int(ini.get(section, "DAYS")))
datetime.timedelta(seconds=ini.getint(section, "MaxAgeSeconds"))
datetime.timedelta(seconds=ini.getint(section, "MinAgeSeconds"))
datetime.timedelta(days=int(match_days.group("days")))
datetime.timedelta(hours=int(match_hours.group("hours")))
datetime.timedelta(seconds=ini.getint(section, "min_mod_diff"))
datetime.timedelta(days=float(ini.get(section, "days")))
datetime.timedelta(hours=float(ini.get(section, "hours")))
datetime.timedelta(days=int(ini.get(section, "DAYS")))
datetime.timedelta(seconds=ini.getint(section, "MaxAgeSeconds"))
datetime.timedelta(seconds=ini.getint(section, "MinAgeSeconds")) Thanks! |
Why do these need to be ISO 8601 durations rather than some other, better format? |
ISO 8601 is not a strict necessity here, but a handy standard that can be used. Also for symmetry with Is Go's syntax is preferrable?
|
Note, some other notable libraries that try to do this such as Pandas. See https://pandas.pydata.org/docs/reference/api/pandas.Timedelta.isoformat.html. I've also used isodate quite extensively in lieu of This would be a welcome addition to the standard library. |
@pganssle I think the number of libraries trying to accomplish the same thing (albeit with different trade-offs) is a signal to the desire. From my experience I usually see a mix of these libraries being used to parse durations in business/data-science applications (e.g. ML generated input/output or more recent LLM input/output). For example, LLM applications can be better at generating these type of durations from natural language input versus full datetimes, hence it's usefulness. |
if there are other standards besides ISO 8601 for duration formats, could someone point to them? there is a real need to be able to parse a standard duration format, and if ISO 8601 is not it, then i'd be grateful for a pointer to some alternative standard. I've seen a lot of comments like "we all know what I'd even be happy with a deliberately restrictive standard that only parses days, hours, minutes, and seconds and rejects units larger than that. (assuming that we don't care about leap seconds, so days are uniformly 86400 seconds long). but i don't know where that standard is. |
The P prefix and lack of spacing tends to puts me off ISO 8601. In general I prefer the other HTML 5 “duration string” https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#durations as a readable standard format, e.g. 1w 0d 12h 0m 27.001s. It has weeks, days, hours, minutes and seconds components, with fractional seconds using a decimal point, down to 0.001 s resolution. It allows spaces between components and before units. It does not do negative durations, nor fractional minutes or higher. For negative durations, I agree −1h 2m would be ambiguous (especially with the space included). I’d consider putting it in brackets: −(1h 2m). Or relying on ISO 8601; I don’t think −PT1h2m is so ambiguous. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: