web-dev-qa-db-fra.com

Format Python timedelta en chaîne

Je suis un débutant en Python (2 semaines) et je ne parviens pas à formater un objet datetime.timedelta.

Voici ce que j'essaie de faire: J'ai une liste d'objets et l'un des membres de la classe de l'objet est un objet timedelta qui indique la durée d'un événement. Je voudrais afficher cette durée au format heures: minutes.

J'ai essayé diverses méthodes pour ce faire et j'ai des difficultés. Mon approche actuelle consiste à ajouter des méthodes à la classe pour mes objets renvoyant des heures et des minutes. Je peux obtenir les heures en divisant les heures timedelta par 3600 et en les arrondissant. J'ai du mal à obtenir les secondes restantes et à les convertir en minutes.

En passant, j'utilise Google AppEngine avec Django Templates pour la présentation.

Si quelqu'un peut aider ou connaît un meilleur moyen de résoudre ce problème, je serais très heureux.

Merci,

214
mawcs

Vous pouvez simplement convertir timedelta en chaîne avec str (). Voici un exemple:

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

Comme vous le savez, vous pouvez obtenir le total en secondes d'un objet timedelta en accédant à l'attribut .seconds.

Python fournit la fonction intégrée divmod() qui permet:

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

ou vous pouvez convertir en heures et le reste en combinant modulo et soustraction:

# 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
147
John Fouhy
>>> 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

Passer l'objet timedelta à la fonction str() appelle le même code de formatage utilisé si nous tapons simplement print td. Puisque vous ne voulez pas les secondes, nous pouvons séparer la chaîne par deux points (3 parties) et la reconstituer avec seulement les 2 premières parties.

53
joeforker
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)
33
Adam Jacob Muller

Il a déjà un objet timedelta, alors pourquoi ne pas utiliser sa méthode intégrée total_seconds () pour le convertir en secondes, puis utiliser divmod () pour obtenir des heures et des 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)

Cela fonctionne que le delta temporel ait même des jours ou des années.

26
Bill Kidd

Personnellement, j'utilise la bibliothèque humanize pour cela:

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

Bien sûr, cela ne vous donne pas exactement la réponse que vous recherchiez (c'est-à-dire, str(timeA - timeB), mais j'ai constaté qu'une fois que vous avez dépassé quelques heures, l'affichage devient rapidement illisible. humanize a un support. pour des valeurs beaucoup plus grandes qui sont lisibles par l'homme et qui sont également bien localisées.

Il est apparemment inspiré du module contrib.humanize de Django, donc puisque vous utilisez Django, vous devriez probablement l'utiliser.

18
anarcat

Je sais que c’est une vieille question à réponse, mais j’utilise datetime.utcfromtimestamp() pour cela. Cela prend le nombre de secondes et retourne une datetime qui peut être formatée comme n'importe quelle autre datetime.

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

Tant que vous restez dans les plages légales pour les tranches horaires, cela devrait fonctionner, c'est-à-dire qu'il ne renvoie pas 1234: 35 car les heures sont <= 23.

15
sjngm

Le questionneur veut un format plus agréable que le format typique:

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

Donc, en réalité, il existe deux formats, l’un dans lequel les jours sont 0 et omis, et l’autre dans lequel il est écrit "n days, h: m: s". Mais, les secondes peuvent avoir des fractions, et il n'y a pas de zéros au début des impressions, donc les colonnes sont en désordre.

Voici ma routine, si vous l'aimez:

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

cela retourne la sortie au format dd: hh: mm: ss:

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

J'ai bien pensé à ajouter des années à cela, mais cela reste un exercice pour le lecteur, car le résultat est sûr à plus d'un an:

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

Mes objets datetime.timedelta ont dépassé un jour. Donc, voici un autre problème. Toute la discussion ci-dessus suppose moins d'une journée. timedelta est en fait un tuple de jours, de secondes et de microsecondes. La discussion ci-dessus devrait utiliser td.seconds comme joe, mais si vous avez des jours, il n’EST PAS inclus dans la valeur en secondes 

Je reçois un intervalle de temps entre 2 dates-heures et l'impression de jours et d'heures.

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

Voici une fonction à usage général permettant de convertir un objet timedelta ou un nombre régulier (sous forme de secondes ou de minutes, etc.) en une chaîne bien formatée. J'ai pris la réponse fantastique de mpounsett sur une question dupliquée, l'a rendue un peu plus flexible, a amélioré la lisibilité et ajouté de la documentation. 

Vous constaterez que c’est la réponse la plus flexible à ce jour car elle vous permet de:

  1. Personnalisez le format de chaîne à la volée au lieu de le coder en dur.
  2. Laissez certains intervalles de temps sans problème (voir les exemples ci-dessous).

Une fonction:

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)

Démo:

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

Je considérerais sérieusement l'approche de Razor d'Occam ici:

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

Ceci retourne une chaîne sans les microsecondes.

Si vous souhaitez régénérer l'objet datetime.timedelta, procédez comme suit:

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

2 ans, j'adore cette langue!

8
Pyrotherm

En suivant l'exemple de Joe ci-dessus, j'utiliserais l'opérateur arithmétique à module:

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

Notez que la division entière en Python arrondit les valeurs par défaut; si vous voulez être plus explicite, utilisez math.floor () ou math.ceil () selon le cas.

6
UltraNurd

J'ai utilisé la bibliothèque humanfriendly python pour le faire, cela fonctionne très bien.

import humanfriendly
from datetime import timedelta
delta = timedelta(seconds = 321)
humanfriendly.format_timespan(delta)

'5 minutes and 21 seconds'

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

3
Dhiraj Gupta
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
3
Veselin Penev

J'ai eu un problème similaire avec la sortie du calcul des heures supplémentaires au travail. La valeur doit toujours apparaître dans HH: MM, même si elle est supérieure à un jour et que la valeur peut devenir négative. J'ai combiné certaines des solutions présentées et peut-être que quelqu'un d'autre trouve cette solution utile. Je me suis rendu compte que si la valeur timedelta est négative, la plupart des solutions présentées avec la méthode divmod ne fonctionnent pas immédiatement:

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 à HH: MM chaîne:

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

Cela produira:

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

Veuillez vérifier cette fonction - elle convertit l'objet timedelta en chaîne '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)
0
MobilePro.pl