python3 - use datetime python




Formato Python timedelta a cadena (14)

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,


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

Como sabe, puede obtener los 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 al resto 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

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!


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)

Personalmente uso la biblioteca 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 descubrí 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.


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

t = str(timedeltaobj) 

print t[:-3]

Siguiendo el ejemplo del valor 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.


Tuve un problema similar con el resultado 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'

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.


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

Pasar el objeto timedelta a la función str() 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 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

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'

import datetime
hours = datetime.timedelta(hours=16, minutes=30)
print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))






timedelta