Skip to content

gh-155175: Reject fractional seconds without a decimal mark in C fromisoformat - #155177

Open
SEPURI-SAI-KRISHNA wants to merge 1 commit into
python:mainfrom
SEPURI-SAI-KRISHNA:fix-isoformat-fraction-separator
Open

gh-155175: Reject fractional seconds without a decimal mark in C fromisoformat#155177
SEPURI-SAI-KRISHNA wants to merge 1 commit into
python:mainfrom
SEPURI-SAI-KRISHNA:fix-isoformat-fraction-separator

Conversation

@SEPURI-SAI-KRISHNA

@SEPURI-SAI-KRISHNA SEPURI-SAI-KRISHNA commented Aug 4, 2026

Copy link
Copy Markdown

parse_hh_mm_ss_ff() in Modules/_datetimemodule.c parsed any characters left
over after HHMMSS in the ISO 8601 basic format as a fractional seconds
component, without requiring the decimal mark that ISO 8601 mandates:

>>> time.fromisoformat('12345678')
datetime.time(12, 34, 56, 780000)          # now raises ValueError
>>> datetime.fromisoformat('2020-01-01T12345678')
datetime.datetime(2020, 1, 1, 12, 34, 56, 780000)   # now raises ValueError

The HH/MM/SS loop only breaks into the fraction-parsing code when it
sees . or ,. In the basic format it can also fall out of the loop normally
with input still unconsumed, the else if (!has_separator) { --p; } branch
un-consumes the character and the loop ends, after which the code below
parses whatever remains as a fraction, never checking that a decimal mark
introduced it.

This tracks whether a decimal mark was actually seen and rejects the string
otherwise, matching _pydatetime._parse_hh_mm_ss_ff(), which performs the
check explicitly:

if pos < len_str:
    if tstr[pos] not in '.,':
        raise ValueError("Invalid microsecond separator")

Because the same helper also parses the UTC offset, this fixes malformed
offsets being accepted with the trailing digits silently discarded:

>>> time.fromisoformat('12:34:56+00000000')
datetime.time(12, 34, 56, tzinfo=datetime.timezone.utc)   # now raises ValueError

Valid inputs are unaffected, '123456', '123456.78', '123456,78',
'123456.123456789', '12:34:56.123456', '1234', '12', '123456+0000',
'123456Z', '12:34:56+00:00:00.123456', '20200101T123456.789' and
'20200101T123456+0530' all parse exactly as before, and identically to the
pure-Python implementation.

This is the same class of C/pure-Python divergence as gh-152157 (empty fraction
before a timezone designator) and gh-152079.

Tests

Five cases added to each of the time and datetime bad_strs lists in
Lib/test/datetimetester.py, covering the fraction and the UTC offset paths.
They run under both the _Fast (C) and _Pure (pure Python) test classes.

test_datetime passes (1160 tests), and the full suite passes
(run=48,806, Result: SUCCESS).

@python-cla-bot

python-cla-bot Bot commented Aug 4, 2026

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

@SEPURI-SAI-KRISHNA

Copy link
Copy Markdown
Author

CIFuzz has no datetime target, Modules/_xxtestfuzz/fuzz_tests.txt lists 11 targets and none reach _datetimemodule.c. All three sanitizer variants failed identically, which points to an OSS-Fuzz infra issue rather than this change.

@aisk aisk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment and the NEWS entry are a bit too verbose IMO, but it's up to you. The failed CI should be unrelated, other PRs failed too. I restarted them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants