Passed
Pull Request — master (#16)
by Ramon
51s
created

jsons._datetime_impl   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 46
dl 0
loc 109
rs 10
c 0
b 0
f 0
wmc 15

7 Functions

Rating   Name   Duplication   Size   Complexity  
A _datetime_utc_inst() 0 10 1
A get_offset_str() 0 9 2
A _timedelta_offset_str() 0 13 2
A _new_datetime() 0 12 1
A _datetime_offset_inst() 0 17 2
A get_datetime_inst() 0 9 2
A _datetime_offset_str() 0 17 5
1
"""
2
PRIVATE MODULE: do not import (from) it directly.
3
4
This module contains functionality for ``datetime`` related stuff.
5
"""
6
from datetime import datetime, timezone, timedelta, time, date
7
from typing import Union
8
9
10
def get_offset_str(obj: Union[datetime, timedelta]) -> str:
11
    """
12
    Return the textual offset of the given ``obj``.
13
    :param obj: a datetime or timedelta instance.
14
    :return: the offset following RFC3339.
15
    """
16
    func = (_datetime_offset_str if isinstance(obj, datetime)
17
            else _timedelta_offset_str)
18
    return func(obj)
19
20
21
def get_datetime_inst(obj: str, pattern: str) -> datetime:
22
    """
23
    Return a datetime instance with timezone info from the given ``obj``.
24
    :param obj: the ``obj`` in RFC3339 format.
25
    :param pattern: the datetime pattern.
26
    :return: a datetime instance with timezone info.
27
    """
28
    func = _datetime_utc_inst if obj[-1] == 'Z' else _datetime_offset_inst
29
    return func(obj, pattern)
30
31
32
def _datetime_offset_str(obj: datetime) -> str:
33
    """
34
    Return a textual offset (e.g. +01:00 or Z) for the given datetime.
35
    :param obj: the datetime instance.
36
    :return: the offset for ``obj``.
37
    """
38
    tzone = obj.tzinfo
39
    if not tzone:
40
        # datetimes without tzinfo are treated as local times.
41
        tzone = datetime.now(timezone.utc).astimezone().tzinfo
42
        if tzone is timezone.utc or tzone.utc is timezone.utc:
43
            return '+00:00'
44
    offset = 'Z'
45
    if tzone.tzname(None) not in ('UTC', 'UTC+00:00'):
46
        tdelta = tzone.utcoffset(None) or tzone.adjusted_offset
47
        offset = _timedelta_offset_str(tdelta)
48
    return offset
49
50
51
def _timedelta_offset_str(tdelta: timedelta) -> str:
52
    """
53
    Return a textual offset (e.g. +01:00 or Z) for the given timedelta.
54
    :param tdelta: the timedelta instance.
55
    :return: the offset for ``tdelta``.
56
    """
57
    offset_s = tdelta.total_seconds()
58
    offset_h = int(offset_s / 3600)
59
    offset_m = int((offset_s / 60) % 60)
60
    offset_t = time(abs(offset_h), abs(offset_m))
61
    operator = '+' if offset_s > 0 else '-'
62
    offset = offset_t.strftime('{}%H:%M'.format(operator))
63
    return offset
64
65
66
def _datetime_utc_inst(obj: str, pattern: str) -> datetime:
67
    """
68
    Return a datetime instance with UTC timezone info.
69
    :param obj: a datetime in RFC3339 format.
70
    :param pattern: the datetime pattern that is used.
71
    :return: a datetime instance with timezone info.
72
    """
73
    dattim_str = obj[0:-1]
74
    dattim_obj = datetime.strptime(dattim_str, pattern)
75
    return _new_datetime(dattim_obj.date(), dattim_obj.time(), timezone.utc)
76
77
78
def _datetime_offset_inst(obj: str, pattern: str) -> datetime:
79
    """
80
    Return a datetime instance with timezone info.
81
    :param obj: a datetime in RFC3339 format.
82
    :param pattern: the datetime pattern that is used.
83
    :return: a datetime instance with timezone info.
84
    """
85
    dat_str, tim_str = obj.split('T')
86
    splitter, factor = ('+', 1) if '+' in tim_str else ('-', -1)
87
    naive_tim_str, offset = tim_str.split(splitter)
88
    naive_dattim_str = '{}T{}'.format(dat_str, naive_tim_str)
89
    dattim_obj = datetime.strptime(naive_dattim_str, pattern)
90
    hrs_str, mins_str = offset.split(':')
91
    hrs = int(hrs_str) * factor
92
    mins = int(mins_str) * factor
93
    tz = timezone(offset=timedelta(hours=hrs, minutes=mins))
94
    return _new_datetime(dattim_obj.date(), dattim_obj.time(), tz)
95
96
97
def _new_datetime(date_inst: date, time_inst: time, tzinfo: timezone):
98
    """
99
    Return a datetime instance from a date, time and timezone.
100
101
    This function was required due to the missing argument for tzinfo under the
102
    Linux Python distribution.
103
    :param date_inst: the date.
104
    :param time_inst: the time.
105
    :param tzinfo: the Timezone.
106
    :return: a combined datetime instance.
107
    """
108
    return datetime.combine(date_inst, time_inst).replace(tzinfo=tzinfo)
109