Passed
Push — 2.x ( 8092b0...269f1b )
by Jordi
09:02
created

senaite.core.api.dtime   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 58
eloc 145
dl 0
loc 272
rs 4.5599
c 0
b 0
f 0

16 Functions

Rating   Name   Duplication   Size   Complexity  
A is_dt() 0 7 1
A is_d() 0 7 1
A is_timezone_naive() 0 16 5
A is_DT() 0 7 1
A is_date() 0 16 5
A is_timezone_aware() 0 7 1
B to_DT() 0 20 6
A is_str() 0 7 1
B to_dt() 0 22 6
A is_valid_timezone() 0 11 2
C get_timezone() 0 29 9
A to_zone() 0 19 5
A from_timestamp() 0 7 1
A to_iso_format() 0 11 4
A to_timestamp() 0 15 4
B get_os_timezone() 0 22 6

How to fix   Complexity   

Complexity

Complex classes like senaite.core.api.dtime often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
3
import os
4
import time
5
from datetime import date
6
from datetime import datetime
7
8
import six
9
10
import pytz
11
from bika.lims import logger
12
from bika.lims.api import APIError
13
from DateTime import DateTime
14
from DateTime.DateTime import DateError
15
from DateTime.DateTime import SyntaxError
16
from DateTime.DateTime import TimeError
17
18
19
def is_str(obj):
20
    """Check if the given object is a string
21
22
    :param obj: arbitrary object
23
    :returns: True when the object is a string
24
    """
25
    return isinstance(obj, six.string_types)
26
27
28
def is_d(dt):
29
    """Check if the date is a Python `date` object
30
31
    :param dt: date to check
32
    :returns: True when the date is a Python `date`
33
    """
34
    return type(dt) is date
35
36
37
def is_dt(dt):
38
    """Check if the date is a Python `datetime` object
39
40
    :param dt: date to check
41
    :returns: True when the date is a Python `datetime`
42
    """
43
    return type(dt) is datetime
44
45
46
def is_DT(dt):
47
    """Check if the date is a Zope `DateTime` object
48
49
    :param dt: object to check
50
    :returns: True when the object is a Zope `DateTime`
51
    """
52
    return type(dt) is DateTime
53
54
55
def is_date(dt):
56
    """Check if the date is a datetime or DateTime object
57
58
    :param dt: date to check
59
    :returns: True when the object is either a datetime or DateTime
60
    """
61
    if is_str(dt):
62
        DT = to_DT(dt)
63
        return is_date(DT)
64
    if is_d(dt):
65
        return True
66
    if is_dt(dt):
67
        return True
68
    if is_DT(dt):
69
        return True
70
    return False
71
72
73
def is_timezone_naive(dt):
74
    """Check if the date is timezone naive
75
76
    :param dt: date to check
77
    :returns: True when the date has no timezone
78
    """
79
    if is_d(dt):
80
        return True
81
    elif is_DT(dt):
82
        return dt.timezoneNaive()
83
    elif is_dt(dt):
84
        return dt.tzinfo is None
85
    elif is_str(dt):
86
        DT = to_DT(dt)
87
        return is_timezone_naive(DT)
88
    raise APIError("Expected a date type, got '%r'" % type(dt))
89
90
91
def is_timezone_aware(dt):
92
    """Check if the date is timezone aware
93
94
    :param dt: date to check
95
    :returns: True when the date has a timezone
96
    """
97
    return not is_timezone_naive(dt)
98
99
100
def to_DT(dt):
101
    """Convert to DateTime
102
103
    :param dt: DateTime/datetime/date
104
    :returns: DateTime object
105
    """
106
    if is_DT(dt):
107
        return dt
108
    elif is_str(dt):
109
        try:
110
            return DateTime(dt)
111
        except (DateError, TimeError, SyntaxError, IndexError):
112
            return None
113
    elif is_dt(dt):
114
        return DateTime(dt.isoformat())
115
    elif is_d(dt):
116
        dt = datetime(dt.year, dt.month, dt.day)
117
        return DateTime(dt.isoformat())
118
    else:
119
        return None
120
121
122
def to_dt(dt):
123
    """Convert to datetime
124
125
    :param dt: DateTime/datetime/date
126
    :returns: datetime object
127
    """
128
    if is_DT(dt):
129
        # get a valid pytz timezone
130
        tz = get_timezone(dt)
131
        dt = dt.asdatetime()
132
        if is_valid_timezone(tz):
