Skip to content

Commit

Permalink
Merge pull request #360 from lsst/tickets/DM-26445
Browse files Browse the repository at this point in the history
DM-26445: Fix Timespan equality to be fuzzy
  • Loading branch information
timj committed Aug 26, 2020
2 parents f4a9c9d + f730264 commit 6682cc8
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
26 changes: 24 additions & 2 deletions python/lsst/daf/butler/core/timespan.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ def __str__(self) -> str:
if self.begin is None:
head = "(-∞, "
else:
head = f"[{self.begin}, "
head = f"[{self.begin.tai.isot}, "
if self.end is None:
tail = "∞)"
else:
tail = f"{self.end})"
tail = f"{self.end.tai.isot})"
return head + tail

def __repr__(self) -> str:
Expand All @@ -82,6 +82,28 @@ def __repr__(self) -> str:
end = tmpl.format(t=self.end) if self.end is not None else None
return f"Timespan(begin={begin}, end={end})"

def __eq__(self, other: Any) -> bool:
# Include some fuzziness in equality because round tripping
# can introduce some drift at the picosecond level
# Butler is okay wih nanosecond precision
if not isinstance(other, type(self)):
return False

def compare_time(t1: Optional[astropy.time.Time], t2: Optional[astropy.time.Time]) -> bool:
if t1 is None and t2 is None:
return True
if t1 is None or t2 is None:
return False

return times_equal(t1, t2)

result = compare_time(self.begin, other.begin) and compare_time(self.end, other.end)
return result

def __ne__(self, other: Any) -> bool:
# Need to override the explicit parent class implementation
return not self.__eq__(other)

def overlaps(self, other: Timespan) -> Any:
"""Test whether this timespan overlaps another.
Expand Down
7 changes: 7 additions & 0 deletions tests/test_time_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from astropy.time import Time, TimeDelta
from lsst.daf.butler import time_utils
from lsst.daf.butler.core.time_utils import astropy_to_nsec


class TimeTestCase(unittest.TestCase):
Expand Down Expand Up @@ -120,6 +121,12 @@ def test_times_equal(self):
self.assertFalse(time_utils.times_equal(time1, time2, 499))
self.assertFalse(time_utils.times_equal(time2, time1, 499))

# UTC vs TAI
time1 = Time('2013-06-17 13:34:45.775000', scale='tai', format='iso')
time2 = Time('2013-06-17T13:34:10.775', scale='utc', format='isot')
self.assertTrue(time_utils.times_equal(time1, time2))
self.assertEqual(astropy_to_nsec(time1), astropy_to_nsec(time2))


if __name__ == "__main__":
unittest.main()
33 changes: 33 additions & 0 deletions tests/test_timespan.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,39 @@ def testOperationConsistency(self):
self.assertEqual(diffs1, (a,))
self.assertEqual(diffs2, (b,))

def testPrecision(self):
"""Test that we only use nanosecond precision for equality."""
ts1 = self.timespans[-1]
ts2 = Timespan(begin=ts1.begin + astropy.time.TimeDelta(1e-10, format="sec"), end=ts1.end)
self.assertEqual(ts1, ts2)

self.assertEqual(Timespan(begin=None, end=None), Timespan(begin=None, end=None))
self.assertEqual(Timespan(begin=None, end=ts1.end), Timespan(begin=None, end=ts1.end))

ts2 = Timespan(begin=ts1.begin + astropy.time.TimeDelta(1e-8, format="sec"), end=ts1.end)
self.assertNotEqual(ts1, ts2)

ts2 = Timespan(begin=None, end=ts1.end)
self.assertNotEqual(ts1, ts2)

t1 = Timespan(begin=astropy.time.Time(2456461.0, val2=0.06580758101851847, format="jd", scale="tai"),
end=astropy.time.Time(2456461.0, val2=0.06617994212962963, format="jd", scale="tai"))
t2 = Timespan(begin=astropy.time.Time(2456461.0, val2=0.06580758101851858, format="jd", scale="tai"),
end=astropy.time.Time(2456461.0, val2=0.06617994212962963, format="jd", scale="tai"))
self.assertEqual(t1, t2)

# Ensure that == and != work properly
self.assertTrue(t1 == t2, f"Equality of {t1} and {t2}")
self.assertFalse(t1 != t2, f"Check != is false for {t1} and {t2}")

def testTimescales(self):
"""Test time scale conversion occurs on comparison."""
ts1 = Timespan(begin=astropy.time.Time('2013-06-17 13:34:45.775000', scale='tai', format='iso'),
end=astropy.time.Time('2013-06-17 13:35:17.947000', scale='tai', format='iso'))
ts2 = Timespan(begin=astropy.time.Time('2013-06-17T13:34:10.775', scale='utc', format='isot'),
end=astropy.time.Time('2013-06-17T13:34:42.947', scale='utc', format='isot'))
self.assertEqual(ts1, ts2, f"Compare {ts1} with {ts2}")


if __name__ == "__main__":
unittest.main()

0 comments on commit 6682cc8

Please sign in to comment.