Python format timedelta to string

匿名 (未验证) 提交于 2019-12-03 01:59:02

问题:

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,

回答1:

You can just convert the timedelta to a string with str(). 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 


回答2:

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!



回答3:

>>> 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.



回答4:

def td_format(td_object):     seconds = int(td_object.total_seconds())     periods = [         ('year',        60*60*24*365),         ('month',       60*60*24*30),         ('day',         60*60*24),         ('hour',        60*60),         ('minute',      60),         ('second',      1)     ]      strings=[]     for period_name, period_seconds in periods:         if seconds > period_seconds:             period_value , seconds = divmod(seconds, period_seconds)             has_s = 's' if period_value > 1 else ''             strings.append("%s %s%s" % (period_value, period_name, has_s))      return ", ".join(strings) 


回答5:

He already has a timedelta object so why not use its built-in method total_seconds() to convert it to seconds, then use divmod() to get hours and minutes?

hours, remainder = divmod(myTimeDelta.total_seconds(), 3600) minutes, seconds = divmod(remainder, 60)  # Formatted only for hours and minutes as requested print '%s:%s' % (hours, minutes) 

This works regardless if the time delta has even days or years.



回答6:

Questioner wants a nicer format than the typical:

  >>> import datetime   >>> datetime.timedelta(seconds=41000)   datetime.timedelta(0, 41000)   >>> str(datetime.timedelta(seconds=41000))   '11:23:20'   >>> str(datetime.timedelta(seconds=4102.33))   '1:08:22.330000'   >>> str(datetime.timedelta(seconds=413302.33))   '4 days, 18:48:22.330000' 

So, really there's two formats, one where days are 0 and it's left out, and another where there's text "n days, h:m:s". But, the seconds may have fractions, and there's no leading zeroes in the printouts, so columns are messy.

Here's my routine, if you like it:

def printNiceTimeDelta(stime, etime):     delay = datetime.timedelta(seconds=(etime - stime))     if (delay.days > 0):         out = str(delay).replace(" days, ", ":")     else:         out = "0:" + str(delay)     outAr = out.split(':')     outAr = ["%02d" % (int(float(x))) for x in outAr]     out   = ":".join(outAr)     return out 

this returns output as dd:hh:mm:ss format:

00:00:00:15 00:00:00:19 02:01:31:40 02:01:32:22 

I did think about adding years to this, but this is left as an exercise for the reader, since the output is safe at over 1 year:

>>> str(datetime.timedelta(seconds=99999999)) '1157 days, 9:46:39' 


回答7:

I personally use the humanize library for this:

>>> import datetime >>> humanize.naturalday(datetime.datetime.now()) 'today' >>> humanize.naturalday(datetime.datetime.now() - datetime.timedelta(days=1)) 'yesterday' >>> humanize.naturalday(datetime.date(2007, 6, 5)) 'Jun 05' >>> humanize.naturaldate(datetime.date(2007, 6, 5)) 'Jun 05 2007' >>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=1)) 'a second ago' >>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=3600)) 'an hour ago' 

Of course, it doesn't give you exactly the answer you were looking for (which is, indeed, str(timeA - timeB), but I have found that once you go beyond a few hours, the display becomes quickly unreadable. humanize has support for much larger values that are human-readable, and is also well localized.

It's inspired by Django's contrib.humanize module, apparently, so since you are using Django, you should probably use that.



回答8:

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) 


回答9:

I know that this is an old answered question, but I use datetime.utcfromtimestamp() for this. It takes the number of seconds and returns a datetime that can be formatted like any other datetime.

duration = datetime.utcfromtimestamp(end - begin) print duration.strftime('%H:%M') 

As long as you stay in the legal ranges for the time parts this should work, i.e. it doesn't return 1234:35 as hours are



回答10:

Here is a general purpose function for converting either a timedelta object or a regular number (in the form of seconds or minutes, etc.) to a nicely formatted string. I took mpounsett's fantastic answer on a duplicate question, made it a bit more flexible, improved readibility, and added documentation.

