aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar AtieP <[email protected]>2020-09-28 16:54:08 +0200
committerGravatar AtieP <[email protected]>2020-09-28 16:54:08 +0200
commit734addece0491fe16656a787e5f032e8f7190168 (patch)
tree237c25c708fad92263209bd5e30cf04ca745b51c
parentMerge branch 'master' into master (diff)
Add time utils
-rw-r--r--bot/utils/time.py84
1 files changed, 84 insertions, 0 deletions
diff --git a/bot/utils/time.py b/bot/utils/time.py
new file mode 100644
index 00000000..3c57e126
--- /dev/null
+++ b/bot/utils/time.py
@@ -0,0 +1,84 @@
+import datetime
+
+from dateutil.relativedelta import relativedelta
+
+
+# All these functions are from https://github.com/python-discord/bot/blob/master/bot/utils/time.py
+def _stringify_time_unit(value: int, unit: str) -> str:
+ """
+ Returns a string to represent a value and time unit, ensuring that it uses the right plural form of the unit.
+
+ >>> _stringify_time_unit(1, "seconds")
+ "1 second"
+ >>> _stringify_time_unit(24, "hours")
+ "24 hours"
+ >>> _stringify_time_unit(0, "minutes")
+ "less than a minute"
+ """
+ if unit == "seconds" and value == 0:
+ return "0 seconds"
+ elif value == 1:
+ return f"{value} {unit[:-1]}"
+ elif value == 0:
+ return f"less than a {unit[:-1]}"
+ else:
+ return f"{value} {unit}"
+
+
+def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6) -> str:
+ """
+ Returns a human-readable version of the relativedelta.
+
+ precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").
+ max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).
+ """
+ if max_units <= 0:
+ raise ValueError("max_units must be positive")
+
+ units = (
+ ("years", delta.years),
+ ("months", delta.months),
+ ("days", delta.days),
+ ("hours", delta.hours),
+ ("minutes", delta.minutes),
+ ("seconds", delta.seconds),
+ )
+
+ # Add the time units that are >0, but stop at accuracy or max_units.
+ time_strings = []
+ unit_count = 0
+ for unit, value in units:
+ if value:
+ time_strings.append(_stringify_time_unit(value, unit))
+ unit_count += 1
+
+ if unit == precision or unit_count >= max_units:
+ break
+
+ # Add the 'and' between the last two units, if necessary
+ if len(time_strings) > 1:
+ time_strings[-1] = f"{time_strings[-2]} and {time_strings[-1]}"
+ del time_strings[-2]
+
+ # If nothing has been found, just make the value 0 precision, e.g. `0 days`.
+ if not time_strings:
+ humanized = _stringify_time_unit(0, precision)
+ else:
+ humanized = ", ".join(time_strings)
+
+ return humanized
+
+
+def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6) -> str:
+ """
+ Takes a datetime and returns a human-readable string that describes how long ago that datetime was.
+
+ precision specifies the smallest unit of time to include (e.g. "seconds", "minutes").
+ max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours).
+ """
+ now = datetime.datetime.utcnow()
+ delta = abs(relativedelta(now, past_datetime))
+
+ humanized = humanize_delta(delta, precision, max_units)
+
+ return f"{humanized} ago"