patternpythonMinor
Efficient human readable timedelta
Viewed 0 times
humanefficientreadabletimedelta
Problem
I wanted to write a human readable datetime.timedelta that can be used in log files.
Eg, "Report issued 1 hour, 44 minutes, 20 seconds ago"
I noticed that casting a timedelta to str() generates something almost like what I want, but not quite.
To this end I wrote this:
Essentially, it's shaving off both ends of a list to make the results more meaningful.
The code above feels clunky though. Is there a more "Pythonic" way to do it? I'm using Python 2.7.3.
Eg, "Report issued 1 hour, 44 minutes, 20 seconds ago"
I noticed that casting a timedelta to str() generates something almost like what I want, but not quite.
To this end I wrote this:
def verbose_timedelta(delta):
hours, remainder = divmod(delta.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
dstr = "%s day%s" % (delta.days, "s"[delta.days==1:])
hstr = "%s hour%s" % (hours, "s"[hours==1:])
mstr = "%s minute%s" % (minutes, "s"[minutes==1:])
sstr = "%s second%s" % (seconds, "s"[seconds==1:])
dhms = [dstr, hstr, mstr, sstr]
for x in range(len(dhms)):
if not dhms[x].startswith('0'):
dhms = dhms[x:]
break
dhms.reverse()
for x in range(len(dhms)):
if not dhms[x].startswith('0'):
dhms = dhms[x:]
break
dhms.reverse()
return ', '.join(dhms)Essentially, it's shaving off both ends of a list to make the results more meaningful.
The code above feels clunky though. Is there a more "Pythonic" way to do it? I'm using Python 2.7.3.
Solution
- Use a ternary operator in
"s"[seconds==1:].
- Use a generator expression to replace the
xstr = "%s...lines.
- The two for loops should use
enumerate(), i.e. they could befor s, i in range(...).
- The two for loops should be moved into a
for _ in range(2):.
iis preferred overxwhen using an incrementing index counter.
- The filtering of the redundant strings which the for-loop does could be done earlier so that the number to string code can be modified but the filtering code will not require adjustments.
PS: I have implemented a similar function here:
days, rem = divmod(seconds, 86400)
hours, rem = divmod(rem, 3600)
minutes, seconds = divmod(rem, 60)
if seconds < 1:seconds = 1
locals_ = locals()
magnitudes_str = ("{n} {magnitude}".format(n=int(locals_[magnitude]), magnitude=magnitude)
for magnitude in ("days", "hours", "minutes", "seconds") if locals_[magnitude])
eta_str = ", ".join(magnitudes_str)Code Snippets
days, rem = divmod(seconds, 86400)
hours, rem = divmod(rem, 3600)
minutes, seconds = divmod(rem, 60)
if seconds < 1:seconds = 1
locals_ = locals()
magnitudes_str = ("{n} {magnitude}".format(n=int(locals_[magnitude]), magnitude=magnitude)
for magnitude in ("days", "hours", "minutes", "seconds") if locals_[magnitude])
eta_str = ", ".join(magnitudes_str)Context
StackExchange Code Review Q#37285, answer score: 3
Revisions (0)
No revisions yet.