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.year
time.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
Bug report
Bug description
In the pure-Python
datetimeimplementation (Lib/_pydatetime.py),datetime.time.strftime()raisesAttributeErrorfor the directives%Y,%G,%Cand%F, while the C accelerator returns the correct values. Atimehas nodate, so
strftimefills the 1900-01-01 placeholder, and these directives shouldformat that placeholder.
The C accelerator is correct:
Date-only directives such as
%jalready agree between the two implementations(both give
'001').Root cause
_wrap_strftime(object, format, timetuple)readsobject.yearin theyear-normalization branch:
time.strftimepasses thetimeobject asobject(it has no.year) togetherwith the fixed placeholder
timetuplewhose year is1900. Becauseobject.year < 1000is evaluated first in theandchain (before_need_normalize_century()), theAttributeErroris raised on every platform,including ones where
_need_normalize_century()isFalse.The year actually being formatted is
timetuple[0], which equals1900for atimeand equalsobject.yearfordate/datetime. Usingtimetuple[0]fixestimeand leavesdate/datetimeunchanged.Regression
Introduced by gh-120713 (the
%Y/%Gnormalization branch) and widened to%Fand
%Cby gh-122272.Affected versions
3.13+ (pure-Python implementation). The C accelerator is not affected.
Linked PRs
_pydatetime.time.strftime()raising on year directives (GH-152306) #152425_pydatetime.time.strftime()raising on year directives (GH-152306) #152426