summaryrefslogtreecommitdiffstats
path: root/src/python-common/ceph/utils.py
blob: 643be06580b6194d3c03e3b660569598520bec0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import datetime
import re
import string

from typing import Optional


def datetime_now() -> datetime.datetime:
    """
    Return the current local date and time.
    :return: Returns an aware datetime object of the current date
        and time.
    """
    return datetime.datetime.now(tz=datetime.timezone.utc)


def datetime_to_str(dt: datetime.datetime) -> str:
    """
    Convert a datetime object into a ISO 8601 string, e.g.
    '2019-04-24T17:06:53.039991Z'.
    :param dt: The datetime object to process.
    :return: Return a string representing the date in
        ISO 8601 (timezone=UTC).
    """
    return dt.astimezone(tz=datetime.timezone.utc).strftime(
        '%Y-%m-%dT%H:%M:%S.%fZ')


def str_to_datetime(string: str) -> datetime.datetime:
    """
    Convert an ISO 8601 string into a datetime object.
    The following formats are supported:

    - 2020-03-03T09:21:43.636153304Z
    - 2020-03-03T15:52:30.136257504-0600
    - 2020-03-03T15:52:30.136257504

    :param string: The string to parse.
    :return: Returns an aware datetime object of the given date
        and time string.
    :raises: :exc:`~exceptions.ValueError` for an unknown
        datetime string.
    """
    fmts = [
        '%Y-%m-%dT%H:%M:%S.%f',
        '%Y-%m-%dT%H:%M:%S.%f%z'
    ]

    # In *all* cases, the 9 digit second precision is too much for
    # Python's strptime. Shorten it to 6 digits.
    p = re.compile(r'(\.[\d]{6})[\d]*')
    string = p.sub(r'\1', string)

    # Replace trailing Z with -0000, since (on Python 3.6.8) it
    # won't parse.
    if string and string[-1] == 'Z':
        string = string[:-1] + '-0000'

    for fmt in fmts:
        try:
            dt = datetime.datetime.strptime(string, fmt)
            # Make sure the datetime object is aware (timezone is set).
            # If not, then assume the time is in UTC.
            if dt.tzinfo is None:
                dt = dt.replace(tzinfo=datetime.timezone.utc)
            return dt
        except ValueError:
            pass

    raise ValueError("Time data {} does not match one of the formats {}".format(
        string, str(fmts)))


def parse_timedelta(delta: str) -> Optional[datetime.timedelta]:
    """
    Returns a timedelta object represents a duration, the difference
    between two dates or times.

    >>> parse_timedelta('foo')

    >>> parse_timedelta('2d') == datetime.timedelta(days=2)
    True

    >>> parse_timedelta("4w") == datetime.timedelta(days=28)
    True

    >>> parse_timedelta("5s") == datetime.timedelta(seconds=5)
    True

    >>> parse_timedelta("-5s") == datetime.timedelta(days=-1, seconds=86395)
    True

    :param delta: The string to process, e.g. '2h', '10d', '30s'.
    :return: The `datetime.timedelta` object or `None` in case of
        a parsing error.
    """
    parts = re.match(r'(?P<seconds>-?\d+)s|'
                     r'(?P<minutes>-?\d+)m|'
                     r'(?P<hours>-?\d+)h|'
                     r'(?P<days>-?\d+)d|'
                     r'(?P<weeks>-?\d+)w$',
                     delta,
                     re.IGNORECASE)
    if not parts:
        return None
    parts = parts.groupdict()
    args = {name: int(param) for name, param in parts.items() if param}
    return datetime.timedelta(**args)


def is_hex(s: str, strict: bool = True) -> bool:
    """Simple check that a string contains only hex chars"""
    try:
        int(s, 16)
    except ValueError:
        return False

    # s is multiple chars, but we should catch a '+/-' prefix too.
    if strict:
        if s[0] not in string.hexdigits:
            return False

    return True