133
            dt = to_zone(dt, tz)
134
        return dt
135
    elif is_str(dt):
136
        DT = to_DT(dt)
137
        return to_dt(DT)
138
    elif is_dt(dt):
139
        return dt
140
    elif is_d(dt):
141
        return datetime(dt.year, dt.month, dt.day)
142
    else:
143
        return None
144
145
146
def get_timezone(dt, default="Etc/GMT"):
147
    """Get a valid pytz timezone of the datetime object
148
149
    :param dt: date object
150
    :returns: timezone as string, e.g. Etc/GMT or CET
151
    """
152
    tz = None
153
    if is_dt(dt):
154
        tz = dt.tzname()
155
    elif is_DT(dt):
156
        tz = dt.timezone()
157
    elif is_d(dt):
158
        tz = default
159
160
    if tz:
161
        # convert DateTime `GMT` to `Etc/GMT` timezones
162
        # NOTE: `GMT+1` get `Etc/GMT-1`!
163
        if tz.startswith("GMT+0"):
164
            tz = tz.replace("GMT+0", "Etc/GMT")
165
        elif tz.startswith("GMT+"):
166
            tz = tz.replace("GMT+", "Etc/GMT-")
167
        elif tz.startswith("GMT-"):
168
            tz = tz.replace("GMT-", "Etc/GMT+")
169
        elif tz.startswith("GMT"):
170
            tz = tz.replace("GMT", "Etc/GMT")
171
    else:
172
        tz = default
173
174
    return tz
175
176
177
def is_valid_timezone(timezone):
178
    """Checks if the timezone is a valid pytz/Olson name
179
180
    :param timezone: pytz/Olson timezone name
181
    :returns: True when the timezone is a valid zone
182
    """
183
    try:
184
        pytz.timezone(timezone)
185
        return True
186
    except pytz.UnknownTimeZoneError:
187
        return False
188
189
190
def get_os_timezone(default="Etc/GMT"):
191
    """Return the default timezone of the system
192
193
    :returns: OS timezone or default timezone
194
    """
195
    timezone = None
196
    if "TZ" in os.environ.keys():
197
        # Timezone from OS env var
198
        timezone = os.environ["TZ"]
199
    if not timezone:
200
        # Timezone from python time
201
        zones = time.tzname
202
        if zones and len(zones) > 0:
203
            timezone = zones[0]
204
        else:
205
            logger.warn(
206
                "Operating system\'s timezone cannot be found. "
207
                "Falling back to %s." % default)
208
            timezone = default
209
    if not is_valid_timezone(timezone):
210
        return default
211
    return timezone
212
213
214
def to_zone(dt, timezone):
215
    """Convert date to timezone
216
217
    Adds the timezone for timezone naive datetimes
218
219
    :param dt: date object
220
    :param timezone: timezone
221
    :returns: date converted to timezone
222
    """
223
    if is_dt(dt) or is_d(dt):
224
        dt = to_dt(dt)
225
        zone = pytz.timezone(timezone)
226
        if is_timezone_aware(dt):
227
            return dt.astimezone(zone)
228
        return zone.localize(dt)
229
    elif is_DT(dt):
230
        # NOTE: This shifts the time according to the TZ offset
231
        return dt.toZone(timezone)
232
    raise TypeError("Expected a date, got '%r'" % type(dt))
233
234
235
def to_timestamp(dt):
236
    """Generate a Portable Operating System Interface (POSIX) timestamp
237
238
    :param dt: date object
239
    :returns: timestamp in seconds
240
    """
241
    timestamp = 0
242
    if is_DT(dt):
243
        timestamp = dt.timeTime()
244
    elif is_dt(dt):
245
        timestamp = time.mktime(dt.timetuple())
246
    elif is_str(dt):
247
        DT = to_DT(dt)
248
        return to_timestamp(DT)
249
    return timestamp
250
251
252
def from_timestamp(timestamp):
253
    """Generate a datetime object from a POSIX timestamp
254
255
    :param timestamp: POSIX timestamp
256
    :returns: datetime object
257
    """
258
    return datetime.utcfromtimestamp(timestamp)
259
260
261
def to_iso_format(dt):
262
    """Convert to ISO format
263
    """
264
    if is_dt(dt):
265
        return dt.isoformat()
266
    elif is_DT(dt):
267
        return dt.ISO()
268
    elif is_str(dt):
269
        DT = to_DT(dt)
270
        return to_iso_format(DT)
271
    return None
272