2

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Pwnna
  • 9,178
  • 21
  • 65
  • 91
  • 3
    related [How can I produce a human readable difference when subtracting two UNIX timestamps using Python?](http://stackoverflow.com/q/6574329/4279) – jfs Jul 02 '12 at 22:16
  • While the solution is good (and mine has grammar problems), I don't want to display such a long string and I only want to display up to 2 fields at any given timedeltas. That will make go back to my code again. – Pwnna Jul 03 '12 at 15:50

3 Answers3

5

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.

C2H5OH
  • 5,452
  • 2
  • 27
  • 39
  • Heh. While this entertaining to read, It seems like a maintainability disaster :P. Though more elegant – Pwnna Jul 03 '12 at 15:51
  • 1
    Looking back, this is pretty good, so accepted. However if anyone wants a more sane solution, you can take a look at the one I created here, but for coffeescript: https://gist.github.com/3994233 – Pwnna Jan 13 '13 at 18:34
1

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

>>> import datetime
>>> str(datetime.timedelta(seconds=1000))
'0:16:40'
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • 1
    This is... exactly the opposite of what the OP asked for, gets less readable when you have more than three components, and is ambiguous. (Can't tell at a glance whether it's a duration or a time.) – millimoose Jul 02 '12 at 22:24
  • Yeah, the way I'm asking for it is that it shows a maximum of 2 fields. Once it gets beyond days, it should only show 1 field. So it's brief and offers _enough_ information. – Pwnna Jul 03 '12 at 15:48
1

What about something like the following:

> import datetime
> import re
>
> def get_duration(sec):
...    return re.sub(r'(\d+):(\d\d):(\d\d)', 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.

Community
  • 1
  • 1
D.Shawley
  • 58,213
  • 10
  • 98
  • 113