Skip to content

Instantly share code, notes, and snippets.

@nimaid
Last active December 22, 2023 17:45
Show Gist options
  • Save nimaid/f8893767beb70f9cb5b9c7c62c969174 to your computer and use it in GitHub Desktop.
Save nimaid/f8893767beb70f9cb5b9c7c62c969174 to your computer and use it in GitHub Desktop.
General purpose python classes for computing ETAs and formatting time strings.
import datetime
class TimeString:
@staticmethod
def split_seconds(seconds_in):
days, remainder = divmod(seconds_in, (60 ** 2) * 24)
hours, remainder = divmod(remainder, 60 ** 2)
minutes, seconds = divmod(remainder, 60)
return {
"d": days,
"h": hours,
"m": minutes,
"s": seconds
}
class TimeDelta:
@staticmethod
def short(time_in):
if time_in < datetime.timedelta(0):
time_in = datetime.timedelta(0)
time_parts = TimeString.split_seconds(round(time_in.total_seconds()))
time_string = f"{time_parts['h']}H:{time_parts['m']:02}M:{time_parts['s']:02}S"
if time_parts["d"] > 0:
time_string = f"{time_parts['d']}D:{time_string}"
return time_string
@staticmethod
def long(time_in):
if time_in < datetime.timedelta(0):
time_in = datetime.timedelta(0)
time_parts = TimeString.split_seconds(round(time_in.total_seconds()))
time_strings = []
if time_parts["d"] > 0:
time_strings.append(f"{time_parts['d']} day")
if time_parts["d"] != 1:
time_strings[-1] += "s"
if time_parts["h"] > 0:
time_strings.append(f"{time_parts['h']} hour")
if time_parts["h"] != 1:
time_strings[-1] += "s"
if time_parts["m"] > 0:
time_strings.append(f"{time_parts['m']} minute")
if time_parts["m"] != 1:
time_strings[-1] += "s"
if len(time_strings) == 0 or time_parts["s"] > 0:
time_strings.append(f"{time_parts['s']} second")
if time_parts["s"] != 1:
time_strings[-1] += "s"
if len(time_strings) == 1:
time_string = time_strings[0]
elif len(time_strings) == 2:
time_string = f"{time_strings[0]} and {time_strings[1]}"
else:
time_string = ", ".join(time_strings[:-1])
time_string += f", and {time_strings[-1]}"
return time_string
class DateTime:
@staticmethod
def short(time_in):
now = datetime.datetime.now()
if time_in.day != now.day or time_in.year != now.year:
return time_in.strftime("%Y/%m/%d @ %#I:%M:%S %p").strip()
else:
return time_in.strftime("%#I:%M:%S %p").strip()
@staticmethod
def long(time_in):
now = datetime.datetime.now()
if time_in.day != now.day or time_in.year != now.year:
return time_in.strftime("%A, %B %#d, %Y @ %#I:%M:%S %p %Z").strip()
else:
return time_in.strftime("%#I:%M:%S %p %Z").strip()
class Eta:
def __init__(self, total_items, start_time=None, verbose=False):
if start_time is None:
self.start_time = datetime.datetime.now()
else:
self.start_time = start_time
self.total_items = total_items
self.verbose = verbose
def set_verbose(self, verbose):
self.verbose = verbose
def set_total_items(self, total_items):
self.total_items = total_items
def get_time_taken(self, current_time=None):
if current_time is None:
current_time = datetime.datetime.now()
return current_time - self.start_time
def get_eta(self, current_item_index):
if current_item_index < 1:
raise ValueError("Unable to compute ETA for the first item (infinite time)")
if current_item_index > self.total_items:
raise IndexError("Item index is larger than the total items")
current_time = datetime.datetime.now()
time_taken = self.get_time_taken(current_time)
percent_done = current_item_index / (self.total_items - 1)
progress_scale = (1 - percent_done) / percent_done
eta_diff = time_taken * progress_scale
eta = current_time + eta_diff
return {
"eta": eta,
"difference": eta_diff
}
def get_start_time(self):
return self.start_time
def get_time_taken_string(self, current_time=None):
if current_time is None:
current_time = datetime.datetime.now()
time_taken = self.get_time_taken(current_time)
if self.verbose:
return TimeString.TimeDelta.long(time_taken)
else:
return TimeString.TimeDelta.short(time_taken)
def get_start_time_string(self):
if self.verbose:
return TimeString.DateTime.long(self.start_time)
else:
return TimeString.DateTime.short(self.start_time)
def get_time_remaining_string(self, current_item_index):
eta = self.get_eta(current_item_index)
if self.verbose:
difference_string = TimeString.TimeDelta.long(eta["difference"])
eta_string = TimeString.DateTime.long(eta["eta"])
else:
difference_string = TimeString.TimeDelta.short(eta["difference"])
eta_string = TimeString.DateTime.short(eta["eta"])
return f"Time remaining: {difference_string} | ETA: {eta_string}"
def get_eta_string(self, current_item_index):
eta = self.get_eta(current_item_index)
if self.verbose:
return TimeString.DateTime.long(eta["eta"])
else:
return TimeString.DateTime.short(eta["eta"])
def get_difference_string(self, current_item_index):
eta = self.get_eta(current_item_index)
if self.verbose:
return TimeString.TimeDelta.long(eta["difference"])
else:
return TimeString.TimeDelta.short(eta["difference"])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment