codekingpro/portable-devtools
114k
1import datetime2import re3 4 5class Frequencies:6 @staticmethod7 def hourly(t):8 dt = t + datetime.timedelta(hours=1)9 return dt.replace(minute=0, second=0, microsecond=0)10 11 @staticmethod12 def daily(t):13 dt = t + datetime.timedelta(days=1)14 return dt.replace(hour=0, minute=0, second=0, microsecond=0)15 16 @staticmethod17 def weekly(t):18 dt = t + datetime.timedelta(days=7 - t.weekday())19 return dt.replace(hour=0, minute=0, second=0, microsecond=0)20 21 @staticmethod22 def monthly(t):23 if t.month == 12:24 y, m = t.year + 1, 125 else:26 y, m = t.year, t.month + 127 return t.replace(year=y, month=m, day=1, hour=0, minute=0, second=0, microsecond=0)28 29 @staticmethod30 def yearly(t):31 y = t.year + 132 return t.replace(year=y, month=1, day=1, hour=0, minute=0, second=0, microsecond=0)33 34 35def parse_size(size):36 size = size.strip()37 reg = re.compile(r"([e\+\-\.\d]+)\s*([kmgtpezy])?(i)?(b)", flags=re.I)38 39 match = reg.fullmatch(size)40 41 if not match:42 return None43 44 s, u, i, b = match.groups()45 46 try:47 s = float(s)48 except ValueError as e:49 raise ValueError("Invalid float value while parsing size: '%s'" % s) from e50 51 u = "kmgtpezy".index(u.lower()) + 1 if u else 052 i = 1024 if i else 100053 b = {"b": 8, "B": 1}[b] if b else 154 return s * i**u / b55 56 57def parse_duration(duration):58 duration = duration.strip()59 reg = r"(?:([e\+\-\.\d]+)\s*([a-z]+)[\s\,]*)"60 61 units = [62 ("y|years?", 31536000),63 ("months?", 2628000),64 ("w|weeks?", 604800),65 ("d|days?", 86400),66 ("h|hours?", 3600),67 ("min(?:ute)?s?", 60),68 ("s|sec(?:ond)?s?", 1), # spellchecker: disable-line69 ("ms|milliseconds?", 0.001),70 ("us|microseconds?", 0.000001),71 ]72 73 if not re.fullmatch(reg + "+", duration, flags=re.I):74 return None75 76 seconds = 077 78 for value, unit in re.findall(reg, duration, flags=re.I):79 try:80 value = float(value)81 except ValueError as e:82 raise ValueError("Invalid float value while parsing duration: '%s'" % value) from e83 84 try:85 unit = next(u for r, u in units if re.fullmatch(r, unit, flags=re.I))86 except StopIteration:87 raise ValueError("Invalid unit value while parsing duration: '%s'" % unit) from None88 89 seconds += value * unit90 91 return datetime.timedelta(seconds=seconds)92 93 94def parse_frequency(frequency):95 frequencies = {96 "hourly": Frequencies.hourly,97 "daily": Frequencies.daily,98 "weekly": Frequencies.weekly,99 "monthly": Frequencies.monthly,100 "yearly": Frequencies.yearly,101 }102 frequency = frequency.strip().lower()103 return frequencies.get(frequency, None)104 105 106def parse_day(day):107 days = {108 "monday": 0,109 "tuesday": 1,110 "wednesday": 2,111 "thursday": 3,112 "friday": 4,113 "saturday": 5,114 "sunday": 6,115 }116 day = day.strip().lower()117 if day in days:118 return days[day]119 if day.startswith("w") and day[1:].isdigit():120 day = int(day[1:])121 if not 0 <= day < 7:122 raise ValueError("Invalid weekday value while parsing day (expected [0-6]): '%d'" % day)123 else:124 day = None125 126 return day127 128 129def parse_time(time):130 time = time.strip()131 reg = re.compile(r"^[\d\.\:]+\s*(?:[ap]m)?$", flags=re.I)132 133 if not reg.match(time):134 return None135 136 formats = [137 "%H",138 "%H:%M",139 "%H:%M:%S",140 "%H:%M:%S.%f",141 "%I %p",142 "%I:%M %S",143 "%I:%M:%S %p",144 "%I:%M:%S.%f %p",145 ]146 147 for format_ in formats:148 try:149 dt = datetime.datetime.strptime(time, format_)150 except ValueError:151 pass152 else:153 return dt.time()154 155 raise ValueError("Unrecognized format while parsing time: '%s'" % time)156 157 158def parse_daytime(daytime):159 daytime = daytime.strip()160 reg = re.compile(r"^(.*?)\s+at\s+(.*)$", flags=re.I)161 162 match = reg.match(daytime)163 if match:164 day, time = match.groups()165 else:166 day = time = daytime167 168 try:169 parsed_day = parse_day(day)170 if match and parsed_day is None:171 raise ValueError("Unparsable day")172 except ValueError as e:173 raise ValueError("Invalid day while parsing daytime: '%s'" % day) from e174 175 try:176 parsed_time = parse_time(time)177 if match and parsed_time is None:178 raise ValueError("Unparsable time")179 except ValueError as e:180 raise ValueError("Invalid time while parsing daytime: '%s'" % time) from e181 182 if parsed_day is None and parsed_time is None:183 return None184 185 return parsed_day, parsed_time186 