@@ -161,11 +161,42 @@ def test_compile(self):
161161        for  directive  in  ('a' ,'A' ,'b' ,'B' ,'c' ,'d' ,'G' ,'H' ,'I' ,'j' ,'m' ,'M' ,'p' ,
162162                          'S' ,'u' ,'U' ,'V' ,'w' ,'W' ,'x' ,'X' ,'y' ,'Y' ,'Z' ,'%' ):
163163            fmt  =  "%d %Y"  if  directive  ==  'd'  else  "%"  +  directive 
164+             input_string  =  time .strftime (fmt )
164165            compiled  =  self .time_re .compile (fmt )
165-             found  =  compiled .match (time .strftime (fmt ))
166-             self .assertTrue (found , "Matching failed on '%s' using '%s' regex"  % 
167-                                     (time .strftime (fmt ),
168-                                      compiled .pattern ))
166+             found  =  compiled .match (input_string )
167+             self .assertTrue (found ,
168+                             (f"Matching failed on '{ input_string }  
169+                              f"using '{ compiled .pattern }  ))
170+         for  directive  in  ('c' , 'x' ):
171+             fmt  =  "%"  +  directive 
172+             with  self .subTest (f"{ fmt !r}  
173+                               f"year with fewer digits than usual" ):
174+                 # gh-124529 
175+                 params  =  _input_str_and_expected_year_for_few_digits_year (fmt )
176+                 if  params  is  None :
177+                     self .skipTest (f"this subtest needs locale for which " 
178+                                   f"{ fmt !r}  )
179+                 input_string , _  =  params 
180+                 compiled  =  self .time_re .compile (fmt )
181+                 found  =  compiled .match (input_string )
182+                 self .assertTrue (found ,
183+                                 (f"Matching failed on '{ input_string }  
184+                                  f"using '{ compiled .pattern }  ))
185+         for  directive  in  ('y' , 'Y' ):
186+             fmt  =  "%"  +  directive 
187+             with  self .subTest (f"{ fmt !r}  
188+                               f"year with fewer digits than usual" ):
189+                 params  =  _input_str_and_expected_year_for_few_digits_year (fmt )
190+                 if  params  is  None :
191+                     self .skipTest (f"this subtest needs locale for which " 
192+                                   f"{ fmt !r}  )
193+                 input_string , _  =  params 
194+                 compiled  =  self .time_re .compile (fmt )
195+                 found  =  compiled .match (input_string )
196+                 self .assertFalse (found ,
197+                                  (f"Matching unexpectedly succeeded " 
198+                                   f"on '{ input_string }  
199+                                   f"'{ compiled .pattern }  ))
169200
170201    def  test_blankpattern (self ):
171202        # Make sure when tuple or something has no values no regex is generated. 
@@ -299,6 +330,25 @@ def helper(self, directive, position):
299330                         (directive , strf_output , strp_output [position ],
300331                          self .time_tuple [position ]))
301332
333+     def  helper_for_directives_accepting_few_digits_year (self , directive ):
334+         fmt  =  "%"  +  directive 
335+         params  =  _input_str_and_expected_year_for_few_digits_year (fmt )
336+         if  params  is  None :
337+             self .skipTest (f"test needs locale for which { fmt !r}  
338+                           f"includes year in some variant" )
339+         input_string , expected_year  =  params 
340+         try :
341+             output_year  =  _strptime ._strptime (input_string , fmt )[0 ][0 ]
342+         except  ValueError  as  exc :
343+             # See: gh-124529 
344+             self .fail (f"testing of { directive !r}  
345+                       f"{ input_string !r} { exc !r}  )
346+         else :
347+             self .assertEqual (output_year , expected_year ,
348+                              (f"testing of { directive !r}  
349+                               f"{ input_string !r}  
350+                               f"{ output_year !r} { expected_year !r}  ))
351+ 
302352    def  test_year (self ):
303353        # Test that the year is handled properly 
304354        for  directive  in  ('y' , 'Y' ):
@@ -312,6 +362,17 @@ def test_year(self):
312362                                "'y' test failed; passed in '%s' " 
313363                                "and returned '%s'"  %  (bound , strp_output [0 ]))
314364
365+     def  test_bad_year (self ):
366+         for  directive , bad_inputs  in  (
367+             ('y' , ('9' , '100' , 'ni' )),
368+             ('Y' , ('7' , '42' , '999' , '10000' , 'SPAM' )),
369+         ):
370+             fmt  =  "%"  +  directive 
371+             for  input_val  in  bad_inputs :
372+                 with  self .subTest (directive = directive , input_val = input_val ):
373+                     with  self .assertRaises (ValueError ):
374+                         _strptime ._strptime_time (input_val , fmt )
375+ 
315376    def  test_month (self ):
316377        # Test for month directives 
317378        for  directive  in  ('B' , 'b' , 'm' ):
@@ -454,11 +515,21 @@ def test_date_time(self):
454515        for  position  in  range (6 ):
455516            self .helper ('c' , position )
456517
518+     def  test_date_time_accepting_few_digits_year (self ):  # gh-124529 
519+         # Test %c directive with input containing year 
520+         # number consisting of fewer digits than usual 
521+         self .helper_for_directives_accepting_few_digits_year ('c' )
522+ 
457523    def  test_date (self ):
458524        # Test %x directive 
459525        for  position  in  range (0 ,3 ):
460526            self .helper ('x' , position )
461527
528+     def  test_date_accepting_few_digits_year (self ):  # gh-124529 
529+         # Test %x directive with input containing year 
530+         # number consisting of fewer digits than usual 
531+         self .helper_for_directives_accepting_few_digits_year ('x' )
532+ 
462533    def  test_time (self ):
463534        # Test %X directive 
464535        for  position  in  range (3 ,6 ):
@@ -769,5 +840,55 @@ def test_TimeRE_recreation_timezone(self):
769840            _strptime ._strptime_time (oldtzname [1 ], '%Z' )
770841
771842
843+ def  _input_str_and_expected_year_for_few_digits_year (fmt ):
844+     # This helper, for the given format string (fmt), returns a 2-tuple: 
845+     #   (<strptime input string>, <expected year>) 
846+     # where: 
847+     # * <strptime input string> -- is a `strftime(fmt)`-result-like str 
848+     #   containing a year number which is *shorter* than the usual four 
849+     #   or two digits (namely: the contained year number consist of just 
850+     #   one digit: 7; the choice of this particular digit is arbitrary); 
851+     # * <expected year> -- is an int representing the year number that 
852+     #   is expected to be part of the result of a `strptime(<strptime 
853+     #   input string>, fmt)` call (namely: either 7 or 2007, depending 
854+     #   on the given format string and current locale...); however, it 
855+     #   is None if <strptime input string> does *not* contain the year 
856+     #   part (for the given format string and current locale). 
857+ 
858+     # 1. Prepare auxiliary *magic* time data (note that the magic values 
859+     # we use here are guaranteed to be compatible with `time.strftime()` 
860+     # and also well distinguishable within a formatted string, thanks to 
861+     # the fact that the amount of overloaded numbers is minimized, as in 
862+     # `_strptime.LocaleTime.__calc_date_time()`...): 
863+     magic_year  =  1999 
864+     magic_tt  =  (magic_year , 3 , 17 , 22 , 44 , 55 , 2 , 76 , 0 )
865+     magic_4digits  =  str (magic_year )
866+     magic_2digits  =  magic_4digits [- 2 :]
867+ 
868+     # 2. Pick our example year whose representation 
869+     # is shorter than the usual four or two digits: 
870+     input_year_str  =  '7' 
871+ 
872+     # 3. Determine the <strptime input string> part of the return value: 
873+     input_string  =  time .strftime (fmt , magic_tt )
874+     if  (index_4digits  :=  input_string .find (magic_4digits )) !=  - 1 :
875+         # `input_string` contains up-to-4-digit year representation 
876+         input_string  =  input_string .replace (magic_4digits , input_year_str )
877+     if  (index_2digits  :=  input_string .find (magic_2digits )) !=  - 1 :
878+         # `input_string` contains up-to-2-digit year representation 
879+         input_string  =  input_string .replace (magic_2digits , input_year_str )
880+ 
881+     # 4. Determine the <expected year> part of the return value: 
882+     if  index_4digits  >  index_2digits :
883+         expected_year  =  int (input_year_str )
884+     elif  index_4digits  <  index_2digits :
885+         expected_year  =  2000  +  int (input_year_str )
886+     else :
887+         assert  index_4digits  ==  index_2digits  ==  - 1 
888+         expected_year  =  None 
889+ 
890+     return  input_string , expected_year 
891+ 
892+ 
772893if  __name__  ==  '__main__' :
773894    unittest .main ()
0 commit comments