Wednesday, April 6, 2011

Python format timedelta to string

Hi, I'm a python newbie (2 weeks) and I'm having trouble formatting a datetime.timedelta object.

Here's what I'm trying to do. I have a list of objects and one of the members of the class of the object is a timedelta object that shows the duration of an event. I would like to display that duration in the format of hours:minutes.

I have tried a variety of methods for doing this and I'm having difficulty. My current approach is to add methods to the class for my objects that return hours and minutes. I can get the hours by dividing the timedelta.seconds by 3600 and rounding it. I'm having trouble with getting the remainder seconds and converting that to minutes.

By the way, I'm using Google AppEngine with DJango Templates for presentation.

If anyone can help or knows of a better way to resolve this, I would be very happy.

Thanks,

From stackoverflow
  • >>> str(datetime.timedelta(hours=10.56))
    10:33:36
    
    >>> td = datetime.timedelta(hours=10.505) # any timedelta object
    >>> ':'.join(str(td).split(':')[:2])
    10:30
    

    Passing the timedelta object to the str() function calls the same formatting code used if we simply type print td. Since you don't want the seconds, we can split the string by colons (3 parts) and put it back together with only the first 2 parts.

    mawcs : Thanks for your answer joeforker, but I'm not sure I understand your response. I am getting a time delta by way of datetime - datetime. I don't know the hours. Plus, it looks like your example includes seconds, how would I remove that?
    joeforker : Doesn't matter where you get the timedelta object, it will format the same.
    joeforker : If it's longer than a day, it will format as e.g. "4 days, 8:00" after the split/join processing.
  • Following Joe's example value above, I'd use the modulus arithmetic operator, thusly:

    td = datetime.timedelta(hours=10.56)
    td_str = "%d:%d" % (td.seconds/3600, td.seconds%3600/60)
    

    Note that integer division in Python rounds down by default; if you want to be more explicit, use math.floor() or math.ceil() as appropriate.

    joeforker : timedelta already knows how to format itself, as in 'print some_timedelta'.
    UltraNurd : Yeah, but it can't accept an arbitrary format string, which is what Michael was asking. Although now that I think about it 3600 division mod makes the hours-seconds assumption which causes problems at leap seconds.
    joeforker : Yeah, but he doesn't want an arbitrary format string, he wants almost exactly the default behaviour.
    joeforker : Don't forget // for truncating division in Python 3000
    UltraNurd : Ah, I've only be using up through 2.6.
  • You can just call the "str" method on the timedelta. Here's an example:

    import datetime
    start = datetime.datetime(2009,2,10,14,00)
    end = datetime.datetime(2009,2,10,16,00)
    delta = end-start
    print str(delta)
    # prints 2:00:00
    
    joeforker : More like calling the str() method with timedelta as its argument.
  • Thanks everyone for your help. I took many of your ideas and put them together, let me know what you think.

    I added two methods to the class like this:

    def hours(self):
        retval = ""
        if self.totalTime:
            hoursfloat = self.totalTime.seconds / 3600
            retval = round(hoursfloat)
        return retval
    
    def minutes(self):
        retval = ""
        if self.totalTime:
            minutesfloat = self.totalTime.seconds / 60
            hoursAsMinutes = self.hours() * 60
            retval = round(minutesfloat - hoursAsMinutes)
        return retval
    

    In my django I used this (sum is the object and it is in a dictionary):

    <td>{{ sum.0 }}</td>    
    <td>{{ sum.1.hours|stringformat:"d" }}:{{ sum.1.minutes|stringformat:"#02.0d" }}</td>
    
    joeforker : It's a bit long. I would suggest: def hhmm(self): return ':'.join(str(td).split(':')[:2]) {{ sum.1.hhmm }}
  • As you know, you can get the seconds from a timedelta object by accessing the .seconds attribute.

    You can convert that to hours and remainder by using a combination of modulo and subtraction:

    # arbitrary number of seconds
    s = 13420
    # hours
    hours = s // 3600 
    # remaining seconds
    s = s - (hours * 3600)
    # minutes
    minutes = s // 60
    # remaining seconds
    seconds = s - (minutes * 60)
    # total time
    print '%s:%s:%s' % (hours, minutes, seconds)
    # result: 3:43:40
    

    However, python provides the builtin function divmod() which allows us to simplify this code:

    s = 13420
    hours, remainder = divmod(s, 3600)
    minutes, seconds = divmod(remainder, 60)
    print '%s:%s:%s' % (hours, minutes, seconds)
    # result: 3:43:40
    

    Hope this helps!

  • My datetime.timedelta objects went greater than a day. So here is a further problem. All the discussion above assumes less than a day. A timedelta is actually a tuple of days, seconds and microseconds. The above discussion should use td.seconds as joe did, but if you have days it is NOT included in the seconds value.

    I am getting a span of time between 2 datetimes and printing days and hours.

    span = currentdt - previousdt print '%d,%d\n' % (span.days,span.seconds/3600)

0 comments:

Post a Comment