Skip to content

Commit e69f76a

Browse files
committed
Shift timecode floats forward a few picoseconds
Test that the resulting floats truncate down to the correct frame number when used to construct a timecode Closes #62
1 parent 224088b commit e69f76a

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

tests/test_timecode.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from timecode import Timecode, TimecodeError
55

6+
import random
67

78
@pytest.mark.parametrize(
89
"args,kwargs", [
@@ -999,6 +1000,34 @@ def test_rollover_for_23_98():
9991000
assert 2071873 == tc.frames
10001001
assert "23:58:48:00" == tc.__repr__()
10011002

1003+
@pytest.mark.parametrize(
1004+
"framerate", [
1005+
"23.976", "23.98", "24", "25", "29.97", "30", "50", "59.94", "60", "ms"
1006+
]
1007+
)
1008+
def test_float_representation_roundtrip(framerate):
1009+
"""Test float representation of Timecode."""
1010+
mismatched = 0
1011+
# Close enough to max frame across our supported framerates.
1012+
num_frames = Timecode(framerate, "23:59:59;23").frame_number
1013+
max_samples = 50_000
1014+
1015+
if num_frames <= max_samples:
1016+
frames_to_test = range(1, num_frames)
1017+
else:
1018+
# Not the most efficient sample allocation, but our range is pretty small.
1019+
random.seed(42) # Fixed seed for repeatability
1020+
frames_to_test = sorted(random.sample(range(1, num_frames), max_samples))
1021+
1022+
for i in frames_to_test:
1023+
tc = Timecode(framerate, frames=i)
1024+
from_float = Timecode(framerate, start_seconds=tc.float)
1025+
if tc != from_float:
1026+
mismatched += 1
1027+
1028+
tested = len(frames_to_test)
1029+
assert mismatched == 0, f"{mismatched}/{tested} ({mismatched / tested * 100:.1f}%) incorrect (sampled {tested} of {num_frames} total frames)"
1030+
10021031

10031032
@pytest.mark.parametrize(
10041033
"args,kwargs,str_repr", [

timecode/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,11 @@ def float(self) -> float:
871871
Returns:
872872
float: The seconds as float.
873873
"""
874-
return float(self.frames) / float(self._int_framerate)
874+
time_value = float(self.frames) / float(self._int_framerate)
875+
# Add a very small epsilon - just enough to ensure we're above the truncation point
876+
# but not so much that we'd jump to the next frame
877+
epsilon = 1e-10 # This is much smaller than 1/framerate for any reasonable framerate
878+
return time_value + epsilon
875879

876880

877881
class TimecodeError(Exception):

0 commit comments

Comments
 (0)