Search code examples
pythondjangodjango-templatesdjango-custom-tags

What is the best way to write a function that converts an integer of minutes into a string formatted in "X hour(s) Y minute(s)"?


I currently have my Django custom template filter like this:

from django import template

register = template.Library()

@register.filter
def duration(value):
    hours   = value / 60
    minutes = value % 60

    hours_string   = str(hours)   + (" hours"   if hours   > 1 else " hour"  ) if hours   else ""
    minutes_string = str(minutes) + (" minutes" if minutes > 1 else " minute") if minutes else ""

    return ' '.join("{hours} {minutes}".format(hours=hours_string, minutes=minutes_string).split())

Some possible outputs:

1 hour 25 minutes
2 hours
45 minutes
3 hours 1 minute

The ' '.join(....split()) trick was something I got from this solution. I realize I only need to check for the extra string at either the beginning or the end of the string (for when it's 0 hours or 0 minutes), but it seems to do the trick and maybe it's easier to read than regular expression to some people.

I also feel that I am violating DRY since I am repeating almost the exact same if-then block twice. I was thinking of something like...

for i, unit in enumerate([hours, minutes]):
    # Do some code.

return ' '.join("{hours} {minutes}".format(hours=value[0], minutes=value[1]).split())

but I couldn't figure out how to choose between "hour(s)" or "minute(s)" without another if-then block. Also the two short-hand if-then per line also looks a bit ugly too...

I'll appreciate any suggestions or tips. Thanks!


Solution

  • import datetime
    def pprint(val):
       if val == 1:
          return '1 minute'
       x = str(datetime.timedelta(minutes=val))[:-3].split(':')
       r = ''
       if int(x[0]):
           r += x[0]+' hours' if int(x[0]) > 1 else x[0]+' hour'
       if int(x[1]):
           r += ' %s minutes' % int(x[1]) if int(x[1]) > 1 else ' %s minute' % int(x[0])
       return r.strip()
    

    Sample run:

    >>> pprint(65)
    '1 hour 5 minutes'
    >>> pprint(1)
    '1 minute'
    >>> pprint(60)
    '1 hour'
    >>> pprint(61)
    '1 hour 1 minute'
    

    You can easily expand this to include days as well, but since you specifically mentioned only hours and minutes I adjusted the method.

    The main heavy lifting is done by datetime.timedelta, the rest is just string parsing.