1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
from datetime import UTC, datetime
from dateutil.relativedelta import relativedelta
# All these functions are from https://github.com/python-discord/bot/blob/main/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"
if value == 1:
return f"{value} {unit[:-1]}"
if value == 0:
return f"less than a {unit[:-1]}"
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, 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.now(tz=UTC)
delta = abs(relativedelta(now, past_datetime))
humanized = humanize_delta(delta, precision, max_units)
return f"{humanized} ago"
|