@@ -1105,6 +1105,163 @@ def test_approx_on_unordered_mapping_matching():
11051105 result .assert_outcomes (passed = 1 )
11061106
11071107
1108+ class TestApproxDatetime :
1109+ """Tests for datetime/timedelta support in approx (issue #8395)."""
1110+
1111+ def test_datetime_exactly_equal (self ):
1112+ from datetime import datetime , timedelta
1113+
1114+ dt = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1115+ assert dt == approx (dt , abs = timedelta (seconds = 1 ))
1116+
1117+ def test_datetime_within_tolerance (self ):
1118+ from datetime import datetime , timedelta
1119+
1120+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1121+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , 500000 ) # +0.5s
1122+ assert dt1 == approx (dt2 , abs = timedelta (seconds = 1 ))
1123+
1124+ def test_datetime_outside_tolerance (self ):
1125+ from datetime import datetime , timedelta
1126+
1127+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1128+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 2 ) # +2s
1129+ assert dt1 != approx (dt2 , abs = timedelta (seconds = 1 ))
1130+
1131+ def test_datetime_negative_difference (self ):
1132+ from datetime import datetime , timedelta
1133+
1134+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 1 )
1135+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 0 ) # dt2 < dt1
1136+ assert dt1 == approx (dt2 , abs = timedelta (seconds = 2 ))
1137+ assert dt1 != approx (dt2 , abs = timedelta (milliseconds = 500 ))
1138+
1139+ def test_timedelta_within_tolerance (self ):
1140+ from datetime import timedelta
1141+
1142+ td1 = timedelta (seconds = 100 )
1143+ td2 = timedelta (seconds = 100.5 )
1144+ assert td1 == approx (td2 , abs = timedelta (seconds = 1 ))
1145+
1146+ def test_timedelta_outside_tolerance (self ):
1147+ from datetime import timedelta
1148+
1149+ td1 = timedelta (seconds = 100 )
1150+ td2 = timedelta (seconds = 102 )
1151+ assert td1 != approx (td2 , abs = timedelta (seconds = 1 ))
1152+
1153+ def test_requires_abs (self ):
1154+ from datetime import datetime
1155+
1156+ with pytest .raises (TypeError , match = "requires an absolute tolerance" ):
1157+ approx (datetime (2024 , 1 , 1 ))
1158+
1159+ def test_rejects_rel (self ):
1160+ from datetime import datetime , timedelta
1161+
1162+ with pytest .raises (TypeError , match = "does not support relative tolerance" ):
1163+ approx (datetime (2024 , 1 , 1 ), rel = 0.1 , abs = timedelta (seconds = 1 ))
1164+
1165+ def test_abs_must_be_timedelta (self ):
1166+ from datetime import datetime
1167+
1168+ with pytest .raises (TypeError , match = "must be a timedelta" ):
1169+ approx (datetime (2024 , 1 , 1 ), abs = 1.0 )
1170+
1171+ def test_rejects_nan_ok (self ):
1172+ from datetime import datetime , timedelta
1173+
1174+ with pytest .raises (TypeError , match = "does not support nan_ok" ):
1175+ approx (datetime (2024 , 1 , 1 ), abs = timedelta (seconds = 1 ), nan_ok = True )
1176+
1177+ def test_datetime_repr (self ):
1178+ from datetime import datetime , timedelta
1179+
1180+ dt = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1181+ result = repr (approx (dt , abs = timedelta (seconds = 1 )))
1182+ assert "2024-01-01 12:00:00" in result
1183+ assert "0:00:01" in result
1184+
1185+ def test_timedelta_repr (self ):
1186+ from datetime import timedelta
1187+
1188+ td = timedelta (seconds = 100 )
1189+ result = repr (approx (td , abs = timedelta (seconds = 1 )))
1190+ assert "0:01:40" in result # 100 seconds
1191+ assert "0:00:01" in result # 1 second tolerance
1192+
1193+ def test_datetime_symmetry (self ):
1194+ """approx comparison should work on both sides of ==."""
1195+ from datetime import datetime , timedelta
1196+
1197+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1198+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , 500000 )
1199+ tol = timedelta (seconds = 1 )
1200+ assert dt1 == approx (dt2 , abs = tol )
1201+ assert approx (dt2 , abs = tol ) == dt1
1202+
1203+ def test_datetime_ne_operator (self ):
1204+ from datetime import datetime , timedelta
1205+
1206+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1207+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 5 )
1208+ tol = timedelta (seconds = 1 )
1209+ assert dt1 != approx (dt2 , abs = tol )
1210+ assert not (dt1 == approx (dt2 , abs = tol ))
1211+
1212+ def test_datetime_with_timezone (self ):
1213+ from datetime import datetime , timedelta , timezone
1214+
1215+ tz = timezone .utc
1216+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , tzinfo = tz )
1217+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , 500000 , tzinfo = tz )
1218+ assert dt1 == approx (dt2 , abs = timedelta (seconds = 1 ))
1219+
1220+ def test_datetime_error_message (self ):
1221+ from datetime import datetime , timedelta
1222+
1223+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1224+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 5 ) # 5 seconds off
1225+ with pytest .raises (AssertionError , match = "comparison failed" ):
1226+ assert dt1 == approx (dt2 , abs = timedelta (seconds = 1 ))
1227+
1228+ def test_timedelta_zero (self ):
1229+ from datetime import timedelta
1230+
1231+ td1 = timedelta (seconds = 0 )
1232+ td2 = timedelta (seconds = 0 )
1233+ assert td1 == approx (td2 , abs = timedelta (seconds = 1 ))
1234+
1235+ def test_datetime_boundary_exact (self ):
1236+ """Test that values exactly at the tolerance boundary are equal."""
1237+ from datetime import datetime , timedelta
1238+
1239+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 )
1240+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 1 ) # exactly 1 second
1241+ assert dt1 == approx (dt2 , abs = timedelta (seconds = 1 ))
1242+
1243+ def test_datetime_microsecond_tolerance (self ):
1244+ from datetime import datetime , timedelta
1245+
1246+ dt1 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , 0 )
1247+ dt2 = datetime (2024 , 1 , 1 , 12 , 0 , 0 , 100 ) # +100 microseconds
1248+ assert dt1 == approx (dt2 , abs = timedelta (microseconds = 200 ))
1249+ assert dt1 != approx (dt2 , abs = timedelta (microseconds = 50 ))
1250+
1251+ def test_bool_context_raises (self ):
1252+ from datetime import datetime , timedelta
1253+
1254+ with pytest .raises (AssertionError , match = "boolean context" ):
1255+ bool (approx (datetime (2024 , 1 , 1 ), abs = timedelta (seconds = 1 )))
1256+
1257+ def test_wrong_type_comparison (self ):
1258+ """Comparing a datetime approx with a non-datetime should return False."""
1259+ from datetime import datetime , timedelta
1260+
1261+ assert 42 != approx (datetime (2024 , 1 , 1 ), abs = timedelta (seconds = 1 ))
1262+ assert "string" != approx (datetime (2024 , 1 , 1 ), abs = timedelta (seconds = 1 ))
1263+
1264+
11081265class MyVec3 : # incomplete
11091266 """sequence like"""
11101267
0 commit comments