From 078f87687f53242d1cc5566cfc1472bd7796dd0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Mon, 9 Jan 2023 22:48:09 +0100 Subject: [PATCH] Make format_duration more robust --- CHANGELOG.md | 3 ++- Dockerfile | 2 +- pyproject.toml | 2 +- src/web/common/util/time.py | 17 +++++++++++++---- tests/test_time.py | 28 ++++++++++++++++++++++++++-- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5774c8..0b93395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -## Unreleased +## 0.2.0 / 2023-01-09 22:42+01:00 * Show playtime total on session list (https://git.kucharczyk.xyz/lukas/timetracker/issues/6) +* Make formatting durations more robust, change default duration display to "X hours" (https://git.kucharczyk.xyz/lukas/timetracker/issues/26) ## 0.1.4 / 2023-01-08 15:45+01:00 diff --git a/Dockerfile b/Dockerfile index 235d347..33cfa96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN npm install && \ FROM python:3.10.9-alpine -ENV VERSION_NUMBER 0.1.2-11-g025ea0d +ENV VERSION_NUMBER 0.2.0 ENV PROD 1 RUN apk add \ diff --git a/pyproject.toml b/pyproject.toml index 55795fa..cfd223a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "timetracker" -version = "0.1.2" +version = "0.2.0" description = "A simple time tracker." authors = ["Lukáš Kucharczyk "] license = "GPL" diff --git a/src/web/common/util/time.py b/src/web/common/util/time.py index 084ab38..d73df88 100644 --- a/src/web/common/util/time.py +++ b/src/web/common/util/time.py @@ -18,7 +18,7 @@ def _safe_timedelta(duration: timedelta | int | None): def format_duration( - duration: timedelta | int | None, format_string: str = "%H hours %m minutes" + duration: timedelta | int | None, format_string: str = "%H hours" ) -> str: """ Format timedelta into the specified format_string. @@ -27,6 +27,10 @@ def format_duration( - %m minutes - %s seconds - %r total seconds + Values don't change into higher units if those units are missing + from the formatting string. For example: + - 61 seconds as "%s" = 61 seconds + - 61 seconds as "%m %s" = 1 minutes 1 seconds" """ minute_seconds = 60 hour_seconds = 60 * minute_seconds @@ -37,9 +41,14 @@ def format_duration( # timestamps where end is before start if seconds_total < 0: seconds_total = 0 - days, remainder = divmod(seconds_total, day_seconds) - hours, remainder = divmod(remainder, hour_seconds) - minutes, seconds = divmod(remainder, minute_seconds) + days = hours = minutes = seconds = 0 + remainder = seconds = seconds_total + if "%d" in format_string: + days, remainder = divmod(seconds_total, day_seconds) + if "%H" in format_string: + hours, remainder = divmod(remainder, hour_seconds) + if "%m" in format_string: + minutes, seconds = divmod(remainder, minute_seconds) literals = { "%d": str(days), "%H": str(hours), diff --git a/tests/test_time.py b/tests/test_time.py index f7ca4ce..e2ae81a 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -18,15 +18,40 @@ class FormatDurationTest(unittest.TestCase): result = format_duration(delta, "%H hours") self.assertEqual(result, "1 hours") + def test_overflow_hours(self): + delta = timedelta(hours=25) + result = format_duration(delta, "%H hours") + self.assertEqual(result, "25 hours") + + def test_overflow_hours_into_days(self): + delta = timedelta(hours=25) + result = format_duration(delta, "%d days, %H hours") + self.assertEqual(result, "1 days, 1 hours") + def test_only_minutes(self): delta = timedelta(minutes=34) result = format_duration(delta, "%m minutes") self.assertEqual(result, "34 minutes") + def test_only_overflow_minutes(self): + delta = timedelta(minutes=61) + result = format_duration(delta, "%m minutes") + self.assertEqual(result, "61 minutes") + + def test_overflow_minutes_into_hours(self): + delta = timedelta(minutes=61) + result = format_duration(delta, "%H hours, %m minutes") + self.assertEqual(result, "1 hours, 1 minutes") + def test_only_overflow_seconds(self): delta = timedelta(seconds=61) result = format_duration(delta, "%s seconds") - self.assertEqual(result, "1 seconds") + self.assertEqual(result, "61 seconds") + + def test_overflow_seconds_into_minutes(self): + delta = timedelta(seconds=61) + result = format_duration(delta, "%m minutes, %s seconds") + self.assertEqual(result, "1 minutes, 1 seconds") def test_only_rawseconds(self): delta = timedelta(seconds=5690) @@ -65,4 +90,3 @@ class FormatDurationTest(unittest.TestCase): def test_number(self): self.assertEqual(format_duration(3600, "%H hour"), "1 hour") -