1010 * Supported inputs are exact str objects containing only ASCII, with:
1111 * - numeric %Y, %y, %m, %d, %H, %M, %S, and %f fields;
1212 * - ASCII locale month/weekday names and numeric %w/%u weekdays;
13+ * - %I with ASCII %p indicators, and the %k, %l, %P aliases;
1314 * - a terminal %z: empty, Z, or +/-HHMM or +/-HH:MM with hours below 24;
1415 * - literal characters, %%, and ASCII whitespace.
1516 * Duplicate directives, mixed %Y/%y, and day-of-month without a year use the
@@ -44,13 +45,14 @@ strptime_name(const unsigned char *data, Py_ssize_t length, Py_ssize_t pos,
4445 * As in TimeRE, prefer the longest match, then the first equal name.
4546 */
4647 int month = directive == 'b' || directive == 'B' ;
47- int count = month ? 12 : 7 ;
48+ int count = month ? 12 : directive == 'p' ? 2 : 7 ;
4849 char format [] = {'%' , directive , '\0' };
4950 int matched = 0 ;
5051 for (int i = 0 ; i < count ; i ++ ) {
5152 struct tm tm = {.tm_year = 101 , .tm_mday = 1 ,
5253 .tm_mon = month ? i : 0 ,
53- .tm_wday = month ? 0 : (i + 1 ) % 7 };
54+ .tm_wday = month ? 0 : (i + 1 ) % 7 ,
55+ .tm_hour = i == 0 ? 1 : 22 };
5456 char name [256 ];
5557 size_t size = strftime (name , sizeof (name ), format , & tm );
5658 if (size == 0 ) {
@@ -61,12 +63,31 @@ strptime_name(const unsigned char *data, Py_ssize_t length, Py_ssize_t pos,
6163 return 0 ;
6264 }
6365 }
64- if (size <= (size_t )matched || size > (size_t )(length - pos )) {
66+ char * start = name ;
67+ if (directive == 'p' ) {
68+ while (size && Py_ISSPACE (start [size - 1 ])) {
69+ size -- ;
70+ }
71+ while (size && Py_ISSPACE (* start )) {
72+ start ++ ;
73+ size -- ;
74+ }
75+ /* Empty indicators have special defaulting rules in Python. */
76+ if (size == 0 ) {
77+ return 0 ;
78+ }
79+ }
80+ if (data == NULL ) {
81+ continue ;
82+ }
83+ if (size < (size_t )matched ||
84+ (size == (size_t )matched && directive != 'p' ) ||
85+ size > (size_t )(length - pos )) {
6586 continue ;
6687 }
6788 size_t j ;
6889 for (j = 0 ; j < size ; j ++ ) {
69- if (Py_TOLOWER (data [pos + j ]) != Py_TOLOWER (name [j ])) {
90+ if (Py_TOLOWER (data [pos + j ]) != Py_TOLOWER (start [j ])) {
7091 break ;
7192 }
7293 }
@@ -75,7 +96,7 @@ strptime_name(const unsigned char *data, Py_ssize_t length, Py_ssize_t pos,
7596 * value = month ? i + 1 : i ;
7697 }
7798 }
78- return matched ;
99+ return data == NULL ? 1 : matched ;
79100#else
80101 return 0 ;
81102#endif
@@ -134,6 +155,7 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
134155 FRACTION = 1U << 7 ,
135156 OFFSET = 1U << 8 ,
136157 WEEKDAY = 1U << 9 ,
158+ AMPM = 1U << 10 ,
137159 ANY_YEAR = YEAR | SHORT_YEAR ,
138160 };
139161 static const unsigned short directive_bits [128 ] = {
@@ -147,6 +169,7 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
147169 ['f' ] = FRACTION ,
148170 ['z' ] = OFFSET ,
149171 ['w' ] = WEEKDAY ,
172+ ['p' ] = AMPM ,
150173 };
151174 /* String subclasses and non-ASCII input retain Python's behavior. */
152175 if (!PyUnicode_CheckExact (string ) || !PyUnicode_CheckExact (format ) ||
@@ -160,6 +183,8 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
160183 * fields = (_PyTime_StrptimeFields ){.year = 1900 , .month = 1 , .day = 1 ,
161184 .gmtoff = INT_MIN , .weekday = -1 };
162185 unsigned int seen = 0 ;
186+ int hour12 = 0 ;
187+ int ampm = -1 ;
163188 Py_ssize_t pos = 0 ;
164189
165190 for (Py_ssize_t i = 0 ; i < fmt_length ; i ++ ) {
@@ -186,6 +211,15 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
186211 return 0 ;
187212 }
188213 c = fmt [i ];
214+ if (c == 'k' ) {
215+ c = 'H' ;
216+ }
217+ else if (c == 'l' ) {
218+ c = 'I' ;
219+ }
220+ else if (c == 'P' ) {
221+ c = 'p' ;
222+ }
189223 if (c == '%' ) {
190224 if (pos == length || data [pos ++ ] != '%' ) {
191225 return 0 ;
@@ -201,6 +235,9 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
201235 else if (c == 'a' || c == 'A' || c == 'u' ) {
202236 field = 'w' ;
203237 }
238+ else if (c == 'I' ) {
239+ field = 'H' ;
240+ }
204241 unsigned int bit = directive_bits [field ];
205242 if (bit == 0 ) {
206243 return 0 ;
@@ -210,7 +247,7 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
210247 }
211248 seen |= bit ;
212249
213- if (c == 'b' || c == 'B' || c == 'a' || c == 'A' ) {
250+ if (c == 'b' || c == 'B' || c == 'a' || c == 'A' || c == 'p' ) {
214251 int value ;
215252 int count = strptime_name (data , length , pos , c , & value );
216253 if (count == 0 ) {
@@ -220,6 +257,9 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
220257 if (field == 'm' ) {
221258 fields -> month = value ;
222259 }
260+ else if (c == 'p' ) {
261+ ampm = value ;
262+ }
223263 else {
224264 fields -> weekday = value ;
225265 }
@@ -249,7 +289,8 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
249289 else if (field == 'w' ) {
250290 maximum = 1 ;
251291 }
252- else if ((c == 'd' || c == 'H' ) && pos < length && data [pos ] == ' ' ) {
292+ else if ((c == 'd' || c == 'H' || c == 'I' ) &&
293+ pos < length && data [pos ] == ' ' ) {
253294 pos ++ ;
254295 maximum = 1 ;
255296 }
@@ -260,6 +301,13 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
260301 }
261302 pos += count ;
262303 switch (c ) {
304+ case 'I' :
305+ if (value < 1 || value > 12 ) {
306+ return 0 ;
307+ }
308+ hour12 = 1 ;
309+ fields -> hour = value ;
310+ break ;
263311 case 'w' :
264312 if (value > 6 ) {
265313 return 0 ;
@@ -316,6 +364,12 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
316364 break ;
317365 }
318366 }
367+ if (hour12 ) {
368+ if (ampm < 0 && !strptime_name (NULL , 0 , 0 , 'p' , & ampm )) {
369+ return 0 ;
370+ }
371+ fields -> hour = fields -> hour % 12 + (ampm == 1 ? 12 : 0 );
372+ }
319373 static const int days_before_month [] = {
320374 0 , 0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 , 365
321375 };
0 commit comments