1 | """ |
---|
2 | Time formatting utilities. |
---|
3 | |
---|
4 | ISO-8601: |
---|
5 | http://www.cl.cam.ac.uk/~mgk25/iso-time.html |
---|
6 | """ |
---|
7 | |
---|
8 | import calendar, datetime, re, time |
---|
9 | |
---|
10 | from typing import Optional |
---|
11 | |
---|
12 | def format_time(t): |
---|
13 | return time.strftime("%Y-%m-%d %H:%M:%S", t) |
---|
14 | |
---|
15 | def iso_utc_date( |
---|
16 | now: Optional[float] = None, |
---|
17 | t=time.time |
---|
18 | ) -> str: |
---|
19 | if now is None: |
---|
20 | now = t() |
---|
21 | return datetime.datetime.utcfromtimestamp(now).isoformat()[:10] |
---|
22 | |
---|
23 | def iso_utc( |
---|
24 | now: Optional[float] = None, |
---|
25 | sep: str = '_', |
---|
26 | t=time.time |
---|
27 | ) -> str: |
---|
28 | if now is None: |
---|
29 | now = t() |
---|
30 | sep = str(sep) # should already be a str |
---|
31 | return datetime.datetime.utcfromtimestamp(now).isoformat(sep) |
---|
32 | |
---|
33 | def iso_utc_time_to_seconds(isotime, _conversion_re=re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})[T_ ](?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(?P<subsecond>\.\d+)?")): |
---|
34 | """ |
---|
35 | The inverse of iso_utc(). |
---|
36 | |
---|
37 | Real ISO-8601 is "2003-01-08T06:30:59". We also accept the widely |
---|
38 | used variants "2003-01-08_06:30:59" and "2003-01-08 06:30:59". |
---|
39 | """ |
---|
40 | m = _conversion_re.match(isotime) |
---|
41 | if not m: |
---|
42 | raise ValueError(isotime, "not a complete ISO8601 timestamp") |
---|
43 | year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day')) |
---|
44 | hour, minute, second = int(m.group('hour')), int(m.group('minute')), int(m.group('second')) |
---|
45 | subsecstr = m.group('subsecond') |
---|
46 | if subsecstr: |
---|
47 | subsecfloat = float(subsecstr) |
---|
48 | else: |
---|
49 | subsecfloat = 0 |
---|
50 | |
---|
51 | return calendar.timegm( (year, month, day, hour, minute, second, 0, 1, 0) ) + subsecfloat |
---|
52 | |
---|
53 | def parse_duration(s): |
---|
54 | orig = s |
---|
55 | unit = None |
---|
56 | DAY = 24*60*60 |
---|
57 | MONTH = 31*DAY |
---|
58 | YEAR = 365*DAY |
---|
59 | if s.endswith("s"): |
---|
60 | s = s[:-1] |
---|
61 | if s.endswith("day"): |
---|
62 | unit = DAY |
---|
63 | s = s[:-len("day")] |
---|
64 | elif s.endswith("month"): |
---|
65 | unit = MONTH |
---|
66 | s = s[:-len("month")] |
---|
67 | elif s.endswith("mo"): |
---|
68 | unit = MONTH |
---|
69 | s = s[:-len("mo")] |
---|
70 | elif s.endswith("year"): |
---|
71 | unit = YEAR |
---|
72 | s = s[:-len("YEAR")] |
---|
73 | else: |
---|
74 | raise ValueError("no unit (like day, month, or year) in '%s'" % orig) |
---|
75 | s = s.strip() |
---|
76 | return int(s) * unit |
---|
77 | |
---|
78 | def parse_date(s): |
---|
79 | # return seconds-since-epoch for the UTC midnight that starts the given |
---|
80 | # day |
---|
81 | return int(iso_utc_time_to_seconds(s + "T00:00:00")) |
---|
82 | |
---|
83 | def format_delta(time_1, time_2): |
---|
84 | if time_1 is None: |
---|
85 | return "N/A" |
---|
86 | if time_1 > time_2: |
---|
87 | return '-' |
---|
88 | delta = int(time_2 - time_1) |
---|
89 | seconds = delta % 60 |
---|
90 | delta -= seconds |
---|
91 | minutes = (delta // 60) % 60 |
---|
92 | delta -= minutes * 60 |
---|
93 | hours = delta // (60*60) % 24 |
---|
94 | delta -= hours * 24 |
---|
95 | days = delta // (24*60*60) |
---|
96 | if not days: |
---|
97 | if not hours: |
---|
98 | if not minutes: |
---|
99 | return "%ss" % (seconds) |
---|
100 | else: |
---|
101 | return "%sm %ss" % (minutes, seconds) |
---|
102 | else: |
---|
103 | return "%sh %sm %ss" % (hours, minutes, seconds) |
---|
104 | else: |
---|
105 | return "%sd %sh %sm %ss" % (days, hours, minutes, seconds) |
---|
106 | |
---|