@@ -1104,6 +1104,99 @@ def __divmod__(self, other):
11041104#############################################################################
11051105# date tests
11061106
1107+ class TestStrptime (unittest .TestCase ):
1108+ def test_numeric_fields (self ):
1109+ cases = (
1110+ ('2024-02-29 12:34:56.123' , '%Y-%m-%d %H:%M:%S.%f' ,
1111+ datetime (2024 , 2 , 29 , 12 , 34 , 56 , 123000 )),
1112+ ('2024-02-29T12:34:56.123+0530' , '%Y-%m-%dT%H:%M:%S.%f%z' ,
1113+ datetime (2024 , 2 , 29 , 12 , 34 , 56 , 123000 ,
1114+ timezone (timedelta (hours = 5 , minutes = 30 )))),
1115+ ('12:34:56Z' , '%H:%M:%S%z' , datetime (1900 , 1 , 1 , 12 , 34 , 56 , tzinfo = UTC )),
1116+ ('12:34:56-03:30' , '%H:%M:%S%z' ,
1117+ datetime (1900 , 1 , 1 , 12 , 34 , 56 ,
1118+ tzinfo = timezone (- timedelta (hours = 3 , minutes = 30 )))),
1119+ ('12:34:56' , '%H:%M:%S%z' , datetime (1900 , 1 , 1 , 12 , 34 , 56 )),
1120+ ('' , '' , datetime (1900 , 1 , 1 )),
1121+ ('2024111' , '%Y%m%d' , datetime (2024 , 11 , 1 )),
1122+ ('2024131' , '%Y%m%d' , datetime (2024 , 1 , 31 )),
1123+ ('2024\0 -02-29' , '%Y\0 -%m-%d' , datetime (2024 , 2 , 29 )),
1124+ ('٢٠٢٤-02-29' , '%Y-%m-%d' , datetime (2024 , 2 , 29 )),
1125+ ('2024\u2003 02\u2003 29' , '%Y %m %d' , datetime (2024 , 2 , 29 )),
1126+ ('2024t02t29' , '%YT%mt%d' , datetime (2024 , 2 , 29 )),
1127+ ('24 2025-02-01' , '%y %Y-%m-%d' , datetime (2025 , 2 , 1 )),
1128+ )
1129+ for text , fmt , expected in cases :
1130+ for cls , result in ((datetime , expected ), (date , expected .date ()),
1131+ (time , expected .timetz ())):
1132+ with self .subTest (text = text , fmt = fmt , cls = cls ):
1133+ actual = cls .strptime (text , fmt )
1134+ self .assertEqual (actual , result )
1135+ self .assertIs (type (actual ), cls )
1136+ with self .subTest (text = text , fmt = fmt , cls = _time .struct_time ):
1137+ actual = _time .strptime (text , fmt )
1138+ self .assertEqual (actual , expected .timetuple ())
1139+ self .assertIsNone (actual .tm_zone )
1140+ offset = expected .utcoffset ()
1141+ self .assertEqual (actual .tm_gmtoff ,
1142+ None if offset is None else offset .total_seconds ())
1143+
1144+ def test_invalid_fields (self ):
1145+ cases = (
1146+ ('2024-02-30' , '%Y-%m-%d' ),
1147+ ('1900-02-29' , '%Y-%m-%d' ),
1148+ ('0000-01-01' , '%Y-%m-%d' ),
1149+ ('2024-01- 12' , '%Y-%m-%d' ),
1150+ ('2024\0 ignored' , '%Y' ),
1151+ ('24:00:00' , '%H:%M:%S' ),
1152+ ('23:60:00' , '%H:%M:%S' ),
1153+ ('23:59:62' , '%H:%M:%S' ),
1154+ )
1155+ for text , fmt in cases :
1156+ for parse in (datetime .strptime , date .strptime , time .strptime ,
1157+ _time .strptime ):
1158+ with self .subTest (text = text , fmt = fmt , parse = parse ):
1159+ with self .assertRaises (ValueError ):
1160+ parse (text , fmt )
1161+
1162+ def test_leap_seconds (self ):
1163+ for second in (60 , 61 ):
1164+ text = f'2024-02-29 23:59:{ second } '
1165+ fmt = '%Y-%m-%d %H:%M:%S'
1166+ with self .subTest (second = second ):
1167+ self .assertEqual (date .strptime (text , fmt ), date (2024 , 2 , 29 ))
1168+ self .assertEqual (_time .strptime (text , fmt ),
1169+ (2024 , 2 , 29 , 23 , 59 , second , 3 , 60 , - 1 ))
1170+ for cls in (datetime , time ):
1171+ with self .assertRaises (ValueError ):
1172+ cls .strptime (text , fmt )
1173+
1174+ def test_offset_outside_datetime_range (self ):
1175+ # date discards the offset; struct_time doesn't construct a timezone.
1176+ self .assertEqual (date .strptime ('+2400' , '%z' ), date (1900 , 1 , 1 ))
1177+ self .assertEqual (_time .strptime ('+2400' , '%z' ).tm_gmtoff , 86400 )
1178+ for cls in (datetime , time ):
1179+ with self .subTest (cls = cls ):
1180+ with self .assertRaises (ValueError ):
1181+ cls .strptime ('+2400' , '%z' )
1182+
1183+ def test_subclass_constructor (self ):
1184+ for cls , args in ((date , (2024 , 2 , 29 )),
1185+ (time , (12 , 34 , 56 , 123000 )),
1186+ (datetime , (2024 , 2 , 29 , 12 , 34 , 56 , 123000 ))):
1187+ class Capture (cls ):
1188+ def __new__ (cls , * args , ** kwargs ):
1189+ return args , kwargs
1190+
1191+ text = '2024-02-29 12:34:56.123'
1192+ fmt = '%Y-%m-%d %H:%M:%S.%f'
1193+ with self .subTest (cls = cls ):
1194+ self .assertEqual (Capture .strptime (text , fmt ), (args , {}))
1195+ aware_args = args if cls is date else args + (UTC ,)
1196+ self .assertEqual (Capture .strptime (text + 'Z' , fmt + '%z' ),
1197+ (aware_args , {}))
1198+
1199+
11071200class TestDateOnly (unittest .TestCase ):
11081201 # Tests here won't pass if also run on datetime objects, so don't
11091202 # subclass this to test datetimes too.
0 commit comments