1# -*- coding: utf-8 -*-
2"""Date and time utilities for acme2certifier"""
3
4import calendar
5import datetime
6import re
7from typing import Union
8
9from dateutil.parser import parse
10
11_DURATION_RE = re.compile(r"^(\d+)\s*([smhdw]?)$", re.IGNORECASE)
12_DURATION_UNIT_SECONDS = {
13 "": 1,
14 "s": 1,
15 "m": 60,
16 "h": 3600,
17 "d": 86400,
18 "w": 604800,
19}
20
21
22def duration_to_seconds(value: Union[str, int]) -> int:
23 """Parse a duration into seconds.
24
25 Accepts plain integers (seconds) or strings with an optional unit suffix:
26 ``s`` (seconds), ``m`` (minutes), ``h`` (hours), ``d`` (days), ``w`` (weeks).
27 Examples: ``172800``, ``"2d"``, ``"48h"``, ``"30m"``.
28 """
29 if isinstance(value, bool):
30 raise ValueError(f"invalid duration: {value!r}")
31 if isinstance(value, int):
32 if value <= 0:
33 raise ValueError("duration must be positive")
34 return value
35
36 text = str(value).strip()
37 if not text:
38 raise ValueError("empty duration")
39
40 match = _DURATION_RE.match(text)
41 if not match:
42 raise ValueError(f"invalid duration: {value!r}")
43
44 amount = int(match.group(1))
45 unit = match.group(2).lower()
46 seconds = amount * _DURATION_UNIT_SECONDS[unit]
47 if seconds <= 0:
48 raise ValueError("duration must be positive")
49 return seconds
50
51
52def uts_now():
53 """unixtimestamp in utc"""
54 return calendar.timegm(datetime.datetime.now(datetime.timezone.utc).utctimetuple())
55
56
57def uts_to_date_utc(uts: int, tformat: str = "%Y-%m-%dT%H:%M:%SZ") -> str:
58 """convert unix timestamp to date format"""
59 return datetime.datetime.fromtimestamp(int(uts), tz=datetime.timezone.utc).strftime(
60 tformat
61 )
62
63
64def date_to_uts_utc(date_human: str, _tformat: str = "%Y-%m-%dT%H:%M:%S") -> int:
65 """convert date to unix timestamp"""
66 if isinstance(date_human, datetime.datetime):
67 # we already got an datetime object as input
68 result = calendar.timegm(date_human.timetuple())
69 else:
70 result = int(calendar.timegm(parse(date_human).timetuple()))
71 return result
72
73
74def date_to_datestr(
75 date: datetime.datetime, tformat: str = "%Y-%m-%dT%H:%M:%SZ"
76) -> str:
77 """convert dateobj to datestring"""
78 try:
79 result = date.strftime(tformat)
80 except Exception:
81 result = None
82 return result
83
84
85def datestr_to_date(datestr: str, tformat: str = "%Y-%m-%dT%H:%M:%S") -> str:
86 """convert datestr to dateobj"""
87 try:
88 result = datetime.datetime.strptime(datestr, tformat)
89 except Exception:
90 result = None
91 return result