codekingpro/portable-devtools
115k
1# -*- coding: UTF-8 -*-2 3import re4import datetime5 6_nanosecond_size = 17_microsecond_size = 1000 * _nanosecond_size8_millisecond_size = 1000 * _microsecond_size9_second_size = 1000 * _millisecond_size10_minute_size = 60 * _second_size11_hour_size = 60 * _minute_size12_day_size = 24 * _hour_size13_week_size = 7 * _day_size14_month_size = 30 * _day_size15_year_size = 365 * _day_size16 17units = {18 "ns": _nanosecond_size,19 "us": _microsecond_size,20 "µs": _microsecond_size,21 "μs": _microsecond_size,22 "ms": _millisecond_size,23 "s": _second_size,24 "m": _minute_size,25 "h": _hour_size,26 "d": _day_size,27 "w": _week_size,28 "mm": _month_size,29 "y": _year_size,30}31 32_duration_re = re.compile(r'([\d\.]+)([a-zµμ]+)')33 34 35class DurationError(ValueError):36 """duration error"""37 38 39def from_str(duration):40 """Parse a duration string to a datetime.timedelta"""41 42 original = duration43 44 if duration in ("0", "+0", "-0"):45 return datetime.timedelta()46 47 sign = 148 if duration and duration[0] in '+-':49 if duration[0] == '-':50 sign = -151 duration = duration[1:]52 53 matches = list(_duration_re.finditer(duration))54 if not matches:55 raise DurationError("Invalid duration {}".format(original))56 if matches[0].start() != 0 or matches[-1].end() != len(duration):57 raise DurationError(58 'Extra chars at start or end of duration {}'.format(original))59 60 total = 061 for match in matches:62 value, unit = match.groups()63 if unit not in units:64 raise DurationError(65 "Unknown unit {} in duration {}".format(unit, original))66 try:67 total += float(value) * units[unit]68 except Exception:69 raise DurationError(70 "Invalid value {} in duration {}".format(value, original))71 72 microseconds = total / _microsecond_size73 return datetime.timedelta(microseconds=sign * microseconds)74 75def to_str(delta, extended=False):76 """Format a datetime.timedelta to a duration string"""77 78 total_seconds = delta.total_seconds()79 sign = "-" if total_seconds < 0 else ""80 nanoseconds = round(abs(total_seconds * _second_size), 0)81 82 if abs(total_seconds) < 1:83 result_str = _to_str_small(nanoseconds, extended)84 else:85 result_str = _to_str_large(nanoseconds, extended)86 87 return "{}{}".format(sign, result_str)88 89 90def _to_str_small(nanoseconds, extended):91 92 result_str = ""93 94 if not nanoseconds:95 return "0"96 97 milliseconds = int(nanoseconds / _millisecond_size)98 if milliseconds:99 nanoseconds -= _millisecond_size * milliseconds100 result_str += "{:g}ms".format(milliseconds)101 102 microseconds = int(nanoseconds / _microsecond_size)103 if microseconds:104 nanoseconds -= _microsecond_size * microseconds105 result_str += "{:g}us".format(microseconds)106 107 if nanoseconds:108 result_str += "{:g}ns".format(nanoseconds)109 110 return result_str111 112 113def _to_str_large(nanoseconds, extended):114 115 result_str = ""116 117 if extended:118 119 years = int(nanoseconds / _year_size)120 if years:121 nanoseconds -= _year_size * years122 result_str += "{:g}y".format(years)123 124 months = int(nanoseconds / _month_size)125 if months:126 nanoseconds -= _month_size * months127 result_str += "{:g}mm".format(months)128 129 days = int(nanoseconds / _day_size)130 if days:131 nanoseconds -= _day_size * days132 result_str += "{:g}d".format(days)133 134 hours = int(nanoseconds / _hour_size)135 if hours:136 nanoseconds -= _hour_size * hours137 result_str += "{:g}h".format(hours)138 139 minutes = int(nanoseconds / _minute_size)140 if minutes:141 nanoseconds -= _minute_size * minutes142 result_str += "{:g}m".format(minutes)143 144 seconds = float(nanoseconds) / float(_second_size)145 if seconds:146 nanoseconds -= _second_size * seconds147 result_str += "{:g}s".format(seconds)148 149 return result_str150 