]> git.proxmox.com Git - ceph.git/blame - ceph/src/python-common/ceph/utils.py
import quincy beta 17.1.0
[ceph.git] / ceph / src / python-common / ceph / utils.py
CommitLineData
adb31ebb
TL
1import datetime
2import re
20effc67 3import string
adb31ebb 4
522d829b
TL
5from typing import Optional
6
adb31ebb
TL
7
8def datetime_now() -> datetime.datetime:
9 """
10 Return the current local date and time.
11 :return: Returns an aware datetime object of the current date
12 and time.
13 """
14 return datetime.datetime.now(tz=datetime.timezone.utc)
15
16
17def datetime_to_str(dt: datetime.datetime) -> str:
18 """
19 Convert a datetime object into a ISO 8601 string, e.g.
20 '2019-04-24T17:06:53.039991Z'.
21 :param dt: The datetime object to process.
22 :return: Return a string representing the date in
23 ISO 8601 (timezone=UTC).
24 """
25 return dt.astimezone(tz=datetime.timezone.utc).strftime(
26 '%Y-%m-%dT%H:%M:%S.%fZ')
27
28
29def str_to_datetime(string: str) -> datetime.datetime:
30 """
31 Convert an ISO 8601 string into a datetime object.
32 The following formats are supported:
33
34 - 2020-03-03T09:21:43.636153304Z
35 - 2020-03-03T15:52:30.136257504-0600
36 - 2020-03-03T15:52:30.136257504
37
38 :param string: The string to parse.
39 :return: Returns an aware datetime object of the given date
40 and time string.
41 :raises: :exc:`~exceptions.ValueError` for an unknown
42 datetime string.
43 """
44 fmts = [
45 '%Y-%m-%dT%H:%M:%S.%f',
46 '%Y-%m-%dT%H:%M:%S.%f%z'
47 ]
48
49 # In *all* cases, the 9 digit second precision is too much for
50 # Python's strptime. Shorten it to 6 digits.
51 p = re.compile(r'(\.[\d]{6})[\d]*')
52 string = p.sub(r'\1', string)
53
54 # Replace trailing Z with -0000, since (on Python 3.6.8) it
55 # won't parse.
56 if string and string[-1] == 'Z':
57 string = string[:-1] + '-0000'
58
59 for fmt in fmts:
60 try:
61 dt = datetime.datetime.strptime(string, fmt)
62 # Make sure the datetime object is aware (timezone is set).
63 # If not, then assume the time is in UTC.
64 if dt.tzinfo is None:
65 dt = dt.replace(tzinfo=datetime.timezone.utc)
66 return dt
67 except ValueError:
68 pass
69
70 raise ValueError("Time data {} does not match one of the formats {}".format(
71 string, str(fmts)))
522d829b
TL
72
73
74def parse_timedelta(delta: str) -> Optional[datetime.timedelta]:
75 """
76 Returns a timedelta object represents a duration, the difference
77 between two dates or times.
78
79 >>> parse_timedelta('foo')
80
a4b75251
TL
81 >>> parse_timedelta('2d') == datetime.timedelta(days=2)
82 True
522d829b 83
a4b75251
TL
84 >>> parse_timedelta("4w") == datetime.timedelta(days=28)
85 True
522d829b 86
a4b75251
TL
87 >>> parse_timedelta("5s") == datetime.timedelta(seconds=5)
88 True
522d829b 89
a4b75251
TL
90 >>> parse_timedelta("-5s") == datetime.timedelta(days=-1, seconds=86395)
91 True
522d829b
TL
92
93 :param delta: The string to process, e.g. '2h', '10d', '30s'.
94 :return: The `datetime.timedelta` object or `None` in case of
95 a parsing error.
96 """
97 parts = re.match(r'(?P<seconds>-?\d+)s|'
98 r'(?P<minutes>-?\d+)m|'
99 r'(?P<hours>-?\d+)h|'
100 r'(?P<days>-?\d+)d|'
101 r'(?P<weeks>-?\d+)w$',
102 delta,
103 re.IGNORECASE)
104 if not parts:
105 return None
106 parts = parts.groupdict()
107 args = {name: int(param) for name, param in parts.items() if param}
108 return datetime.timedelta(**args)
20effc67
TL
109
110
111def is_hex(s: str, strict: bool = True) -> bool:
112 """Simple check that a string contains only hex chars"""
113 try:
114 int(s, 16)
115 except ValueError:
116 return False
117
118 # s is multiple chars, but we should catch a '+/-' prefix too.
119 if strict:
120 if s[0] not in string.hexdigits:
121 return False
122
123 return True