@@ -249,6 +249,19 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
249249 ['U' ] = WEEK ,
250250 ['G' ] = ISO_YEAR ,
251251 ['V' ] = ISO_WEEK ,
252+ /* Equivalent directives share the duplicate-field checks. */
253+ ['b' ] = MONTH ,
254+ ['B' ] = MONTH ,
255+ ['e' ] = DAY ,
256+ ['k' ] = HOUR ,
257+ ['I' ] = HOUR ,
258+ ['l' ] = HOUR ,
259+ ['a' ] = WEEKDAY ,
260+ ['A' ] = WEEKDAY ,
261+ ['u' ] = WEEKDAY ,
262+ ['P' ] = AMPM ,
263+ [':' ] = OFFSET ,
264+ ['W' ] = WEEK ,
252265 };
253266 /* String subclasses and non-ASCII input retain Python's behavior. */
254267 if (!PyUnicode_CheckExact (string ) || !PyUnicode_CheckExact (format ) ||
@@ -308,145 +321,144 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
308321 return 0 ;
309322 }
310323 c = fmt [i ];
311- const char * alias = NULL ;
312- switch (c ) {
313- case 'F' : alias = "%Y-%m-%d" ; break ;
314- case 'D' : alias = "%m/%d/%y" ; break ;
315- case 'T' : alias = "%H:%M:%S" ; break ;
316- case 'R' : alias = "%H:%M" ; break ;
317- case 'e' : c = 'd' ; break ;
318- }
319- if (alias != NULL ) {
320- /* These fixed expansions contain no further aliases. Parsing
321- * them in place keeps duplicate-field checks and avoids copying
322- * an arbitrarily long user format into a temporary buffer.
323- */
324- assert (outer_fmt == NULL );
325- outer_fmt = fmt ;
326- outer_length = fmt_length ;
327- outer_pos = i + 1 ;
328- fmt = (const unsigned char * )alias ;
329- fmt_length = strlen (alias );
330- i = -1 ;
331- continue ;
332- }
333- if (c == 'n' || c == 't' ) {
334- while (pos < length && Py_UNICODE_ISSPACE (data [pos ])) {
335- pos ++ ;
336- }
337- continue ;
338- }
339- int require_colon = 0 ;
340- if (c == ':' && i + 1 < fmt_length && fmt [i + 1 ] == 'z' ) {
341- c = fmt [++ i ];
342- require_colon = 1 ;
343- }
344- if (c == 'k' ) {
345- c = 'H' ;
346- }
347- else if (c == 'l' ) {
348- c = 'I' ;
349- }
350- else if (c == 'P' ) {
351- c = 'p' ;
352- }
353- if (c == '%' ) {
354- if (pos == length || data [pos ++ ] != '%' ) {
355- return 0 ;
356- }
357- continue ;
358- }
359-
360- /* Mixed spellings of the same field retain Python's precedence. */
361- unsigned char field = c ;
362- if (c == 'b' || c == 'B' ) {
363- field = 'm' ;
364- }
365- else if (c == 'a' || c == 'A' || c == 'u' ) {
366- field = 'w' ;
367- }
368- else if (c == 'I' ) {
369- field = 'H' ;
370- }
371- else if (c == 'W' ) {
372- field = 'U' ;
373- }
374- unsigned int bit = directive_bits [field ];
375- if (bit == 0 ) {
376- return 0 ;
377- }
324+ unsigned int bit = directive_bits [c ];
378325 if (seen & bit ) {
379326 return 0 ;
380327 }
381328 seen |= bit ;
382329
383- if (c == 'b' || c == 'B' || c == 'a' || c == 'A' || c == 'p' ) {
384- int value ;
385- int count = strptime_name (data , length , pos , c , & value );
386- if (count == 0 ) {
387- return 0 ;
388- }
389- pos += count ;
390- if (field == 'm' ) {
391- fields -> month = value ;
392- }
393- else if (c == 'p' ) {
394- ampm = value ;
330+ /* Dispatch once: numeric fields don't need to test for names,
331+ * aliases or timezone spellings before parsing their digits.
332+ */
333+ int minimum = 1 ;
334+ int maximum = 2 ;
335+ switch (c ) {
336+ case 'Y' :
337+ case 'G' :
338+ minimum = maximum = 4 ;
339+ break ;
340+ case 'y' :
341+ minimum = maximum = 2 ;
342+ break ;
343+ case 'f' :
344+ maximum = 6 ;
345+ break ;
346+ case 'j' :
347+ maximum = 3 ;
348+ break ;
349+ case 'w' :
350+ case 'u' :
351+ maximum = 1 ;
352+ break ;
353+ case 'd' :
354+ case 'e' :
355+ case 'H' :
356+ case 'k' :
357+ case 'I' :
358+ case 'l' :
359+ if (pos < length && data [pos ] == ' ' ) {
360+ pos ++ ;
361+ maximum = 1 ;
362+ }
363+ break ;
364+ case 'm' :
365+ case 'M' :
366+ case 'S' :
367+ case 'U' :
368+ case 'W' :
369+ case 'V' :
370+ break ;
371+ case 'b' :
372+ case 'B' :
373+ case 'a' :
374+ case 'A' :
375+ case 'p' :
376+ case 'P' : {
377+ int value ;
378+ int count = strptime_name (data , length , pos ,
379+ c == 'P' ? 'p' : c , & value );
380+ if (count == 0 ) {
381+ return 0 ;
382+ }
383+ pos += count ;
384+ if (bit == MONTH ) {
385+ fields -> month = value ;
386+ }
387+ else if (bit == AMPM ) {
388+ ampm = value ;
389+ }
390+ else {
391+ fields -> weekday = value ;
392+ }
393+ continue ;
395394 }
396- else {
397- fields -> weekday = value ;
395+ case 'Z' :
396+ /* Local timezone names and nonterminal names retain Python's
397+ * tzset state, DST rules and regex backtracking.
398+ */
399+ if (i != fmt_length - 1 || length - pos != 3 ||
400+ !((Py_TOLOWER (data [pos ]) == 'u' &&
401+ Py_TOLOWER (data [pos + 1 ]) == 't' &&
402+ Py_TOLOWER (data [pos + 2 ]) == 'c' ) ||
403+ (Py_TOLOWER (data [pos ]) == 'g' &&
404+ Py_TOLOWER (data [pos + 1 ]) == 'm' &&
405+ Py_TOLOWER (data [pos + 2 ]) == 't' ))) {
406+ return 0 ;
407+ }
408+ fields -> zone_start = pos ;
409+ fields -> zone_length = 3 ;
410+ pos = length ;
411+ continue ;
412+ case ':' :
413+ if (++ i == fmt_length || fmt [i ] != 'z' ) {
414+ return 0 ;
415+ }
416+ _Py_FALLTHROUGH ;
417+ case 'z' : {
418+ int count = strptime_offset (data , length , pos , c == ':' , fields );
419+ if (count < 0 ) {
420+ return 0 ;
421+ }
422+ pos += count ;
423+ continue ;
398424 }
399- continue ;
400- }
401-
402- if (c == 'Z' ) {
403- /* Local timezone names and nonterminal names retain Python's
404- * tzset state, DST rules and regex backtracking.
405- */
406- if (i != fmt_length - 1 || length - pos != 3 ||
407- !((Py_TOLOWER (data [pos ]) == 'u' &&
408- Py_TOLOWER (data [pos + 1 ]) == 't' &&
409- Py_TOLOWER (data [pos + 2 ]) == 'c' ) ||
410- (Py_TOLOWER (data [pos ]) == 'g' &&
411- Py_TOLOWER (data [pos + 1 ]) == 'm' &&
412- Py_TOLOWER (data [pos + 2 ]) == 't' ))) {
413- return 0 ;
425+ case 'F' :
426+ case 'D' :
427+ case 'T' :
428+ case 'R' : {
429+ const char * alias ;
430+ switch (c ) {
431+ case 'F' : alias = "%Y-%m-%d" ; break ;
432+ case 'D' : alias = "%m/%d/%y" ; break ;
433+ case 'T' : alias = "%H:%M:%S" ; break ;
434+ default : alias = "%H:%M" ; break ;
435+ }
436+ /* These fixed expansions contain no further aliases. Parsing
437+ * them in place keeps duplicate-field checks and avoids copying
438+ * an arbitrarily long user format into a temporary buffer.
439+ */
440+ assert (outer_fmt == NULL );
441+ outer_fmt = fmt ;
442+ outer_length = fmt_length ;
443+ outer_pos = i + 1 ;
444+ fmt = (const unsigned char * )alias ;
445+ fmt_length = strlen (alias );
446+ i = -1 ;
447+ continue ;
414448 }
415- fields -> zone_start = pos ;
416- fields -> zone_length = 3 ;
417- pos = length ;
418- continue ;
419- }
420- if (c == 'z' ) {
421- int count = strptime_offset (data , length , pos , require_colon , fields );
422- if (count < 0 ) {
449+ case 'n' :
450+ case 't' :
451+ while (pos < length && Py_UNICODE_ISSPACE (data [pos ])) {
452+ pos ++ ;
453+ }
454+ continue ;
455+ case '%' :
456+ if (pos == length || data [pos ++ ] != '%' ) {
457+ return 0 ;
458+ }
459+ continue ;
460+ default :
423461 return 0 ;
424- }
425- pos += count ;
426- continue ;
427- }
428-
429- int minimum = 1 ;
430- int maximum = 2 ;
431- if (c == 'Y' || c == 'G' ) {
432- minimum = maximum = 4 ;
433- }
434- else if (c == 'y' ) {
435- minimum = maximum = 2 ;
436- }
437- else if (c == 'f' ) {
438- maximum = 6 ;
439- }
440- else if (c == 'j' ) {
441- maximum = 3 ;
442- }
443- else if (field == 'w' ) {
444- maximum = 1 ;
445- }
446- else if ((c == 'd' || c == 'H' || c == 'I' ) &&
447- pos < length && data [pos ] == ' ' ) {
448- pos ++ ;
449- maximum = 1 ;
450462 }
451463 int value ;
452464 int count = strptime_digits (data , length , pos , minimum , maximum , & value );
@@ -480,6 +492,7 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
480492 iso_week = value ;
481493 break ;
482494 case 'I' :
495+ case 'l' :
483496 if (value < 1 || value > 12 ) {
484497 return 0 ;
485498 }
@@ -511,12 +524,14 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
511524 fields -> month = value ;
512525 break ;
513526 case 'd' :
527+ case 'e' :
514528 if (value < 1 || value > 31 ) {
515529 return 0 ;
516530 }
517531 fields -> day = value ;
518532 break ;
519533 case 'H' :
534+ case 'k' :
520535 if (value > 23 ) {
521536 return 0 ;
522537 }
@@ -612,8 +627,5 @@ _PyTime_Strptime(PyObject *string, PyObject *format, _PyTime_StrptimeFields *fie
612627 fields -> yday = days_before_month [fields -> month ] + fields -> day +
613628 (fields -> month > 2 && leap );
614629 }
615- if (fields -> weekday < 0 ) {
616- fields -> weekday = _PyTime_Weekday (fields -> year , fields -> yday );
617- }
618630 return 1 ;
619631}
0 commit comments