Make format_duration more robust
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Lukáš Kucharczyk 2023-01-09 22:48:09 +01:00
parent 49723831e9
commit 078f87687f
5 changed files with 43 additions and 9 deletions

View File

@ -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

View File

@ -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 \

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "timetracker"
version = "0.1.2"
version = "0.2.0"
description = "A simple time tracker."
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
license = "GPL"

View File

@ -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),

View File

@ -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")