Skip to content

Instantly share code, notes, and snippets.

@sqdnoises
Created September 15, 2024 10:50
Show Gist options
  • Select an option

  • Save sqdnoises/dce6053e150447f41bce39fae9141527 to your computer and use it in GitHub Desktop.

Select an option

Save sqdnoises/dce6053e150447f41bce39fae9141527 to your computer and use it in GitHub Desktop.
Stopwatch - A solution to a problem that never existed...
import time
import datetime
from typing import Any, Iterable
__all__ = (
"Time",
"DatetimeTime",
"Stopwatch"
)
class Time:
"""
A class to represent time intervals in seconds and nanoseconds.
Attributes:
start (float | None): Start time in seconds.
end (float | None): End time in seconds.
total (float | None): Total time interval in seconds.
start_ns (int | None): Start time in nanoseconds.
end_ns (int | None): End time in nanoseconds.
total_ns (int | None): Total time interval in nanoseconds.
"""
start: float | None
end: float | None
total: float | None
start_ns: int | None
end_ns: int | None
total_ns: int | None
def __init__(
self,
start: int | float | None = None,
end: int | float | None = None,
ns: bool = False
) -> None:
"""
Initializes the Time object.
Args:
start (int | float | None): Start time in seconds or nanoseconds.
end (int | float | None): End time in seconds or nanoseconds.
ns (bool): If True, interpret start and end times as nanoseconds; otherwise, interpret as seconds.
"""
if ns:
start = int(start) if start is not None else None
end = int(end) if end is not None else None
self.start_ns = 0 if start is None and end is not None else start
self.end_ns = end
self.start = self.convert_ns_to_s(self.start_ns) if self.start_ns is not None else None
self.end = self.convert_ns_to_s(self.end_ns) if self.end_ns is not None else None
else:
start = float(start) if start is not None else None
end = float(end) if end is not None else None
self.start = 0.0 if start is None and end is not None else start
self.end = end
self.start_ns = self.convert_s_to_ns(self.start) if self.start is not None else None
self.end_ns = self.convert_s_to_ns(self.end) if self.end is not None else None
if self.start_ns is not None and self.end_ns is not None:
self.total_ns = self.end_ns - self.start_ns
self.total = self.convert_ns_to_s(self.total_ns)
else:
self.total_ns = None
self.total = None
def convert_ns_to_s(self, time_ns: int) -> float:
"""
Converts nanoseconds to seconds.
Args:
time_ns (int): Time in nanoseconds.
Returns:
float: Time in seconds.
"""
return time_ns / 1_000_000_000
def convert_s_to_ns(self, time_s: float) -> int:
"""
Converts seconds to nanoseconds.
Args:
time_s (float): Time in seconds.
Returns:
int: Time in nanoseconds.
"""
return int(time_s * 1_000_000_000)
class DatetimeTime:
"""
A class to represent time intervals using datetime objects.
Attributes:
start (datetime.datetime | None): Start time as a datetime object.
end (datetime.datetime | None): End time as a datetime object.
total (datetime.timedelta | None): Total time interval as a timedelta object.
"""
start: datetime.datetime | None
end: datetime.datetime | None
total: datetime.timedelta | None
def __init__(
self,
start: datetime.datetime | None = None,
end: datetime.datetime | None = None
) -> None:
"""
Initializes the DatetimeTime object.
Args:
start (datetime.datetime | None): Start time as a datetime object.
end (datetime.datetime | None): End time as a datetime object.
"""
self.start = start
self.end = end
if self.start is not None and self.end is not None:
self.total = self.end - self.start
else:
self.total = None
class Stopwatch:
"""
A class to measure elapsed time using either seconds and nanoseconds or datetime objects.
Attributes:
time (Time | DatetimeTime): Object to track time intervals.
_return_ns (bool): If True, return time in nanoseconds; otherwise, return in seconds or timedelta.
_dnargs (Iterable[Any]): Arguments for datetime.now() if using DatetimeTime.
_dnkwargs (dict[str, Any]): Keyword arguments for datetime.now() if using DatetimeTime.
"""
time: Time | DatetimeTime
_return_ns: bool
_dnargs: Iterable[Any]
_dnkwargs: dict[str, Any]
def __init__(
self,
start: bool = False,
*,
return_ns: bool = False,
datetime_: bool = False,
datetime_now_args: Iterable[Any] = (),
datetime_now_kwargs: dict[str, Any] = {}
) -> None:
"""
Initializes the Stopwatch object.
Args:
start (bool): If True, the stopwatch will start automatically upon initialization by calling `self.start()`. Defaults to False.
return_ns (bool): If True, return elapsed time in nanoseconds; otherwise, return in seconds or timedelta.
datetime_ (bool): If True, use datetime for time measurement; otherwise, use seconds and nanoseconds.
datetime_now_args (Iterable[Any]): Arguments for datetime.now() if using DatetimeTime.
datetime_now_kwargs (dict[str, Any]): Keyword arguments for datetime.now() if using DatetimeTime.
"""
if datetime_:
self.time = DatetimeTime()
self._dnargs = datetime_now_args
self._dnkwargs = datetime_now_kwargs
return
self.time = Time()
self._return_ns = return_ns
if start:
self.start()
def start(self) -> None:
"""
Starts the stopwatch. Records the current time as the start time.
"""
if isinstance(self.time, DatetimeTime):
self.time.start = datetime.datetime.now(*self._dnargs, **self._dnkwargs)
return
self.time.start_ns = time.monotonic_ns()
self.time.start = self.time.convert_ns_to_s(self.time.start_ns)
def stop(self) -> int | float | datetime.timedelta:
"""
Stops the stopwatch. Records the current time as the end time and calculates the elapsed time.
Returns:
int | float | datetime.timedelta: Elapsed time in nanoseconds, seconds, or timedelta.
Raises:
ValueError: If the stopwatch has not been started.
"""
if self.time.start is None:
raise ValueError("Stopwatch has not been started.")
if isinstance(self.time, DatetimeTime):
self.time.end = datetime.datetime.now(*self._dnargs, **self._dnkwargs)
self.time.total = self.time.end - self.time.start
return self.time.total
if self.time.start_ns is None:
raise ValueError("Stopwatch has not been started.")
self.time.end_ns = time.monotonic_ns()
self.time.end = self.time.convert_ns_to_s(self.time.end_ns)
self.time.total_ns = self.time.end_ns - self.time.start_ns
self.time.total = self.time.end - self.time.start
if self._return_ns: return self.time.total_ns
else: return self.time.total
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment