| Total Complexity | 59 | 
| Total Lines | 266 | 
| Duplicated Lines | 84.96 % | 
| 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.DataTime 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 | # 05/28/13 2023 dgilling Implement __str__(). | ||
| 8 | # 01/22/14 2667 bclement preserved milliseconds in string representation | ||
| 9 | # 03/03/14 2673 bsteffen allow construction using a Date for refTime | ||
| 10 | # 06/24/14 3096 mnash implement __cmp__ | ||
| 11 | # 06/24/15 4480 dgilling implement __hash__ and __eq__, | ||
| 12 | # replace __cmp__ with rich comparison | ||
| 13 | # operators. | ||
| 14 | # 05/26/16 2416 rjpeter Added str based constructor. | ||
| 15 | # 08/02/16 2416 tgurney Forecast time regex bug fix, | ||
| 16 | # plus misc cleanup | ||
| 17 | # | ||
| 18 | |||
| 19 | import calendar | ||
| 20 | import datetime | ||
| 21 | import re | ||
| 22 | import time | ||
| 23 | import numpy | ||
| 24 | from six.moves import cStringIO as StringIO | ||
| 25 | |||
| 26 | from dynamicserialize.dstypes.java.util import Date | ||
| 27 | from dynamicserialize.dstypes.java.util import EnumSet | ||
| 28 | |||
| 29 | from .TimeRange import TimeRange | ||
| 30 | |||
| 31 | _DATE = r'(\d{4}-\d{2}-\d{2})' | ||
| 32 | _TIME = r'(\d{2}:\d{2}:\d{2})' | ||
| 33 | _MILLIS = '(?:\.(\d{1,3})(?:\d{1,4})?)?'  # might have microsecond but that is thrown out | ||
| 34 | REFTIME_PATTERN_STR = _DATE + '[ _]' + _TIME + _MILLIS | ||
| 35 | FORECAST_PATTERN_STR = r'(?:[ _]\((\d+)(?::(\d{1,2}))?\))?' | ||
| 36 | VALID_PERIOD_PATTERN_STR = r'(?:\[' + REFTIME_PATTERN_STR + '--' + REFTIME_PATTERN_STR + r'\])?' | ||
| 37 | STR_PATTERN = re.compile(REFTIME_PATTERN_STR + FORECAST_PATTERN_STR + VALID_PERIOD_PATTERN_STR) | ||
| 38 | |||
| 39 | |||
| 40 | View Code Duplication | class DataTime(object): | |
|  | |||
| 41 | |||
| 42 | def __init__(self, refTime=None, fcstTime=None, validPeriod=None): | ||
| 43 | """ | ||
| 44 | Construct a new DataTime. | ||
| 45 | May also be called as DataTime(str) to parse a string and create a | ||
| 46 | DataTime from it. Some examples of valid DataTime strings: | ||
| 47 | |||
| 48 | '2016-08-02 01:23:45.0' | ||
| 49 | '2016-08-02 01:23:45.123' | ||
| 50 | '2016-08-02 01:23:45.0 (17)', | ||
| 51 | '2016-08-02 01:23:45.0 (17:34)' | ||
| 52 | '2016-08-02 01:23:45.0[2016-08-02_02:34:45.0--2016-08-02_03:45:56.0]' | ||
| 53 | '2016-08-02 01:23:45.456_(17:34)[2016-08-02_02:34:45.0--2016-08-02_03:45:56.0]' | ||
| 54 | """ | ||
| 55 | if fcstTime is not None: | ||
| 56 | self.fcstTime = int(fcstTime) | ||
| 57 | else: | ||
| 58 | self.fcstTime = 0 | ||
| 59 | self.refTime = refTime | ||
| 60 | if validPeriod is not None and not isinstance(validPeriod, TimeRange): | ||
| 61 |             raise ValueError("Invalid validPeriod object specified for DataTime.") | ||
| 62 | self.validPeriod = validPeriod | ||
| 63 |         self.utilityFlags = EnumSet('com.raytheon.uf.common.time.DataTime$FLAG') | ||
| 64 | self.levelValue = numpy.float64(-1.0) | ||
| 65 | |||
| 66 | if self.refTime is not None: | ||
| 67 | if isinstance(self.refTime, datetime.datetime): | ||
| 68 | self.refTime = int(calendar.timegm(self.refTime.utctimetuple()) * 1000) | ||
| 69 | elif isinstance(self.refTime, time.struct_time): | ||
| 70 | self.refTime = int(calendar.timegm(self.refTime) * 1000) | ||
| 71 | elif hasattr(self.refTime, 'getTime'): | ||
| 72 | # getTime should be returning ms, there is no way to check this | ||
| 73 | # This is expected for java Date | ||
| 74 | self.refTime = int(self.refTime.getTime()) | ||
| 75 | else: | ||
| 76 | try: | ||
| 77 | self.refTime = int(self.refTime) | ||
| 78 | except ValueError: | ||
| 79 | # Assume first arg is a string. Attempt to parse. | ||
| 80 | match = STR_PATTERN.match(self.refTime) | ||
| 81 | if match is None: | ||
| 82 |                         raise ValueError('Could not parse DataTime info from ' | ||
| 83 | + str(refTime)) | ||
| 84 | |||
| 85 | groups = match.groups() | ||
| 86 | rMillis = groups[2] or 0 | ||
| 87 | fcstTimeHr = groups[3] | ||
| 88 | fcstTimeMin = groups[4] | ||
| 89 | periodStart = groups[5], groups[6], (groups[7] or 0) | ||
| 90 | periodEnd = groups[8], groups[9], (groups[10] or 0) | ||
| 91 | self.refTime = self._getTimeAsEpochMillis(groups[0], groups[1], rMillis) | ||
| 92 | |||
| 93 | if fcstTimeHr is not None: | ||
| 94 | self.fcstTime = int(fcstTimeHr) * 3600 | ||
| 95 | if fcstTimeMin is not None: | ||
| 96 | self.fcstTime += int(fcstTimeMin) * 60 | ||
| 97 | |||
| 98 | if periodStart[0] is not None: | ||
| 99 | self.validPeriod = TimeRange() | ||
| 100 | periodStartTime = self._getTimeAsEpochMillis(*periodStart) | ||
| 101 | self.validPeriod.setStart(periodStartTime / 1000) | ||
| 102 | periodEndTime = self._getTimeAsEpochMillis(*periodEnd) | ||
| 103 | self.validPeriod.setEnd(periodEndTime / 1000) | ||
| 104 | |||
| 105 | self.refTime = Date(self.refTime) | ||
| 106 | |||
| 107 | if self.validPeriod is None: | ||
| 108 | validTimeMillis = self.refTime.getTime() + int(self.fcstTime * 1000) | ||
| 109 | self.validPeriod = TimeRange() | ||
| 110 | self.validPeriod.setStart(validTimeMillis / 1000) | ||
| 111 | self.validPeriod.setEnd(validTimeMillis / 1000) | ||
| 112 | |||
| 113 | # figure out utility flags | ||
| 114 | if self.fcstTime: | ||
| 115 |             self.utilityFlags.add("FCST_USED") | ||
| 116 | if self.validPeriod and self.validPeriod.isValid(): | ||
| 117 |             self.utilityFlags.add("PERIOD_USED") | ||
| 118 | |||
| 119 | def getRefTime(self): | ||
| 120 | return self.refTime | ||
| 121 | |||
| 122 | def setRefTime(self, refTime): | ||
| 123 | self.refTime = refTime | ||
| 124 | |||
| 125 | def getFcstTime(self): | ||
| 126 | return self.fcstTime | ||
| 127 | |||
| 128 | def setFcstTime(self, fcstTime): | ||
| 129 | self.fcstTime = fcstTime | ||
| 130 | |||
| 131 | def getValidPeriod(self): | ||
| 132 | return self.validPeriod | ||
| 133 | |||
| 134 | def setValidPeriod(self, validPeriod): | ||
| 135 | self.validPeriod = validPeriod | ||
| 136 | |||
| 137 | def getUtilityFlags(self): | ||
| 138 | return self.utilityFlags | ||
| 139 | |||
| 140 | def setUtilityFlags(self, utilityFlags): | ||
| 141 | self.utilityFlags = utilityFlags | ||
| 142 | |||
| 143 | def getLevelValue(self): | ||
| 144 | return self.levelValue | ||
| 145 | |||
| 146 | def setLevelValue(self, levelValue): | ||
| 147 | self.levelValue = numpy.float64(levelValue) | ||
| 148 | |||
| 149 | def __str__(self): | ||
| 150 | sbuffer = StringIO() | ||
| 151 | |||
| 152 | if self.refTime is not None: | ||
| 153 | refTimeInSecs = self.refTime.getTime() / 1000 | ||
| 154 | micros = (self.refTime.getTime() % 1000) * 1000 | ||
| 155 | dtObj = datetime.datetime.utcfromtimestamp(refTimeInSecs) | ||
| 156 | dtObj = dtObj.replace(microsecond=micros) | ||
| 157 | # This won't be compatible with java or string from java since its to microsecond | ||
| 158 |             sbuffer.write(dtObj.isoformat(' ')) | ||
| 159 | |||
| 160 | if "FCST_USED" in self.utilityFlags: | ||
| 161 | hrs = int(self.fcstTime / 3600) | ||
| 162 | mins = int((self.fcstTime - (hrs * 3600)) / 60) | ||
| 163 |             sbuffer.write(" (" + str(hrs)) | ||
| 164 | if mins != 0: | ||
| 165 |                 sbuffer.write(":" + str(mins)) | ||
| 166 |             sbuffer.write(")") | ||
| 167 | |||
| 168 | if "PERIOD_USED" in self.utilityFlags: | ||
| 169 |             sbuffer.write("[") | ||
| 170 |             sbuffer.write(self.validPeriod.start.isoformat(' ')) | ||
| 171 |             sbuffer.write("--") | ||
| 172 |             sbuffer.write(self.validPeriod.end.isoformat(' ')) | ||
| 173 |             sbuffer.write("]") | ||
| 174 | |||
| 175 | strVal = sbuffer.getvalue() | ||
| 176 | sbuffer.close() | ||
| 177 | return strVal | ||
| 178 | |||
| 179 | def __repr__(self): | ||
| 180 | return "<DataTime instance: " + str(self) + " >" | ||
| 181 | |||
| 182 | def __hash__(self): | ||
| 183 | hashCode = hash(self.refTime) ^ hash(self.fcstTime) | ||
| 184 | if self.validPeriod is not None and self.validPeriod.isValid(): | ||
| 185 | hashCode ^= hash(self.validPeriod.getStart()) | ||
| 186 | hashCode ^= hash(self.validPeriod.getEnd()) | ||
| 187 | hashCode ^= hash(self.levelValue) | ||
| 188 | return hashCode | ||
| 189 | |||
| 190 | def __eq__(self, other): | ||
| 191 | if not isinstance(self, type(other)): | ||
| 192 | return False | ||
| 193 | |||
| 194 | if other.getRefTime() is None: | ||
| 195 | return self.fcstTime == other.fcstTime | ||
| 196 | |||
| 197 | dataTime1 = (self.refTime, self.fcstTime, self.validPeriod, self.levelValue) | ||
| 198 | dataTime2 = (other.refTime, other.fcstTime, other.validPeriod, other.levelValue) | ||
| 199 | return dataTime1 == dataTime2 | ||
| 200 | |||
| 201 | def __ne__(self, other): | ||
| 202 | return not self.__eq__(other) | ||
| 203 | |||
| 204 | def __lt__(self, other): | ||
| 205 | if not isinstance(self, type(other)): | ||
| 206 | return NotImplemented | ||
| 207 | |||
| 208 | myValidTime = self.getRefTime().getTime() + self.getFcstTime() | ||
| 209 | otherValidTime = other.getRefTime().getTime() + other.getFcstTime() | ||
| 210 | if myValidTime < otherValidTime: | ||
| 211 | return True | ||
| 212 | |||
| 213 | if self.fcstTime < other.fcstTime: | ||
| 214 | return True | ||
| 215 | |||
| 216 | if self.levelValue < other.levelValue: | ||
| 217 | return True | ||
| 218 | |||
| 219 | myValidPeriod = self.validPeriod | ||
| 220 | otherValidPeriod = other.validPeriod | ||
| 221 | if myValidPeriod != otherValidPeriod: | ||
| 222 | if myValidPeriod.duration() < otherValidPeriod.duration(): | ||
| 223 | return True | ||
| 224 | return myValidPeriod.getStartInMillis() < otherValidPeriod.getStartInMillis() | ||
| 225 | return False | ||
| 226 | |||
| 227 | def __le__(self, other): | ||
| 228 | if not isinstance(self, type(other)): | ||
| 229 | return NotImplemented | ||
| 230 | |||
| 231 | return self.__lt__(other) or self.__eq__(other) | ||
| 232 | |||
| 233 | def __gt__(self, other): | ||
| 234 | if not isinstance(self, type(other)): | ||
| 235 | return NotImplemented | ||
| 236 | |||
| 237 | myValidTime = self.getRefTime().getTime() + self.getFcstTime() | ||
| 238 | otherValidTime = other.getRefTime().getTime() + other.getFcstTime() | ||
| 239 | if myValidTime > otherValidTime: | ||
| 240 | return True | ||
| 241 | |||
| 242 | if self.fcstTime > other.fcstTime: | ||
| 243 | return True | ||
| 244 | |||
| 245 | if self.levelValue > other.levelValue: | ||
| 246 | return True | ||
| 247 | |||
| 248 | myValidPeriod = self.validPeriod | ||
| 249 | otherValidPeriod = other.validPeriod | ||
| 250 | if myValidPeriod != otherValidPeriod: | ||
| 251 | if myValidPeriod.duration() > otherValidPeriod.duration(): | ||
| 252 | return True | ||
| 253 | return myValidPeriod.getStartInMillis() > otherValidPeriod.getStartInMillis() | ||
| 254 | return False | ||
| 255 | |||
| 256 | def __ge__(self, other): | ||
| 257 | if not isinstance(self, type(other)): | ||
| 258 | return NotImplemented | ||
| 259 | |||
| 260 | return self.__gt__(other) or self.__eq__(other) | ||
| 261 | |||
| 262 | def _getTimeAsEpochMillis(self, dateStr, timeStr, millis): | ||
| 263 | t = time.strptime(dateStr + ' ' + timeStr, '%Y-%m-%d %H:%M:%S') | ||
| 264 | epochSeconds = calendar.timegm(t) | ||
| 265 | return int(epochSeconds * 1000) + int(millis) | ||
| 266 |