Skip to content

Commit b6318da

Browse files
committed
Refactor to use fractions.
1 parent fef3085 commit b6318da

4 files changed

Lines changed: 497 additions & 305 deletions

File tree

src/timecode/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
from timecode._version import __version__ # noqa: F401
2-
from timecode.timecode import Timecode, TimecodeError # noqa: F401
2+
from timecode.timecode import Timecode # noqa: F401

src/timecode/helpers.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""Helper class for Timecode handling and byproducts."""
2+
3+
from __future__ import annotations
4+
5+
import sys
6+
from fractions import Fraction
7+
from typing import NewType
8+
9+
if sys.version_info >= (3, 11):
10+
_frate_type = Fraction | str | float | tuple[int, int]
11+
else:
12+
from typing import Union
13+
_frate_type = Union[Fraction, str, float, tuple[int, int]]
14+
15+
_Framerate = NewType("_Framerate", _frate_type)
16+
17+
class _Timestamp:
18+
def __init__(self, ts: Fraction, usec_precision: bool = False) -> None:
19+
if ts < 0:
20+
raise ValueError(f"Timestamp cannot be negative, got {ts}.")
21+
self.usec_precision = usec_precision
22+
self._exact_ts = ts
23+
24+
def __float__(self) -> float:
25+
"""Convert this _Timestamp instance to a float (timestamp in seconds).
26+
27+
Returns:
28+
float: timestamp value in seconds of this instance
29+
"""
30+
return float(self._exact_ts)
31+
32+
def total_seconds(self) -> float:
33+
"""Return the time in seconds of this Timestamp instance as a float.
34+
35+
Returns:
36+
float: timestamp value in seconds of this instance
37+
38+
Truncation is possible, it's not an exact representation.
39+
"""
40+
return float(self)
41+
42+
def exact(self) -> Fraction:
43+
"""Return the time in seconds of this Timestamp instance as a fraction.
44+
45+
Returns:
46+
Fraction: exact timestamp value in seconds of this instance.
47+
"""
48+
return self._exact_ts
49+
50+
def __eq__(self, other: _Timestamp | float | Fraction) -> bool:
51+
"""Equal implementation for _Timestamp instance.
52+
53+
Args:
54+
other (_Timestamp | int | float | Fraction): object to compare
55+
instance to.
56+
57+
Returns:
58+
Result of the operation.
59+
"""
60+
if isinstance(other, __class__):
61+
return self._exact_ts == other._exact_ts
62+
if isinstance(other, Fraction):
63+
return self._exact_ts == other
64+
if isinstance(other, (float, int)):
65+
return float(self) == other
66+
return NotImplemented
67+
68+
def __ne__(self, other: _Timestamp | float | Fraction) -> bool:
69+
"""Not equal implementation for _Timestamp instance.
70+
71+
Args:
72+
other (_Timestamp | int | float | Fraction): object to compare
73+
instance to.
74+
75+
Returns:
76+
Result of the operation.
77+
"""
78+
if isinstance(other, __class__):
79+
return self._exact_ts != other._exact_ts
80+
if isinstance(other, Fraction):
81+
return self._exact_ts != other
82+
if isinstance(other, (float, int)):
83+
return float(self) != other
84+
return NotImplemented
85+
86+
def __lt__(self, other: _Timestamp | float | Fraction) -> bool:
87+
"""Less than implementation for _Timestamp instance.
88+
89+
Args:
90+
other (_Timestamp | int | float | Fraction): object to compare
91+
instance to.
92+
93+
Returns:
94+
Result of the operation.
95+
"""
96+
if isinstance(other, __class__):
97+
return self._exact_ts < other._exact_ts
98+
if isinstance(other, Fraction):
99+
return self._exact_ts < other
100+
if isinstance(other, (float, int)):
101+
return float(self) < other
102+
return NotImplemented
103+
104+
def __gt__(self, other: _Timestamp | float | Fraction) -> bool:
105+
"""Greater than implementation for _Timestamp instance.
106+
107+
Args:
108+
other (_Timestamp | int | float | Fraction): object to compare
109+
instance to.
110+
111+
Returns:
112+
Result of the operation.
113+
"""
114+
if isinstance(other, __class__):
115+
return self._exact_ts > other._exact_ts
116+
if isinstance(other, Fraction):
117+
return self._exact_ts > other
118+
if isinstance(other, (float, int)):
119+
return float(self) > other
120+
return NotImplemented
121+
122+
def __le__(self, other: _Timestamp | float | Fraction) -> bool:
123+
"""Less or equal implementation for _Timestamp instance.
124+
125+
Args:
126+
other (_Timestamp | int | float | Fraction): object to compare
127+
instance to.
128+
129+
Returns:
130+
Result of the operation.
131+
"""
132+
if isinstance(other, __class__):
133+
return self._exact_ts <= other._exact_ts
134+
if isinstance(other, Fraction):
135+
return self._exact_ts <= other
136+
if isinstance(other, (float, int)):
137+
return float(self) <= other
138+
return NotImplemented
139+
140+
def __ge__(self, other: _Timestamp | float | Fraction) -> bool:
141+
"""Greater or equal implementation for _Timestamp instance.
142+
143+
Args:
144+
other (_Timestamp | int | float | Fraction): object to compare
145+
instance to.
146+
147+
Returns:
148+
Result of the operation.
149+
"""
150+
if isinstance(other, __class__):
151+
return self._exact_ts >= other._exact_ts
152+
if isinstance(other, Fraction):
153+
return self._exact_ts >= other
154+
if isinstance(other, (float, int)):
155+
return float(self) >= other
156+
return NotImplemented
157+
158+
def __str__(self) -> str:
159+
"""Convert the _Timestamp instance to a timestamp string.
160+
161+
Returns:
162+
string: Timestamp string of this _Timestamp instance.
163+
"""
164+
hh = int(self._exact_ts // 3600)
165+
mm = int(self._exact_ts // 60) % 60
166+
ss = int(self._exact_ts % 60)
167+
decimal_part = (self._exact_ts - int(self._exact_ts))*1000
168+
169+
if self.usec_precision:
170+
s_decimal_part = f"{round(decimal_part*1000):>06}"
171+
else:
172+
s_decimal_part = f"{round(decimal_part):03}"
173+
return f"{hh:02d}:{mm:02d}:{ss:02d}.{s_decimal_part}"
174+
175+
def __repr__(self) -> str:
176+
"""Represent a _Timestamp instance.
177+
178+
Returns:
179+
string: representation of this _Timestamp instance.
180+
"""
181+
usec_part = f", usec_precision={self.usec_precision}" * self.usec_precision
182+
return f"{__class__.__name__}({self._exact_ts}{usec_part})"
183+
####

0 commit comments

Comments
 (0)