1
|
|
|
# |
2
|
|
|
# SOFTWARE HISTORY |
3
|
|
|
# |
4
|
|
|
# Date Ticket# Engineer Description |
5
|
|
|
# ------------ ---------- ----------- -------------------------- |
6
|
|
|
# ??/??/?? xxxxxxxx Initial Creation. |
7
|
|
|
# 01/22/14 2667 bclement fixed millisecond support |
8
|
|
|
# 02/28/14 2667 bclement constructor can take extra micros for start and end |
9
|
|
|
# 06/24/15 4480 dgilling fix __eq__. |
10
|
|
|
# |
11
|
|
|
# |
12
|
|
|
|
13
|
|
|
import calendar |
14
|
|
|
import datetime |
15
|
|
|
import time |
16
|
|
|
|
17
|
|
|
MAX_TIME = 2147483647 |
18
|
|
|
MICROS_IN_SECOND = 1000000 |
19
|
|
|
|
20
|
|
|
|
21
|
|
View Code Duplication |
class TimeRange(object): |
|
|
|
|
22
|
|
|
def __init__(self, start=None, end=None, startExtraMicros=None, endExtraMicros=None): |
23
|
|
|
self.start = self.__convertToDateTimeWithExtra(start, startExtraMicros) |
24
|
|
|
self.end = self.__convertToDateTimeWithExtra(end, endExtraMicros) |
25
|
|
|
|
26
|
|
|
def __str__(self): |
27
|
|
|
return self.__repr__() |
28
|
|
|
|
29
|
|
|
def __repr__(self): |
30
|
|
|
return "(" + self.start.strftime("%b %d %y %H:%M:%S %Z") + ", " + \ |
31
|
|
|
self.end.strftime("%b %d %y %H:%M:%S %Z") + ")" |
32
|
|
|
|
33
|
|
|
def __eq__(self, other): |
34
|
|
|
if not isinstance(self, type(other)): |
35
|
|
|
return False |
36
|
|
|
|
37
|
|
|
if self.isValid() and other.isValid(): |
38
|
|
|
return self.getStart() == other.getStart() and self.getEnd() == other.getEnd() |
39
|
|
|
elif not self.isValid() and not other.isValid(): |
40
|
|
|
return True |
41
|
|
|
else: |
42
|
|
|
return False |
43
|
|
|
|
44
|
|
|
def __ne__(self, other): |
45
|
|
|
return not self.__eq__(other) |
46
|
|
|
|
47
|
|
|
def __convertToDateTimeWithExtra(self, timeArg, extraMicros): |
48
|
|
|
rval = self.__convertToDateTime(timeArg) |
49
|
|
|
if rval is not None and extraMicros is not None: |
50
|
|
|
rval = rval + datetime.timedelta(microseconds=extraMicros) |
51
|
|
|
return rval |
52
|
|
|
|
53
|
|
|
def __convertToDateTime(self, timeArg): |
54
|
|
|
if timeArg is None: |
55
|
|
|
return None |
56
|
|
|
if isinstance(timeArg, datetime.datetime): |
57
|
|
|
return timeArg |
58
|
|
|
elif isinstance(timeArg, time.struct_time): |
59
|
|
|
return datetime.datetime(*timeArg[:6]) |
60
|
|
|
elif isinstance(timeArg, float): |
61
|
|
|
# seconds as float, should be avoided due to floating point errors |
62
|
|
|
totalSecs = int(timeArg) |
63
|
|
|
micros = int((timeArg - totalSecs) * MICROS_IN_SECOND) |
64
|
|
|
return self.__convertSecsAndMicros(totalSecs, micros) |
65
|
|
|
elif isinstance(timeArg, int): |
66
|
|
|
# seconds as integer |
67
|
|
|
totalSecs = timeArg |
68
|
|
|
return self.__convertSecsAndMicros(totalSecs, 0) |
69
|
|
|
else: |
70
|
|
|
return None |
71
|
|
|
|
72
|
|
|
def __convertSecsAndMicros(self, seconds, micros): |
73
|
|
|
if seconds < MAX_TIME: |
74
|
|
|
rval = datetime.datetime.utcfromtimestamp(seconds) |
75
|
|
|
else: |
76
|
|
|
extraTime = datetime.timedelta(seconds=(seconds - MAX_TIME)) |
77
|
|
|
rval = datetime.datetime.utcfromtimestamp(MAX_TIME) + extraTime |
78
|
|
|
return rval.replace(microsecond=micros) |
79
|
|
|
|
80
|
|
|
def getStart(self): |
81
|
|
|
return self.start.utctimetuple() |
82
|
|
|
|
83
|
|
|
def getStartInMillis(self): |
84
|
|
|
return self._getInMillis(self.start) |
85
|
|
|
|
86
|
|
|
def setStart(self, start, extraMicros=None): |
87
|
|
|
self.start = self.__convertToDateTimeWithExtra(start, extraMicros) |
88
|
|
|
|
89
|
|
|
def getEnd(self): |
90
|
|
|
return self.end.utctimetuple() |
91
|
|
|
|
92
|
|
|
def getEndInMillis(self): |
93
|
|
|
return self._getInMillis(self.end) |
94
|
|
|
|
95
|
|
|
def _getInMillis(self, time): |
96
|
|
|
rval = int(calendar.timegm(time.utctimetuple()) * 1000) |
97
|
|
|
rval += time.microsecond // 1000 |
98
|
|
|
return rval |
99
|
|
|
|
100
|
|
|
def setEnd(self, end, extraMicros=None): |
101
|
|
|
self.end = self.__convertToDateTimeWithExtra(end, extraMicros) |
102
|
|
|
|
103
|
|
|
def duration(self): |
104
|
|
|
delta = self.end - self.start |
105
|
|
|
return int(delta.total_seconds()) |
106
|
|
|
|
107
|
|
|
def contains(self, timeArg): |
108
|
|
|
if isinstance(timeArg, TimeRange): |
109
|
|
|
if self.duration() == 0: |
110
|
|
|
return self.__eq__(timeArg) |
111
|
|
|
elif timeArg.duration() == 0: |
112
|
|
|
return self.contains(timeArg.start) |
113
|
|
|
return timeArg.start >= self.start and timeArg.end <= self.end |
114
|
|
|
else: |
115
|
|
|
convTime = self.__convertToDateTime(timeArg) |
116
|
|
|
if not isinstance(convTime, datetime.datetime): |
117
|
|
|
raise TypeError("Invalid type for argument time specified to TimeRange.contains().") |
118
|
|
|
if self.duration() != 0: |
119
|
|
|
return self.start <= convTime < self.end |
120
|
|
|
return convTime == self.start |
121
|
|
|
|
122
|
|
|
def isValid(self): |
123
|
|
|
return bool(self.start != self.end) |
124
|
|
|
|
125
|
|
|
def overlaps(self, timeRange): |
126
|
|
|
return timeRange.contains(self.start) or self.contains(timeRange.start) |
127
|
|
|
|
128
|
|
|
def combineWith(self, timeRange): |
129
|
|
|
if self.isValid() and timeRange.isValid(): |
130
|
|
|
newStart = min(self.start, timeRange.start) |
131
|
|
|
newEnd = max(self.end, timeRange.end) |
132
|
|
|
return TimeRange(newStart, newEnd) |
133
|
|
|
elif self.isValid(): |
134
|
|
|
return self |
135
|
|
|
|
136
|
|
|
return timeRange |
137
|
|
|
|
138
|
|
|
@staticmethod |
139
|
|
|
def allTimes(): |
140
|
|
|
return TimeRange(0, MAX_TIME) |
141
|
|
|
|