You will find that it is the most flexible answer here so far since it allows you to:

  1. Customize the string format on the fly instead of it being hard-coded.
  2. Leave out certain time intervals without a problem (see examples below).

Function:

from string import Formatter from datetime import timedelta  def strfdelta(tdelta, fmt='{D:02}d {H:02}h {M:02}m {S:02}s', inputtype='timedelta'):     """Convert a datetime.timedelta object or a regular number to a custom-     formatted string, just like the stftime() method does for datetime.datetime     objects.      The fmt argument allows custom formatting to be specified.  Fields can      include seconds, minutes, hours, days, and weeks.  Each field is optional.      Some examples:         '{D:02}d {H:02}h {M:02}m {S:02}s' --> '05d 08h 04m 02s' (default)         '{W}w {D}d {H}:{M:02}:{S:02}'     --> '4w 5d 8:04:02'         '{D:2}d {H:2}:{M:02}:{S:02}'      --> ' 5d  8:04:02'         '{H}h {S}s'                       --> '72h 800s'      The inputtype argument allows tdelta to be a regular number instead of the       default, which is a datetime.timedelta object.  Valid inputtype strings:          's', 'seconds',          'm', 'minutes',          'h', 'hours',          'd', 'days',          'w', 'weeks'     """      # Convert tdelta to integer seconds.     if inputtype == 'timedelta':         remainder = int(tdelta.total_seconds())     elif inputtype in ['s', 'seconds']:         remainder = int(tdelta)     elif inputtype in ['m', 'minutes']:         remainder = int(tdelta)*60     elif inputtype in ['h', 'hours']:         remainder = int(tdelta)*3600     elif inputtype in ['d', 'days']:         remainder = int(tdelta)*86400     elif inputtype in ['w', 'weeks']:         remainder = int(tdelta)*604800      f = Formatter()     desired_fields = [field_tuple[1] for field_tuple in f.parse(fmt)]     possible_fields = ('W', 'D', 'H', 'M', 'S')     constants = {'W': 604800, 'D': 86400, 'H': 3600, 'M': 60, 'S': 1}     values = {}     for field in possible_fields:         if field in desired_fields and field in constants:             values[field], remainder = divmod(remainder, constants[field])     return f.format(fmt, **values) 

Demo:

>>> td = timedelta(days=2, hours=3, minutes=5, seconds=8, microseconds=340)  >>> print strfdelta(td) 02d 03h 05m 08s  >>> print strfdelta(td, '{D}d {H}:{M:02}:{S:02}') 2d 3:05:08  >>> print strfdelta(td, '{D:2}d {H:2}:{M:02}:{S:02}')  2d  3:05:08  >>> print strfdelta(td, '{H}h {S}s') 51h 308s  >>> print strfdelta(12304, inputtype='s') 00d 03h 25m 04s  >>> print strfdelta(620, '{H}:{M:02}', 'm') 10:20  >>> print strfdelta(49, '{D}d {H}h', 'h') 2d 1h 


回答11:

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.



回答12:

I would seriously consider the Occam's Razor approach here:

td = str(timedelta).split('.')[0] 

This returns a string without the microseconds

If you want to regenerate the datetime.timedelta object, just do this:

h,m,s = re.split(':', td) new_delta = datetime.timedelta(hours=int(h),minutes=int(m),seconds=int(s)) 

2 years in, I love this language!



回答13:

