A more elegant version of generating the time to be viewed by humans

Question:

I’m currently generating the amount of time given the number of seconds. This is what I have come up with very quickly. It works well, but it’s quite ugly.

I can’t think of any tricks to make this more elegant (without making it complicated or rely on this and that), but maybe there’s people here that have some tips.

def humanizeTime(seconds):
  if seconds < 60:
    return "%d seconds" % int(round(seconds))
  else:
    minutes = seconds / 60.0
    if minutes < 60:
      return "%d minutes %d seconds" % divmod(seconds, 60)
    else:
      hours = minutes / 60.0
      if hours < 24:
        return "%d hours %d minutes" % divmod(minutes, 60)
      else:
        days = hours / 24.0
        if days < 7:
          return "%d days" % int(round(days))
        else:
          weeks = days / 7.0
          if weeks < 4:
            return "%d weeks" % int(round(weeks))
          else:
            months = days / 30.0
            if months < 12:
              return "%d months" % int(round(months))
            else:
              return "%d years" % int(round(days / 365.0))

Edit

If there’s a good library that could compute what I have above (which again, never exceeds 2 fields) with proper grammar, I would definitely jump on board.

Then again I can’t find any that does that, as any library that can compute this will still require me to write code like this (or some of the answers shown below) to display only 2 fields max.

Asked By: Pwnna

||

Answers:

I think this works fine as hh:mm:ss format is much more readable.

>>> import datetime
>>> str(datetime.timedelta(seconds=1000))
'0:16:40'
Answered By: Ashwini Chaudhary

DISCLAIMER

This answer is an example of a more pythonic way of expressing the code shown by the OP. It is just a fun coding exercise using high order functions, and by no means is a suggested, not even sane, option to deal with true time intervals.

Remember, real life time and calendar calculations are not easy nor trivial and it is always better to use a well tested and mature library for this matter.


That said, here’s a way to do it, apparently more elegant:

# A function to generate the divisions list
def divisions(l, v):
    l.append(l[-1] * v)
    return l

# A function to reduce the divisions list
def reduction(l, v):
    q, r = divmod(l[-1], v)
    l[-1] = q
    l.append(r)
    return l

TIME_STEPS = (60, 60, 24, 7, 30, 12)
DIVISIONS = reduce(divisions, TIME_STEPS, [1])[1:]
DIVISIONS.reverse()

# The "seconds" variable holds the time interval in seconds
seconds = 6000
fragments = reduce(reduction, DIVISIONS, [seconds])

# Fragments is a list: [years, months, weeks, days, hours, minutes, seconds]
# In this example: [0, 0, 0, 0, 1, 40, 0]

# And here's the readability part
NAMES = ("years", "months", "weeks", "days", "hours", "minutes", "seconds")
readable = " ".join("%d %s" % (v, n) for v, n in zip(fragments, NAMES) if v > 0)

# Final result: readable = "1 hours 40 minutes"

Notice that most of the lists are being mutated during the reductions, which is a bit questionable. In a reasonably pure functional programming language, I could be burned alive by writing this. But, as Python is not a functional programming language, it’s not so bad.

Also note that half of the code shown is to calculate the DIVISIONS list, which is always the same so it could be precalculated by hand.

Answered By: C2H5OH

What about something like the following:

> import datetime
> import re
>
> def get_duration(sec):
...    return re.sub(r'(d+):(dd):(dd)', r'1 hrs, 2 mins, 3 secs',
...                  str(datetime.timedelta(seconds=sec)))
...
> get_duration(0)
'0 hrs, 00 mins, 00 secs'
> get_duration(86401)
'1 day, 0 hrs, 00 mins, 01 secs'
> get_duration(691200)
' 8 days, 0 hrs, 00 mins, 00 secs'

It doesn’t handle months, weeks, etc. Then again, it is a lot of work to correctly handle anything past weeks. I think that datetime.timedelta gets you pretty far down the path to a decent solution without a lot of explicit math or other ugliness.

With that said, look at the dateutil module as suggested by this answer. I have written a bunch of time routines in various embedded devices and I will never even consider writing one again without extreme provocation.

Answered By: D.Shawley
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.