bpo-15873: Implement [date][time].fromisoformat#4699
Conversation
2eb060d to
8f7f1b1
Compare
December 4, 2017 17:44
|
One other thing I'll note - the pure python implementation here is at least partially optimized for speed. There is a fairly trivial implementation that looks, more or less, like this: from datetime import datetime
_base_strptimes = {
10: '%Y-%m-%d',
13: '%Y-%m-%dT%H',
16: '%Y-%m-%dT%H:%M',
19: '%Y-%m-%dT%H:%M:%S',
23: '%Y-%m-%dT%H:%M:%S.%f',
26: '%Y-%m-%dT%H:%M:%S.%f'
}
def from_strptime(dtstr):
if not isinstance(dtstr, str):
raise TypeError('isoformat takes str')
if len(dtstr) >= 19 and dtstr[-6] in '+-':
fmt = _base_strptimes[len(dtstr) - 6] + '%z'
else:
fmt = _base_strptimes[len(dtstr)]
return datetime.strptime(dtstr, fmt)This will work, but |
Sorry, something went wrong.
8f7f1b1 to
a957534
Compare
December 4, 2017 17:50
a957534 to
d7657a6
Compare
December 4, 2017 18:08
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
Sorry, something went wrong.
39a65c3 to
df3c032
Compare
December 5, 2017 01:53
|
@abalkin @vadmium I think I've fixed all the concerns raised so far - some good catches in there. There are three documentation-related concerns:
|
Sorry, something went wrong.
df3c032 to
2a83701
Compare
December 5, 2017 11:40
|
To fully support isoformat output you also need to parse seconds on the timezone:
|
Sorry, something went wrong.
|
@mariocj89 Thanks for the catch. The PR is updated with tests and implementation. Didn't realize that second precision was allowed in |
Sorry, something went wrong.
9e37ae8 to
fb32d7f
Compare
December 7, 2017 17:35
Moreover, the sub-second precision is now allowed as well: >>> from datetime import *
>>> import random
>>> datetime.now(timezone(timedelta(hours=24*random.random()))).isoformat()
'2017-12-08T11:34:30.238049+15:47:34.297540'The good news is that you should be able to reuse the time fields parser logic to parse the timezone. cc: @pganssle |
Sorry, something went wrong.
|
@abalkin Unless I'm doing something wrong, it seems that the pure python implementation of import sys
sys.modules['_datetime'] = None # cause ImportError
from datetime import *
print(time(12, 30, 15, tzinfo=timezone(timedelta(microseconds=123456))).isoformat())
# 12:30:15+00:00:00
print(datetime(2017, 1, 1, 12, 30, 15,
tzinfo=timezone(timedelta(microseconds=123456))).isoformat())
# Traceback (most recent call last):
# File "<stdin>", line 2, in <module>
# File ".../cpython/Lib/datetime.py", line 1832, in isoformat
# assert not ss.microseconds
# AssertionErrorI suppose I'll fix that as part of this PR. |
Sorry, something went wrong.
|
Latest changes fix both |
Sorry, something went wrong.
43f7ff5 to
3da73a3
Compare
December 10, 2017 11:09
@pganssle - It looks like you are right. Please mention bpo-5288 in the commit message when you fix this. Please also find out why this was not caught by the test suit. We should be running all tests with and without C acceleration. |
Sorry, something went wrong.
3da73a3 to
5a233fb
Compare
December 13, 2017 02:20
|
@abalkin It's already fixed and tests added. I didn't see any tests actually testing subsecond support for timezones in I just pushed a rewritten history that mentions bpo-5288 in the commit fixing the pure python version. |
Sorry, something went wrong.
|
@pganssle - great job! This PR looks good to me now and I will merge it in a few days to give others a chance to review. |
Sorry, something went wrong.
|
@vadmium Thanks for all the comments. I've added a test for the ambiguous cases you identified and updated the documentation (plus dropped the unused |
Sorry, something went wrong.
|
@abalkin @vadmium Any thoughts on the question of whether to add a The only reason to do so would be if in a future release we want to support ISO-8601 style strings that are just dates with offsets attached (e.g. Having given this a bit of thought, I think it's probably a good idea to leave out If we assume that some fraction of people will want to be strict and some people will want to be lenient, no matter which one we choose, the other behavior can easily be emulated. If we choose lenient by default, strict users can do the equivalent with: def strict_fromisoformat(dtstr, sep='T'):
if dtstr[10:11] != sep:
raise ValueError('Invalid isoformat string: %s' % dstr)
return datetime.fromisoformat(dtstr)If we choose strict by default, the "lenient by default" option is: def lenient_fromisoformat(dtstr):
# Assuming sep requires a single character be passed, for dates it can be anything
return datetime.fromisoformat(dtstr, sep=dtstr[10:11] or 'T')Anyway, this is a kind of long comment just to say, "I think we should keep the status quo", but I thought I'd at least outline my thinking. |
Sorry, something went wrong.
-1 or adding a parameter, but I would not mind having fromisoformat() not checking the separator at all. |
Sorry, something went wrong.
|
@abalkin The current behavior is to allow any single character for the separator at all, if that's what you mean. |
Sorry, something went wrong.
Yes. That's what I want. |
Sorry, something went wrong.
Per discussion on the python-dev mailing list, this is a C implementation of
fromisoformatas alternate constructors fordatetime,dateandtime, resolving this issue.If it is deemed desirable, it can be extended later to cover all
ISO-8601datetime strings, but I believe the consensus so far is to have a minimum implementation that only covers the outputs ofdatetime.isoformat().One thing I'd like to call attention to is that my profiling seems to indicate that in the common case (i.e. not calling this from a subclass), using the C API directly is much faster than going through
PyObject_CallFunction.Here is a profiling script:
Result on my laptop:
If there's no particularly pressing reason why all the other alternate constructors universally go through the main constructor call, I could write a small function or macro that would take the fast path if available and use it for all the alternate constructors.
CC @abalkin @mariocj89
https://bugs.python.org/issue15873