1212 * - ASCII locale month/weekday names and numeric %w/%u weekdays;
1313 * - %I with ASCII %p indicators, and the %k, %l, %P aliases;
1414 * - %z and %:z offsets with hours below 24, and terminal UTC/GMT %Z names;
15+ * - numeric aliases %F, %D, %T, %R, %e and whitespace %n/%t;
1516 * - literal characters, %%, and ASCII whitespace.
1617 * Duplicate directives, mixed %Y/%y, and day-of-month without a year use the
1718 * fallback. So do other directives (including non-ASCII locale names and
@@ -206,6 +207,8 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
206207 }
207208 const unsigned char * data = PyUnicode_1BYTE_DATA (string );
208209 const unsigned char * fmt = PyUnicode_1BYTE_DATA (format );
210+ const unsigned char * outer_fmt = NULL ;
211+ Py_ssize_t outer_length = 0 , outer_pos = 0 ;
209212 Py_ssize_t length = PyUnicode_GET_LENGTH (string );
210213 Py_ssize_t fmt_length = PyUnicode_GET_LENGTH (format );
211214 * fields = (_PyTime_StrptimeFields ){.year = 1900 , .month = 1 , .day = 1 ,
@@ -215,7 +218,19 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
215218 int ampm = -1 ;
216219 Py_ssize_t pos = 0 ;
217220
218- for (Py_ssize_t i = 0 ; i < fmt_length ; i ++ ) {
221+ for (Py_ssize_t i = 0 ; ; i ++ ) {
222+ if (i == fmt_length ) {
223+ if (outer_fmt == NULL ) {
224+ break ;
225+ }
226+ fmt = outer_fmt ;
227+ fmt_length = outer_length ;
228+ i = outer_pos ;
229+ outer_fmt = NULL ;
230+ if (i == fmt_length ) {
231+ break ;
232+ }
233+ }
219234 unsigned char c = fmt [i ];
220235 if (Py_ISSPACE (c )) {
221236 if (pos == length || !Py_ISSPACE (data [pos ])) {
@@ -239,6 +254,34 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
239254 return 0 ;
240255 }
241256 c = fmt [i ];
257+ const char * alias = NULL ;
258+ switch (c ) {
259+ case 'F' : alias = "%Y-%m-%d" ; break ;
260+ case 'D' : alias = "%m/%d/%y" ; break ;
261+ case 'T' : alias = "%H:%M:%S" ; break ;
262+ case 'R' : alias = "%H:%M" ; break ;
263+ case 'e' : c = 'd' ; break ;
264+ }
265+ if (alias != NULL ) {
266+ /* These fixed expansions contain no further aliases. Parsing
267+ * them in place keeps duplicate-field checks and avoids copying
268+ * an arbitrarily long user format into a temporary buffer.
269+ */
270+ assert (outer_fmt == NULL );
271+ outer_fmt = fmt ;
272+ outer_length = fmt_length ;
273+ outer_pos = i + 1 ;
274+ fmt = (const unsigned char * )alias ;
275+ fmt_length = strlen (alias );
276+ i = -1 ;
277+ continue ;
278+ }
279+ if (c == 'n' || c == 't' ) {
280+ while (pos < length && Py_ISSPACE (data [pos ])) {
281+ pos ++ ;
282+ }
283+ continue ;
284+ }
242285 int require_colon = 0 ;
243286 if (c == ':' && i + 1 < fmt_length && fmt [i + 1 ] == 'z' ) {
244287 c = fmt [++ i ];
0 commit comments