Repository navigation
datetime.time.strftime('%Y') raises AttributeError in the pure-Python implementation #152305
Description
Bug report
Bug description
In the pure-Python datetime implementation (Lib/_pydatetime.py),
datetime.time.strftime() raises AttributeError for the directives %Y, %G,
%C and %F, while the C accelerator returns the correct values. A time has no
date, so strftime fills the 1900-01-01 placeholder, and these directives should
format that placeholder.
>>> import _pydatetime # pure implementation
>>> _pydatetime.time(12, 30).strftime('%Y')
Traceback (most recent call last):
...
File ".../Lib/_pydatetime.py", line 276, in _wrap_strftime
object.year < 1000 and _need_normalize_century()):
AttributeError: 'time' object has no attribute 'year'The C accelerator is correct:
>>> import datetime # C accelerator
>>> datetime.time(12, 30).strftime('%Y')
'1900'
>>> datetime.time(12, 30).strftime('%C')
'19'
>>> datetime.time(12, 30).strftime('%F')
'1900-01-01'
>>> datetime.time(12, 30).strftime('%G')
'1900'Date-only directives such as %j already agree between the two implementations
(both give '001').
Root cause
_wrap_strftime(object, format, timetuple) reads object.year in the
year-normalization branch:
elif ((ch in 'YG' or ch in 'FC') and
object.year < 1000 and _need_normalize_century()):
...
year = object.yeartime.strftime passes the time object as object (it has no .year) together
with the fixed placeholder timetuple whose year is 1900. Because
object.year < 1000 is evaluated first in the and chain (before
_need_normalize_century()), the AttributeError is raised on every platform,
including ones where _need_normalize_century() is False.
The year actually being formatted is timetuple[0], which equals 1900 for a
time and equals object.year for date/datetime. Using timetuple[0] fixes
time and leaves date/datetime unchanged.
Regression
Introduced by gh-120713 (the %Y/%G normalization branch) and widened to %F
and %C by gh-122272.
Affected versions
3.13+ (pure-Python implementation). The C accelerator is not affected.
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
eendebakpt commentedon Jun 26, 2026
I can confirm the issue on 3.14+ (3.13 is not affected I think). The PR just opened seems like a good solution.
StanFromIreland commentedon Jun 26, 2026
@eendebakpt the backport labels work only on PRs. I'll review this tomorrow.
StanFromIreland commentedon Jun 27, 2026
Bisects to 6d34938.
gh-152305: Fix `_pydatetime.time.strftime()` raising on year directiv…
StanFromIreland commentedon Jun 27, 2026
Merged, thanks.
[3.14] gh-152305: Fix `_pydatetime.time.strftime()` raising on year d…
[3.15] gh-152305: Fix `_pydatetime.time.strftime()` raising on year d…