diff --git a/superset/utils/core.py b/superset/utils/core.py index 7a74eb43335a..88f8df1fc776 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -349,6 +349,23 @@ def datetime_f(dttm): return "{}".format(dttm) +def format_timedelta(td: timedelta) -> str: + """ + Ensures negative time deltas are easily interpreted by humans + + >>> td = timedelta(0) - timedelta(days=1, hours=5,minutes=6) + >>> str(td) + '-2 days, 18:54:00' + >>> format_timedelta(td) + '-1 day, 5:06:00' + """ + if td < timedelta(0): + return "-" + str(abs(td)) + else: + # Change this to format positive time deltas the way you want + return str(td) + + def base_json_conv(obj): if isinstance(obj, memoryview): obj = obj.tobytes() @@ -363,7 +380,7 @@ def base_json_conv(obj): elif isinstance(obj, uuid.UUID): return str(obj) elif isinstance(obj, timedelta): - return str(obj) + return format_timedelta(obj) elif isinstance(obj, bytes): try: return obj.decode("utf-8") diff --git a/tests/utils_tests.py b/tests/utils_tests.py index 5efd44d99342..a0765834c6fd 100644 --- a/tests/utils_tests.py +++ b/tests/utils_tests.py @@ -32,6 +32,7 @@ base_json_conv, convert_legacy_filters_into_adhoc, datetime_f, + format_timedelta, get_or_create_db, get_since_until, get_stacktrace, @@ -120,6 +121,7 @@ def test_base_json_conv(self): assert isinstance(base_json_conv(set([1])), list) is True assert isinstance(base_json_conv(Decimal("1.0")), float) is True assert isinstance(base_json_conv(uuid.uuid4()), str) is True + assert isinstance(base_json_conv(timedelta(0)), str) is True @patch("superset.utils.core.datetime") def test_parse_human_timedelta(self, mock_datetime): @@ -535,6 +537,19 @@ def test_datetime_f(self): [a, b, c] = [int(v) for v in iso] self.assertEquals(datetime_f(datetime(a, b, c)), "00:00:00") + def test_format_timedelta(self): + self.assertEquals(format_timedelta(timedelta(0)), "0:00:00") + self.assertEquals(format_timedelta(timedelta(days=1)), "1 day, 0:00:00") + self.assertEquals(format_timedelta(timedelta(minutes=-6)), "-0:06:00") + self.assertEquals( + format_timedelta(timedelta(0) - timedelta(days=1, hours=5, minutes=6)), + "-1 day, 5:06:00", + ) + self.assertEquals( + format_timedelta(timedelta(0) - timedelta(days=16, hours=4, minutes=3)), + "-16 days, 4:03:00", + ) + def test_json_encoded_obj(self): obj = {"a": 5, "b": ["a", "g", 5]} val = '{"a": 5, "b": ["a", "g", 5]}'