Repository navigation
datetime.strptime without a year fails on Feb 29 #70647
Copy link
Copy link
Closed
Labels
docsDocumentation in the Doc dirDocumentation in the Doc dirstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Description
| BPO | 26460 |
|---|---|
| Nosy | @gpshead, @abalkin, @pganssle, @tirkarthi, @nickzoic |
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:
assignee = None
closed_at = None
created_at = <Date 2016-02-29.18:02:35.000>
labels = ['3.7', '3.8', 'type-feature', 'library']
title = 'datetime.strptime without a year fails on Feb 29'
updated_at = <Date 2020-03-03.17:16:20.440>
user = 'https://bugs.python.org/SriramRajagopalan'bugs.python.org fields:
activity = <Date 2020-03-03.17:16:20.440>
actor = 'nickzoic'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2016-02-29.18:02:35.000>
creator = 'Sriram Rajagopalan'
dependencies = []
files = []
hgrepos = []
issue_num = 26460
keywords = []
message_count = 13.0
messages = ['261014', '261024', '261027', '261028', '261033', '343085', '363123', '363202', '363215', '363217', '363223', '363257', '363280']
nosy_count = 8.0
nosy_names = ['gregory.p.smith', 'belopolsky', 'polymorphm', 'Sriram Rajagopalan', 'p-ganssle', 'xtreak', 'gerardw@alum.mit.edu', 'nickzoic']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue26460'
versions = ['Python 3.6', 'Python 3.7', 'Python 3.8']remaining TODO list:
- turn
%einto an error in 3.17.
Linked PRs
- gh-70647: Better promote how to safely parse yearless dates in datetime. #116179
- gh-70647: Add detail to error message when parsing a bad day of month. #116184
- GH-70647: Deprecate strptime day of month parsing without a year present to avoid leap-year bugs #117107
- gh-70647: Raise a more informative error for when date is out of range #131335
- [3.12] gh-70647: update docs to mention the datetime 1900 year default 2/29 issue #131534
- [3.14] gh-70647: Better promote how to safely parse yearless dates in datetime. (GH-116179) #143037
- [3.13] gh-70647: Better promote how to safely parse yearless dates in datetime. (GH-116179) #143038
- GH-70647: Remove support for
%d(and deprecate for%e) without year instrptime()#144570
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
docsDocumentation in the Doc dirDocumentation in the Doc dirstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Projects
- StatusShow more project fieldsDone
- StatusShow more project fieldsTodo
Activity
SriramRajagopalan commentedon Feb 29, 2016
$ python Python 3.5.1 (default, Dec 7 2015, 12:58:09) [GCC 5.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> >>> >>> >>> import time >>> >>> time.strptime("Feb 29", "%b %d") time.struct_time(tm_year=1900, tm_mon=2, tm_mday=29, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=60, tm_isdst=-1) >>> >>> >>> import datetime >>> >>> datetime.datetime.strptime("Feb 29", "%b %d") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.5/_strptime.py", line 511, in _strptime_datetime return cls(*args) ValueError: day is out of range for monthThe same issue is seen in all versions of Python
gpshead commentedon Feb 29, 2016
Python's time.strptime() behavior is consistent with that of glibc 2.19:
======= strptime_c.c =======
=======
I'm not saying that the behavior is a good API, but given the unfortunate API at hand, parsing a date without specifying what year it is using strptime is a bad idea.
abalkin commentedon Feb 29, 2016
This is not no more bug than
Naturally, as long as datetime.strptime('0228', '%m%d') is the same as datetime.strptime('19000228', '%Y%m%d'), datetime.strptime('0229', '%m%d') should raise a ValueError as long as datetime.strptime('19000229', '%Y%m%d') does.
The only improvement, I can think of in this situation is to point the user to time.strptime() in the error message. The time.strptime method works just fine in the recent versions (see bpo-14157.)
abalkin commentedon Feb 29, 2016
Gregory,
I believe OP is complaining about the way datetime.datetime.strptime() behaves, not time.strptime() which is mentioned as (preferred?) alternative.
See msg261015 in bpo-14157 for context.
gpshead commentedon Feb 29, 2016
time.strptime() is "working" (not raising an exception) as it appears not to validate the day of the month when a year is not specified, yet the return value from either of these APIs is a date which has no concept of an ambiguous year.
So what should the validation behavior be?
datetime objects cannot be constructed with the invalid date (as the time.strptime return value allows).
Changing the API to assume the current year or a +/- 6 months from "now" when no year is parsed is likely to break existing code.
tirkarthi commentedon May 21, 2019
See also bpo-19376. This behavior is now documented with 56027cc
nickzoic commentedon Mar 2, 2020
I suspect this is going to come up about this time of every leap year :-/
The workaround is prepending "%Y " to the pattern and eg: "2020 " to the date string, but that's not very nice.
Would adding a kwarg "default_year" be an acceptable solution?
I can't think of any other situation other than leap years when this is going to come up. If both "default_year" and "%Y" are present throw an exception (maybe therefore just call the kwarg "year")
In the weird case where you want to do date maths involving the month as well, you can always use a safe choice like "default_year=2020" and then fix the year up afterwards:
pganssle commentedon Mar 2, 2020
I don't think adding a default_year parameter is the right solution here.
The actual problem is that
time.strptime, and by extensiondatetime.strptimehas a strange and confusing interface. What should happen is either thatyearis set to None or some other marker of a missing value or datetime.strptime should raise an exception when it's being asked to construct something that does not contain a year.Since there is no concept of a partial datetime, I think our best option would be to throw an exception, except that this has been baked into the library for ages and would start to throw exceptions even when the person has correctly handled the Feb 29th case.
I think one possible "solution" to this would be to raise a warning any time someone tries to use
datetime.strptimewithout requesting a year to warn them that the thing they're doing only exists for backwards compatibility reasons. We could possibly eventually make that an exception, but I'm not sure it's totally worth a break in backwards compatibility when a warning should put people on notice.nickzoic commentedon Mar 2, 2020
Not disagreeing with you that "%b %d" timestamps with no "%Y" are excerable, but they're fairly common in the *nix world unfortunately.
People need to parse them, and the simple and obvious way to do this breaks every four years.
I like the idea of having a warning for not including %Y *and* not setting a default_year kwarg though.
47 remaining items
StanFromIreland commentedon Feb 6, 2026
I was writing this up today, however, first we should decide when exactly we are going to do whatever it is we are going to do (i.e. always raise or change the default year).
The initial deprecation was merged into 3.13, so 3.15 (which is noted in the docs and error message) is too early to do anything.
%e's addition was backported to 3.13.5 (ea25f4a) but only documented in 3.15, so I propose we schedule the change to both in 3.18. This way%d's deprecation meets the PEP 387 guideline, and%egets three years, but since it was never documented, I think it is unlikely someone is using it in such a way.GH-70647: Remove support for `%d` (and deprecate for `%e`) without ye…
gpshead commentedon Apr 15, 2026
remaining todo: in 3.17 turn %e usage without a year into an error.
StanFromIreland commentedon Apr 15, 2026
Let's make that a new issue, we won't be needing it for a while, as such, closing.
pythonGH-70647: Remove support for `%d` (and deprecate for `%e`) with…
Fix Python 3.15 compatibility: strptime no longer allows %d without %Y (
fix(sql): Implement workaround for Python datetime change
fix(sql): Implement workaround for Python datetime change
fix(sql): Implement workaround for Python datetime change
`plot`: make sure that parsing timestamps keeps working for python >=…
Keep settings history timestamps on 29 February