Make format_duration more robust
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
49723831e9
commit
078f87687f
|
@ -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)
|
* 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
|
## 0.1.4 / 2023-01-08 15:45+01:00
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ RUN npm install && \
|
||||||
|
|
||||||
FROM python:3.10.9-alpine
|
FROM python:3.10.9-alpine
|
||||||
|
|
||||||
ENV VERSION_NUMBER 0.1.2-11-g025ea0d
|
ENV VERSION_NUMBER 0.2.0
|
||||||
ENV PROD 1
|
ENV PROD 1
|
||||||
|
|
||||||
RUN apk add \
|
RUN apk add \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "timetracker"
|
name = "timetracker"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
description = "A simple time tracker."
|
description = "A simple time tracker."
|
||||||
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
||||||
license = "GPL"
|
license = "GPL"
|
||||||
|
|
|
@ -18,7 +18,7 @@ def _safe_timedelta(duration: timedelta | int | None):
|
||||||
|
|
||||||
|
|
||||||
def format_duration(
|
def format_duration(
|
||||||
duration: timedelta | int | None, format_string: str = "%H hours %m minutes"
|
duration: timedelta | int | None, format_string: str = "%H hours"
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Format timedelta into the specified format_string.
|
Format timedelta into the specified format_string.
|
||||||
|
@ -27,6 +27,10 @@ def format_duration(
|
||||||
- %m minutes
|
- %m minutes
|
||||||
- %s seconds
|
- %s seconds
|
||||||
- %r total 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
|
minute_seconds = 60
|
||||||
hour_seconds = 60 * minute_seconds
|
hour_seconds = 60 * minute_seconds
|
||||||
|
@ -37,9 +41,14 @@ def format_duration(
|
||||||
# timestamps where end is before start
|
# timestamps where end is before start
|
||||||
if seconds_total < 0:
|
if seconds_total < 0:
|
||||||
seconds_total = 0
|
seconds_total = 0
|
||||||
days, remainder = divmod(seconds_total, day_seconds)
|
days = hours = minutes = seconds = 0
|
||||||
hours, remainder = divmod(remainder, hour_seconds)
|
remainder = seconds = seconds_total
|
||||||
minutes, seconds = divmod(remainder, minute_seconds)
|
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 = {
|
literals = {
|
||||||
"%d": str(days),
|
"%d": str(days),
|
||||||
"%H": str(hours),
|
"%H": str(hours),
|
||||||
|
|
|
@ -18,15 +18,40 @@ class FormatDurationTest(unittest.TestCase):
|
||||||
result = format_duration(delta, "%H hours")
|
result = format_duration(delta, "%H hours")
|
||||||
self.assertEqual(result, "1 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):
|
def test_only_minutes(self):
|
||||||
delta = timedelta(minutes=34)
|
delta = timedelta(minutes=34)
|
||||||
result = format_duration(delta, "%m minutes")
|
result = format_duration(delta, "%m minutes")
|
||||||
self.assertEqual(result, "34 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):
|
def test_only_overflow_seconds(self):
|
||||||
delta = timedelta(seconds=61)
|
delta = timedelta(seconds=61)
|
||||||
result = format_duration(delta, "%s seconds")
|
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):
|
def test_only_rawseconds(self):
|
||||||
delta = timedelta(seconds=5690)
|
delta = timedelta(seconds=5690)
|
||||||
|
@ -65,4 +90,3 @@ class FormatDurationTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_number(self):
|
def test_number(self):
|
||||||
self.assertEqual(format_duration(3600, "%H hour"), "1 hour")
|
self.assertEqual(format_duration(3600, "%H hour"), "1 hour")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue