| Total Complexity | 42 | 
| Total Lines | 141 | 
| Duplicated Lines | 85.11 % | 
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like dynamicserialize.dstypes.com.raytheon.uf.common.time.TimeRange 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 | # | ||
| 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 |