可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
- Customize the string format on the fly instead of it being hard-coded.
- 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" }} |