Coverage for portality/lib/dates.py: 73%
71 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-04 15:38 +0100
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-04 15:38 +0100
1# ~~ Dates:Library~~
3from portality.core import app
5from datetime import datetime, timedelta
6from random import randint
7import math
10def parse(s, format=None, guess=True):
11 s = s.strip()
13 if format is not None:
14 try:
15 return datetime.strptime(s, format)
16 except ValueError as e:
17 if not guess:
18 raise e
20 for f in app.config.get("DATE_FORMATS", []):
21 try:
22 return datetime.strptime(s, f)
23 except ValueError as e:
24 pass
26 raise ValueError("Unable to parse {x} with any known format".format(x=s))
29def format(d, format=None):
30 if format is None:
31 format = app.config.get("DEFAULT_DATE_FORMAT")
32 return str(d.strftime(format))
35def reformat(s, in_format=None, out_format=None):
36 return format(parse(s, format=in_format), format=out_format)
39def now():
40 return format(datetime.utcnow())
43def now_with_microseconds():
44 return format(datetime.utcnow(), format="%Y-%m-%dT%H:%M:%S.%fZ")
47def today():
48 return format(datetime.utcnow(), format="%Y-%m-%d")
51def random_date(fro=None, to=None):
52 if fro is None:
53 fro = parse("1970-01-01T00:00:00Z")
54 if isinstance(fro, str):
55 fro = parse(fro)
56 if to is None:
57 to = datetime.utcnow()
58 if isinstance(to, str):
59 to = parse(to)
61 span = int((to - fro).total_seconds())
62 s = randint(0, span)
63 return format(to - timedelta(seconds=s))
66def before(timestamp, seconds):
67 return timestamp - timedelta(seconds=seconds)
70def after(timestamp, seconds):
71 return timestamp + timedelta(seconds=seconds)
74def eta(since, sofar, total):
75 now = datetime.utcnow()
76 td = (now - since).total_seconds()
77 spr = float(td) / float(sofar)
78 alltime = int(math.ceil(total * spr))
79 fin = after(since, alltime)
80 return format(fin)
83def day_ranges(fro, to):
84 aday = timedelta(days=1)
86 # first, workout when the next midnight point is
87 next_day = fro + aday
88 next_midnight = datetime(next_day.year, next_day.month, next_day.day)
90 # in the degenerate case, to is before the next midnight, in which case they both
91 # fall within the one day range
92 if next_midnight > to:
93 return [(format(fro), format(to))]
95 # start the range off with the remainder of the first day
96 ranges = [(format(fro), format(next_midnight))]
98 # go through each day, adding to the range, until the next day is after
99 # the "to" date, then finish up and return
100 current = next_midnight
101 while True:
102 next = current + aday
103 if next > to:
104 ranges.append((format(current), format(to)))
105 break
106 else:
107 ranges.append((format(current), format(next)))
108 current = next
110 return ranges
113def human_date(stamp, string_format="%d %B %Y"):
114 return reformat(stamp, out_format=string_format)