def seconds_to_time_left_string(total_seconds):     s = int(total_seconds)     years = s // 31104000     if years > 1:         return '%d years' % years     s = s - (years * 31104000)     months = s // 2592000     if years == 1:         r = 'one year'         if months > 0:             r += ' and %d months' % months         return r     if months > 1:         return '%d months' % months     s = s - (months * 2592000)     days = s // 86400     if months == 1:         r = 'one month'         if days > 0:             r += ' and %d days' % days         return r     if days > 1:         return '%d days' % days     s = s - (days * 86400)     hours = s // 3600     if days == 1:         r = 'one day'         if hours > 0:             r += ' and %d hours' % hours         return r      s = s - (hours * 3600)     minutes = s // 60     seconds = s - (minutes * 60)     if hours >= 6:         return '%d hours' % hours     if hours >= 1:         r = '%d hours' % hours         if hours == 1:             r = 'one hour'         if minutes > 0:             r += ' and %d minutes' % minutes         return r     if minutes == 1:         r = 'one minute'         if seconds > 0:             r += ' and %d seconds' % seconds         return r     if minutes == 0:         return '%d seconds' % seconds     if seconds == 0:         return '%d minutes' % minutes     return '%d minutes and %d seconds' % (minutes, seconds)  for i in range(10):     print pow(8, i), seconds_to_time_left_string(pow(8, i))   Output: 1 1 seconds 8 8 seconds 64 one minute and 4 seconds 512 8 minutes and 32 seconds 4096 one hour and 8 minutes 32768 9 hours 262144 3 days 2097152 24 days 16777216 6 months 134217728 4 years 


回答14:

I had a similar problem with the output of overtime calculation at work. The value should always show up in HH:MM, even when it is greater than one day and the value can get negative. I combined some of the shown solutions and maybe someone else find this solution useful. I realized that if the timedelta value is negative most of the shown solutions with the divmod method doesn't work out of the box:

def td2HHMMstr(td):   '''Convert timedelta objects to a HH:MM string with (+/-) sign'''   if td 

timedelta to HH:MM string:

td2HHMMstr(datetime.timedelta(hours=1, minutes=45)) '1:54'  td2HHMMstr(datetime.timedelta(days=2, hours=3, minutes=2)) '51:02'  td2HHMMstr(datetime.timedelta(hours=-3, minutes=-2)) '-3:02'  td2HHMMstr(datetime.timedelta(days=-35, hours=-3, minutes=-2)) '-843:02' 


回答15:

from django.utils.translation import ngettext  def localize_timedelta(delta):     ret = []     num_years = int(delta.days / 365)     if num_years > 0:         delta -= timedelta(days=num_years * 365)         ret.append(ngettext('%d year', '%d years', num_years) % num_years)      if delta.days > 0:         ret.append(ngettext('%d day', '%d days', delta.days) % delta.days)      num_hours = int(delta.seconds / 3600)     if num_hours > 0:         delta -= timedelta(hours=num_hours)         ret.append(ngettext('%d hour', '%d hours', num_hours) % num_hours)      num_minutes = int(delta.seconds / 60)     if num_minutes > 0:         ret.append(ngettext('%d minute', '%d minutes', num_minutes) % num_minutes)      return ' '.join(ret) 

This will produce:

>>> from datetime import timedelta >>> localize_timedelta(timedelta(days=3660, minutes=500)) '10 years 10 days 8 hours 20 minutes' 


回答16:

Please check this function - it converts timedelta object into string 'HH:MM:SS'

def format_timedelta(td):     hours, remainder = divmod(td.total_seconds(), 3600)     minutes, seconds = divmod(remainder, 60)     hours, minutes, seconds = int(hours), int(minutes), int(seconds)     if hours 


回答17:

t1 = datetime.datetime.strptime(StartTime, "%H:%M:%S %d-%m-%y")  t2 = datetime.datetime.strptime(EndTime, "%H:%M:%S %d-%m-%y")  return str(t2-t1) 

So for:

StartTime = '15:28:53 21-07-13' EndTime = '15:32:40 21-07-13' 

returns:

'0:03:47' 


回答18:

t = str(timedelta(seconds=500000))   print t[:-6] + ' hours ' + t[-5:-3] + ' mins' 


回答19:

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):

{{ sum.0 }}     {{ sum.1.hours|stringformat:"d" }}:{{ sum.1.minutes|stringformat:"#02.0d" }} 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!