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

1# ~~ Dates:Library~~ 

2 

3from portality.core import app 

4 

5from datetime import datetime, timedelta 

6from random import randint 

7import math 

8 

9 

10def parse(s, format=None, guess=True): 

11 s = s.strip() 

12 

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 

19 

20 for f in app.config.get("DATE_FORMATS", []): 

21 try: 

22 return datetime.strptime(s, f) 

23 except ValueError as e: 

24 pass 

25 

26 raise ValueError("Unable to parse {x} with any known format".format(x=s)) 

27 

28 

29def format(d, format=None): 

30 if format is None: 

31 format = app.config.get("DEFAULT_DATE_FORMAT") 

32 return str(d.strftime(format)) 

33 

34 

35def reformat(s, in_format=None, out_format=None): 

36 return format(parse(s, format=in_format), format=out_format) 

37 

38 

39def now(): 

40 return format(datetime.utcnow()) 

41 

42 

43def now_with_microseconds(): 

44 return format(datetime.utcnow(), format="%Y-%m-%dT%H:%M:%S.%fZ") 

45 

46 

47def today(): 

48 return format(datetime.utcnow(), format="%Y-%m-%d") 

49 

50 

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) 

60 

61 span = int((to - fro).total_seconds()) 

62 s = randint(0, span) 

63 return format(to - timedelta(seconds=s)) 

64 

65 

66def before(timestamp, seconds): 

67 return timestamp - timedelta(seconds=seconds) 

68 

69 

70def after(timestamp, seconds): 

71 return timestamp + timedelta(seconds=seconds) 

72 

73 

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) 

81 

82 

83def day_ranges(fro, to): 

84 aday = timedelta(days=1) 

85 

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) 

89 

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))] 

94 

95 # start the range off with the remainder of the first day 

96 ranges = [(format(fro), format(next_midnight))] 

97 

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 

109 

110 return ranges 

111 

112 

113def human_date(stamp, string_format="%d %B %Y"): 

114 return reformat(stamp, out_format=string_format)