Issue #2667 fixed microseconds floating point error

time ranges now take any extra microseconds as separate argument


Former-commit-id: 24d9440e440b74f6b1b907062b10da0fef268aed
This commit is contained in:
Brian Clements 2014-02-28 15:46:17 -06:00
parent 45e8290111
commit fdf3e42cb7
2 changed files with 41 additions and 17 deletions

View file

@ -29,6 +29,7 @@
# ------------ ---------- ----------- -------------------------- # ------------ ---------- ----------- --------------------------
# 09/16/10 dgilling Initial Creation. # 09/16/10 dgilling Initial Creation.
# 01/22/14 2667 bclement use method to get millis from time range # 01/22/14 2667 bclement use method to get millis from time range
# 02/28/14 2667 bclement deserialize now converts millis to micros
# #
# #
# #
@ -39,6 +40,8 @@ from dynamicserialize.dstypes.com.raytheon.uf.common.time import TimeRange
ClassAdapter = 'com.raytheon.uf.common.time.TimeRange' ClassAdapter = 'com.raytheon.uf.common.time.TimeRange'
MICROS_IN_MILLISECOND = 1000
MILLIS_IN_SECOND = 1000
def serialize(context, timeRange): def serialize(context, timeRange):
context.writeI64(timeRange.getStartInMillis()) context.writeI64(timeRange.getStartInMillis())
@ -49,8 +52,12 @@ def deserialize(context):
endTime = context.readI64() endTime = context.readI64()
timeRange = TimeRange() timeRange = TimeRange()
timeRange.setStart(startTime/1000.0) # java uses milliseconds, python uses microseconds
timeRange.setEnd(endTime/1000.0) startSeconds = startTime // MILLIS_IN_SECOND
endSeconds = endTime // MILLIS_IN_SECOND
startExtraMicros = (startTime % MILLIS_IN_SECOND) * MICROS_IN_MILLISECOND
endExtraMicros = (endTime % MILLIS_IN_SECOND) * MICROS_IN_MILLISECOND
timeRange.setStart(startSeconds, startExtraMicros)
timeRange.setEnd(endSeconds, endExtraMicros)
return timeRange return timeRange

View file

@ -27,6 +27,7 @@
# ------------ ---------- ----------- -------------------------- # ------------ ---------- ----------- --------------------------
# ??/??/?? xxxxxxxx Initial Creation. # ??/??/?? xxxxxxxx Initial Creation.
# 01/22/14 2667 bclement fixed millisecond support # 01/22/14 2667 bclement fixed millisecond support
# 02/28/14 2667 bclement constructor can take extra micros for start and end
# #
# #
# #
@ -39,9 +40,9 @@ MAX_TIME = 2147483647
MICROS_IN_SECOND = 1000000 MICROS_IN_SECOND = 1000000
class TimeRange(object): class TimeRange(object):
def __init__(self, start=None, end=None): def __init__(self, start=None, end=None, startExtraMicros=None, endExtraMicros=None):
self.start = self.__convertToDateTime(start) self.start = self.__convertToDateTimeWithExtra(start, startExtraMicros)
self.end = self.__convertToDateTime(end) self.end = self.__convertToDateTimeWithExtra(end, endExtraMicros)
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
@ -55,6 +56,12 @@ class TimeRange(object):
def __ne__(self, other): def __ne__(self, other):
return (not self.__eq__(other)) return (not self.__eq__(other))
def __convertToDateTimeWithExtra(self, timeArg, extraMicros):
rval = self.__convertToDateTime(timeArg)
if rval is not None and extraMicros is not None:
rval = rval + datetime.timedelta(microseconds=extraMicros)
return rval
def __convertToDateTime(self, timeArg): def __convertToDateTime(self, timeArg):
if timeArg is None: if timeArg is None:
return None return None
@ -62,15 +69,25 @@ class TimeRange(object):
return timeArg return timeArg
elif isinstance(timeArg, time.struct_time): elif isinstance(timeArg, time.struct_time):
return datetime.datetime(*timeArg[:6]) return datetime.datetime(*timeArg[:6])
else: elif isinstance(timeArg, float):
# seconds as float, should be avoided due to floating point errors
totalSecs = long(timeArg) totalSecs = long(timeArg)
micros = int((timeArg - totalSecs) * MICROS_IN_SECOND) micros = int((timeArg - totalSecs) * MICROS_IN_SECOND)
if totalSecs < MAX_TIME: return self.__convertSecsAndMicros(totalSecs, micros)
rval = datetime.datetime.utcfromtimestamp(totalSecs) elif isinstance(timeArg, (int, long)):
else: # seconds as integer
extraTime = datetime.timedelta(seconds=(totalSecs - MAX_TIME)) totalSecs = timeArg
rval = datetime.datetime.utcfromtimestamp(MAX_TIME) + extraTime return self.__convertSecsAndMicros(totalSecs, 0)
return rval.replace(microsecond=micros) else:
return None
def __convertSecsAndMicros(self, seconds, micros):
if seconds < MAX_TIME:
rval = datetime.datetime.utcfromtimestamp(seconds)
else:
extraTime = datetime.timedelta(seconds=(seconds - MAX_TIME))
rval = datetime.datetime.utcfromtimestamp(MAX_TIME) + extraTime
return rval.replace(microsecond=micros)
def getStart(self): def getStart(self):
return self.start.utctimetuple() return self.start.utctimetuple()
@ -78,8 +95,8 @@ class TimeRange(object):
def getStartInMillis(self): def getStartInMillis(self):
return self._getInMillis(self.start) return self._getInMillis(self.start)
def setStart(self, start): def setStart(self, start, extraMicros=None):
self.start = self.__convertToDateTime(start) self.start = self.__convertToDateTimeWithExtra(start, extraMicros)
def getEnd(self): def getEnd(self):
return self.end.utctimetuple() return self.end.utctimetuple()
@ -92,8 +109,8 @@ class TimeRange(object):
rval += time.microsecond // 1000 rval += time.microsecond // 1000
return rval return rval
def setEnd(self, end): def setEnd(self, end, extraMicros=None):
self.end = self.__convertToDateTime(end) self.end = self.__convertToDateTimeWithExtra(end, extraMicros)
def duration(self): def duration(self):
delta = self.end - self.start delta = self.end - self.start