Completed
Push — master ( 265317...7ca1a7 )
by W
06:30
created

st2common.util.parse()   A

Complexity

Conditions 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 21
rs 9.3143
cc 2
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
"""
17
ISO8601 date related utility functions.
18
"""
19
20
import re
21
import datetime
22
23
from st2common.util import date as date_utils
24
25
__all__ = [
26
    'format',
27
    'validate',
28
    'parse'
29
]
30
31
32
ISO8601_FORMAT = '%Y-%m-%dT%H:%M:%S'
33
ISO8601_FORMAT_MICROSECOND = '%Y-%m-%dT%H:%M:%S.%f'
34
ISO8601_UTC_REGEX = \
35
    '^\d{4}\-\d{2}\-\d{2}(\s|T)\d{2}:\d{2}:\d{2}(\.\d{3,6})?(Z|\+00|\+0000|\+00:00)$'
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \d was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \- was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \s was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \. was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
Bug introduced by
A suspicious escape sequence \+ was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
36
37
38
def format(dt, usec=True, offset=True):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in format.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
39
    """
40
    Format a provided datetime object and return ISO8601 string.
41
42
    :type dt: ``datetime.datetime``
43
    """
44
    # pylint: disable=no-member
45
    if isinstance(dt, basestring):
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'basestring'
Loading history...
46
        dt = parse(dt)
47
    fmt = ISO8601_FORMAT_MICROSECOND if usec else ISO8601_FORMAT
48
    if offset:
49
        ost = dt.strftime('%z')
50
        ost = (ost[:3] + ':' + ost[3:]) if ost else '+00:00'
51
    else:
52
        tz = dt.tzinfo.tzname(dt) if dt.tzinfo else 'UTC'
53
        ost = 'Z' if tz == 'UTC' else tz
54
    return dt.strftime(fmt) + ost
55
56
57
def validate(value, raise_exception=True):
58
    if (isinstance(value, datetime.datetime) or
59
            (type(value) in [str, unicode] and re.match(ISO8601_UTC_REGEX, value))):
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'unicode'
Loading history...
60
        return True
61
    if raise_exception:
62
        raise ValueError('Datetime value does not match expected format.')
63
    return False
64
65
66
def parse(value, preserve_original_tz=False, validate_value=True):
67
    """
68
    Parse date in the ISO8601 format and return a time-zone aware datetime object.
69
70
    :param value: Date in ISO8601 format.
71
    :type value: ``str``
72
73
    :param preserve_original_tz: True to preserve the original timezone - by default result is
74
                                 converted into UTC.
75
    :type preserve_original_tz: ``boolean``
76
77
    :param validate_value: True to validate that the date is in the ISO8601 format.
78
    :type validate_value: ``boolean``
79
80
    :rtype: ``datetime.datetime``
81
    """
82
    if validate_value:
83
        validate(value, raise_exception=True)
84
85
    dt = date_utils.parse(value=value, preserve_original_tz=preserve_original_tz)
86
    return dt
87