Skip to content

Instantly share code, notes, and snippets.

@Dan6erbond
Last active September 10, 2020 11:45
Show Gist options
  • Save Dan6erbond/214e329c7a63cfc22ae000cdf209b425 to your computer and use it in GitHub Desktop.
Save Dan6erbond/214e329c7a63cfc22ae000cdf209b425 to your computer and use it in GitHub Desktop.
A Python script to parse various time formats specifying duration.
import cProfile
import random
import re
units = {
"days": {
"seconds": 60 * 60 * 24,
"aliases": ["d"],
},
"hours": {
"seconds": 60 * 60,
"aliases": ["h", "hr", "hrs"],
},
"minutes": {
"seconds": 60,
"aliases": ["m", "min", "mins"],
},
"seconds": {
"seconds": 1,
"aliases": ["s", "sec", "secs", ""],
},
}.items()
RE = re.compile(r"(?:(?P<amt>\d{1,3}(?:\.\d{1,2})?)\s?(?P<unit>[a-zA-Z]*)?)")
def get_seconds(_input):
matches = RE.finditer(_input)
def next_match(matches):
try:
match = next(matches)
except BaseException:
return None, None
amt, unit = match.group("amt"), match.group("unit")
if not amt:
return next_match(matches)
return amt, unit
amt, unit = next_match(matches)
seconds = 0
for name, config in units:
found = False
if unit == name or unit in config["aliases"]:
seconds += float(amt) * config["seconds"]
found = True
if amt and found:
amt, unit = next_match(matches)
elif not amt:
break
return seconds
def get_seconds_optimized(_input):
matches = list(RE.finditer(_input))
index = 0
seconds = 0
for name, config in units:
amt = 0
while not amt:
try:
index += 1
match = matches[index]
amt = match.group("amt")
if amt:
unit = match.group("unit")
except BaseException:
break
if not amt:
break
if unit == name or unit in config["aliases"]:
seconds += float(amt) * config["seconds"]
return seconds
AMY_RE = re.compile(r"(?:(?P<days>\d+(?:.\d+)?)\s?(?:d|days))?\s?(?:(?P<hours>\d+(?:.\d+)?)\s?(?:h|hr|hours))?\s?(?:(?P<mins>\d+(?:.\d+)?)\s?(?:m|minutes))?\s?(?:(?P<secs>\d+(?:.\d+)?)\s?(?:s|seconds|sec)?)?")
def get_seconds_the_amy_way(_input):
match = AMY_RE.match(_input)
seconds = 0
if match.group("days"):
seconds += float(match.group("days")) * 60 * 60 * 24
if match.group("hours"):
seconds += float(match.group("hours")) * 60 * 60
if match.group("mins"):
seconds += float(match.group("mins")) * 60
if match.group("secs"):
seconds += float(match.group("secs"))
values = list()
for i in range(5000):
parts = list()
res = 0
for name, config in units:
if random.random() < 0.2:
continue
amt = random.random() * 365
amt_str = "{:.2f}".format(amt)
res += amt * config["seconds"]
unit_index = random.randint(0, len(config["aliases"]))
unit = name if unit_index == len(config["aliases"]) else config["aliases"][unit_index]
parts.append(amt_str + " " + unit)
values.append((" ".join(parts), float("{:.2f}".format(res))))
pr = cProfile.Profile()
pr.enable()
correct = 0
for _in, res in values:
r = get_seconds(_in)
if r is r or int(r) == int(res):
assert True
correct += 1
else:
assert False
print(f"Done Ravi's way, accuracy: {int(correct / len(values) * 100)}%")
pr.disable()
pr.print_stats()
pr = cProfile.Profile()
pr.enable()
correct = 0
for _in, res in values:
r = get_seconds_optimized(_in)
if r is r or int(r) == int(res):
assert True
correct += 1
else:
assert False
print(f"Done the optimized way, accuracy: {int(correct / len(values) * 100)}%")
pr.disable()
pr.print_stats()
pr = cProfile.Profile()
pr.enable()
correct = 0
for _in, res in values:
res = get_seconds_the_amy_way(_in)
if r is r or int(r) == int(res):
assert True
correct += 1
else:
assert False
print(f"Done the Amy way, accuracy: {int(correct / len(values) * 100)}%")
pr.disable()
pr.print_stats()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment