This repository was archived by the owner on May 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 278
/
Copy pathparse_time.py
74 lines (57 loc) · 1.78 KB
/
parse_time.py
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
import re
from datetime import datetime, timedelta
from difflib import SequenceMatcher
class ParseError(ValueError):
pass
TIME_UNITS = dict(
seconds="seconds",
minutes="minutes",
hours="hours",
days="days",
weeks="weeks",
months="months",
years="years",
# Shortcuts
s="seconds",
min="minutes",
h="hours",
d="days",
w="weeks",
mon="months",
y="years",
)
EXTRAPOLATED = {"months": (30, "days"), "years": (365, "days")}
assert set(EXTRAPOLATED) <= set(TIME_UNITS)
TIME_RE = re.compile(r"(\d+)([a-z]+)")
UNITS_STR = ", ".join(sorted(TIME_UNITS.keys()))
def string_similarity(a, b) -> SequenceMatcher:
return SequenceMatcher(None, a, b).ratio()
def parse_time_atom(count, unit):
count = int(count)
try:
unit = TIME_UNITS[unit]
except KeyError:
most_similar = max(TIME_UNITS, key=lambda k: string_similarity(k, unit))
raise ParseError(
f"'{unit}' is not a recognized time unit. Did you mean '{most_similar}'?" f"\nSupported units: {UNITS_STR}"
)
if unit in EXTRAPOLATED:
mul, unit = EXTRAPOLATED[unit]
count *= mul
return count, unit
def parse_time_delta(t: str) -> timedelta:
time_dict = {}
while t:
m = TIME_RE.match(t)
if not m:
raise ParseError(f"Cannot parse '{t}': Not a recognized time delta")
count, unit = parse_time_atom(*m.groups())
if unit in time_dict:
raise ParseError(f"Time unit {unit} specified more than once")
time_dict[unit] = count
t = t[m.end() :]
if not time_dict:
raise ParseError("No time difference specified")
return timedelta(**time_dict)
def parse_time_before(time: datetime, delta: str) -> datetime:
return time - parse_time_delta(delta)