Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion superset/utils/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,23 @@ def datetime_f(dttm):
return "<nobr>{}</nobr>".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()
Expand All @@ -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")
Expand Down
15 changes: 15 additions & 0 deletions tests/utils_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)), "<nobr>00:00:00</nobr>")

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]}'
Expand Down