Formato Python timedelta a cadena

Soy un novato de Python (2 semanas) y tengo problemas para formatear un objeto datetime.timedelta .

Esto es lo que estoy tratando de hacer: tengo una lista de objetos y uno de los miembros de la clase del objeto es un objeto timedelta que muestra la duración de un evento. Me gustaría mostrar esa duración en el formato de horas: minutos.

He intentado una variedad de métodos para hacer esto y estoy teniendo dificultades. Mi enfoque actual es agregar métodos a la clase para mis objetos que devuelven horas y minutos. Puedo obtener las horas al dividir los segundos de timedelta por 3600 y redondearlo. Estoy teniendo problemas para obtener los segundos restantes y convertir eso en minutos.

Por cierto, estoy usando Google AppEngine con Django Templates para su presentación.

Si alguien puede ayudar o sabe de una mejor manera de resolver esto, sería muy feliz.

Gracias,

Simplemente puede convertir el timedelta en una cadena con str (). Aquí hay un ejemplo:

 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 

Como sabe, puede obtener el total de segundos de un objeto timedelta accediendo al atributo .seconds .

Python proporciona la función incorporada divmod() que permite:

 s = 13420 hours, remainder = divmod(s, 3600) minutes, seconds = divmod(remainder, 60) print '{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds)) # result: 03:43:40 

o puede convertir a horas y rest utilizando una combinación de módulo y resta:

 # 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 '{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds)) # result: 03:43:40 
 >>> 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 

Al pasar el objeto timedelta a la función str() , se llama al mismo código de formato que usamos si simplemente print td . Como no desea los segundos, podemos dividir la cadena por dos puntos (3 partes) y volver a unirlos solo con las 2 primeras partes.

 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) 

Ya tiene un objeto timedelta, ¿por qué no usar su método integrado total_seconds () para convertirlo en segundos, luego usar divmod () para obtener horas y minutos?

 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) 

Esto funciona independientemente de si el delta del tiempo tiene días o años pares.

Personalmente uso la biblioteca de humanize para esto:

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

Por supuesto, no le da exactamente la respuesta que estaba buscando (que es, de hecho, str(timeA - timeB) , pero he encontrado que una vez que ha pasado más de unas pocas horas, la pantalla se vuelve rápidamente ilegible. soporte para valores mucho más grandes que son legibles por humanos, y también están bien localizados.

Está inspirado en el módulo contrib.humanize de Django, al parecer, así que ya que estás usando Django, probablemente deberías usar eso.

Sé que esta es una pregunta antigua, pero uso datetime.utcfromtimestamp() para esto. Toma la cantidad de segundos y devuelve una datetime y datetime que se puede formatear como cualquier otra datetime y datetime .

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

Mientras permanezca en los rangos legales durante el tiempo que deben funcionar, es decir, no devuelve 1234: 35 ya que las horas son <= 23.

El interrogador quiere un formato mejor que el típico:

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

Entonces, realmente hay dos formatos, uno donde los días son 0 y se omite, y otro donde hay texto “n días, h: m: s”. Pero, los segundos pueden tener fracciones y no hay ceros iniciales en las impresiones, por lo que las columnas están desordenadas.

Aquí está mi rutina, si te gusta:

 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 

esto devuelve la salida como formato dd: hh: mm: ss:

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

Pensé en agregar años a esto, pero esto queda como un ejercicio para el lector, ya que la salida es segura a más de 1 año:

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

Mis objetos datetime.timedelta fueron más de un día. Así que aquí hay un problema más. Toda la discusión anterior supone menos de un día. Un timedelta es en realidad una tupla de días, segundos y microsegundos. La discusión anterior debe usar td.seconds como lo hizo joe, pero si tiene días, NO se incluye en el valor de segundos.

Estoy obteniendo un lapso de tiempo entre 2 fechas y días y horas de impresión.

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

Aquí hay una función de propósito general para convertir un objeto timedelta o un número regular (en forma de segundos o minutos, etc.) a una cadena con un formato agradable. Tomé la fantástica respuesta de mpounsett en una pregunta duplicada, la hice un poco más flexible, mejoré la legibilidad y añadí documentación.

Encontrarás que es la respuesta más flexible aquí hasta ahora, ya que te permite:

  1. Personalice el formato de cadena sobre la marcha en lugar de que esté codificado.
  2. Deje de lado ciertos intervalos de tiempo sin problemas (vea los ejemplos a continuación).

Función:

 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) 

Manifestación:

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

Consideraría seriamente el enfoque de Occam’s Razor aquí:

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

Esto devuelve una cadena sin los microsegundos

Si desea regenerar el objeto datetime.timedelta, simplemente haga esto:

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

2 años en, me encanta este idioma!

Siguiendo el valor de ejemplo de Joe anterior, usaría el operador aritmético de módulo, así:

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

Tenga en cuenta que la división de enteros en Python se redondea de forma predeterminada; Si desea ser más explícito, use math.floor () o math.ceil () según corresponda.

humanfriendly biblioteca de Python humanfriendly del ser humanfriendly para hacer esto, funciona muy bien.

 import humanfriendly from datetime import timedelta delta = timedelta(seconds = 321) humanfriendly.format_timespan(delta) '5 minutes and 21 seconds' 

Disponible en https://pypi.org/project/humanfriendly/

 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 rs = 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 

Tuve un problema similar con la salida del cálculo de horas extra en el trabajo. El valor siempre debe aparecer en HH: MM, incluso cuando es mayor que un día y el valor puede ser negativo. Combiné algunas de las soluciones mostradas y tal vez alguien más encuentre útil esta solución. Me di cuenta de que si el valor de timedelta es negativo, la mayoría de las soluciones que se muestran con el método divmod no funcionan de manera inmediata:

 def td2HHMMstr(td): '''Convert timedelta objects to a HH:MM string with (+/-) sign''' if td < datetime.timedelta(seconds=0): sign='-' td = -td else: sign = '' tdhours, rem = divmod(td.total_seconds(), 3600) tdminutes, rem = divmod(rem, 60) tdstr = '{}{:}:{:02d}'.format(sign, int(tdhours), int(tdminutes)) return tdstr 

timedelta a HH: MM cadena:

 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' 
 import datetime hours = datetime.timedelta(hours=16, minutes=30) print((datetime.datetime(1,1,1) + hours).strftime('%H:%M')) 
 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) 

Esto producirá:

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

Compruebe esta función: convierte el objeto timedelta en la cadena ‘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 < 10: hours = '0%s' % int(hours) if minutes < 10: minutes = '0%s' % minutes if seconds < 10: seconds = '0%s' % seconds return '%s:%s:%s' % (hours, minutes, seconds) 

Si ya tienes un obj timedelta, simplemente convierte ese obj en una cadena. Quite los últimos 3 caracteres de la cadena e imprima. Esto truncará la parte de segundos e imprimirá el rest en el formato Horas: Minutos.

 t = str(timedeltaobj) print t[:-3] 
 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) 

Así que para

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

devoluciones:

 '0:03:47' 

Gracias a todos por su ayuda. Tomé muchas de tus ideas y las puse juntas, déjame saber lo que piensas.

Agregué dos métodos a la clase como esto:

 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 

En mi django usé esto (la sum es el objeto y está en un diccionario):

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