web-dev-qa-db-fra.com

Formatage d'objets timedelta

J'ai deux datetime objets. J'ai besoin de calculer le timedelta entre eux, puis d'afficher la sortie dans un format spécifique.

Alpha_TimeObj = datetime.datetime(int(AlphaTime.strftime('%Y')), int(AlphaTime.strftime('%m')), int(AlphaTime.strftime('%d')), int(AlphaTime.strftime('%H')), int(AlphaTime.strftime('%M')), int(AlphaTime.strftime('%S')))
Beta_TimeObj = datetime.datetime(int(BetaTime.strftime('%Y')), int(BetaTime.strftime('%m')), int(BetaTime.strftime('%d')), int(BetaTime.strftime('%H')), int(BetaTime.strftime('%M')), int(BetaTime.strftime('%S')))
Turnaround_TimeObj = Beta_TimeObj  - Alpha_TimeObj

Un exemple de cette Turnaround_TimeObj le delta horaire est "2 jours, 22:13:45". Je souhaite formater la sortie mais je ne peux pas le faire.

print Turnaround_TimeObj.strftime('%H hrs %M mins %S secs')

ne fonctionne pas.

Je sais qu'une façon de le faire sera de le convertir en secondes puis de divmoder pour obtenir le formatage requis. un péché:

totalSeconds = Turnaround_TimeObj.seconds
hours, remainder = divmod(totalSeconds, 3600)
minutes, seconds = divmod(remainder, 60)
print '%s:%s:%s' % (hours, minutes, seconds)

Mais je me demandais si je pouvais le faire sur une seule ligne en utilisant n'importe quelle fonction date-heure comme strftime.

Edit: La conversion en secondes ne fonctionne pas non plus. Si je convertis le delta de temps "1 jour, 3:42:54" en secondes en utilisant

totalSeconds = Turnaround_TimeObj.seconds

totalSeconds valeur est affichée comme 13374 au lieu de 99774. c'est-à-dire qu'elle ignore la valeur "jour".

30
Rishav Sharan

mais je me demandais si je pouvais le faire sur une seule ligne en utilisant une fonction date-heure comme strftime.

Pour autant que je sache, il n'y a pas de méthode intégrée à timedelta qui le fasse. Si vous le faites souvent, vous pouvez créer votre propre fonction, par ex.

def strfdelta(tdelta, fmt):
    d = {"days": tdelta.days}
    d["hours"], rem = divmod(tdelta.seconds, 3600)
    d["minutes"], d["seconds"] = divmod(rem, 60)
    return fmt.format(**d)

Usage:

>>> print strfdelta(delta_obj, "{days} days {hours}:{minutes}:{seconds}")
1 days 20:18:12
>>> print strfdelta(delta_obj, "{hours} hours and {minutes} to go")
20 hours and 18 to go

Si vous souhaitez utiliser un format de chaîne plus proche de celui utilisé par strftime, nous pouvons utiliser string.Template .

from string import Template

class DeltaTemplate(Template):
    delimiter = "%"

def strfdelta(tdelta, fmt):
    d = {"D": tdelta.days}
    d["H"], rem = divmod(tdelta.seconds, 3600)
    d["M"], d["S"] = divmod(rem, 60)
    t = DeltaTemplate(fmt)
    return t.substitute(**d)

Usage:

>>> print strfdelta(delta_obj, "%D days %H:%M:%S")
1 days 20:18:12
>>> print strfdelta(delta_obj, "%H hours and %M to go")
20 hours and 18 to go

la valeur totalSeconds est indiquée comme 13374 au lieu de 99774. ie. son ignorer la valeur "jour".

Notez dans l'exemple ci-dessus que vous pouvez utiliser timedelta.days pour obtenir la valeur "jour".

Alternativement, à partir de Python 2.7, timedelta a une méthode total_seconds () qui retourne le nombre total de secondes contenues dans la durée.

48
Shawn Chin

En Python2.7 ou plus récent, vous pouvez utiliser la méthode total_seconds :

import datetime as dt

turnaround = dt.timedelta(days = 1, hours = 3, minutes = 42, seconds = 54)

total_seconds = int(turnaround.total_seconds())
hours, remainder = divmod(total_seconds,60*60)
minutes, seconds = divmod(remainder,60)

print('{} hrs {} mins {} secs'.format(hours,minutes,seconds))

les rendements

27 hrs 42 mins 54 secs

En Python2.6 ou version antérieure, vous pouvez calculer le total_seconds toi même:

total_seconds = turnaround.seconds + turnaround.days*24*60*60

(pour la formule plus générale, y compris les microsecondes, voir le lien ci-dessus).

15
unutbu

La réponse de Shawn Chin est bonne, mais un problème avec cela est que si vous sautez un élément particulier dans votre format (comme dans son deuxième exemple qui n'a que des heures et des minutes .. pas des jours ou des secondes) alors ce temps disparaît de votre résultat . Vous pouvez le modifier pour résoudre ce problème et obtenir des résultats plus précis en ne traitant que les mots clés qui apparaissent réellement dans la chaîne de format.

class DeltaTemplate(Template):
    delimiter = '%'

def strfdelta(tdelta, fmt):
    d = {}
    l = {'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    rem = int(tdelta.total_seconds())

    for k in ( 'D', 'H', 'M', 'S' ):
        if "%{}".format(k) in fmt:
            d[k], rem = divmod(rem, l[k])

    t = DeltaTemplate(fmt)
    return t.substitute(**d)

Usage:

>>> print strfdelta(delta_obj, "%D days %H:%M:%S")
1 days 20:18:12
>>> print strfdelta(delta_obj, "%H hours and %M to go")
44 hours and 18 to go

Cependant, le formatage est inflexible, car vous ne pouvez pas appliquer de chaînes de conversion et vous retrouver avec des choses laides comme ceci:

>>> delta_obj = timedelta(minutes=5, seconds=2)
>>> print strfdelta(delta_obj, "%H:%M:%S")
0:5:2

Cependant, vous pouvez adopter la même approche et l'appliquer à string.Formatter au lieu de string.Template et obtenir quelque chose de bien meilleur.

from string import Formatter

def strfdelta(tdelta, fmt):
    f = Formatter()
    d = {}
    l = {'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    k = map( lambda x: x[1], list(f.parse(fmt)))
    rem = int(tdelta.total_seconds())

    for i in ('D', 'H', 'M', 'S'):
        if i in k and i in l.keys():
            d[i], rem = divmod(rem, l[i])

    return f.format(fmt, **d)

Usage:

>>> delta_obj = timedelta(days=1, hours=20, minutes=18, seconds=12)
>>> print strfdelta(delta_obj, "{D} days {H}:{M}:{S}")
1 days 20:18:12
>>> print strfdelta(delta_obj, "{H} hours and {M} to go") 
44 hours and 18 to go

>>> delta_obj = timedelta(minutes=5, seconds=2)                     
>>> print strfdelta(delta_obj, "{H:02}h{M:02}m{S:02}s")  
00h05m02s
>>> print strfdelta(delta_obj, "{H:02}:{M:02}:{S:02}")
00:05:02
14
mpounsett

Une légère variante sur Shawn Chin 's answer - qui répond également à un issue soulevé par mpouncett - remplit le heures, minutes et secondes avec des zéros au début pour garantir que les 3 éléments utilisent 2 places (plus cohérent avec les spécifications de ces champs dans strftime ):

from string import Template

class DeltaTemplate(Template):
    delimiter = "%"

def strfdelta(tdelta, fmt):
    d = {"D": tdelta.days}
    hours, rem = divmod(tdelta.seconds, 3600)
    minutes, seconds = divmod(rem, 60)
    d["H"] = '{:02d}'.format(hours)
    d["M"] = '{:02d}'.format(minutes)
    d["S"] = '{:02d}'.format(seconds)
    t = DeltaTemplate(fmt)
    return t.substitute(**d)

Voici un test pour quelques exemples de valeurs:

from datetime import timedelta
for seconds in [0, 1, 59, 60, 61, 3599, 3600, 3601]:
    print strfdelta(timedelta(0, seconds), '%H:%M:%S')

Et voici la sortie:

00:00:00
00:00:01
00:00:59
00:01:00
00:01:01
00:59:59
01:00:00
01:00:01
12
gumption

Vous pouvez utiliser le module dateutil qui a l'objet relativedelta le plus convivial.

import dateutil
import datetime

alpha = datetime.datetime(2012, 1, 16, 6, 0)
beta = datetime.datetime(2012, 1, 18, 10, 42, 57, 230301)
delta = dateutil.relativedelta(beta, alpha)

cela vous donne un delta d'objet qui ressemble

>>> delta
relativedelta(days=+2, hours=+4, minutes=+42, seconds=+57, microseconds=+230301)

Vous pouvez alors faire

print('turnaround %i hrs %i mins %i secs' % (delta.days * 24 + delta.hours, delta.minutes, delta.seconds)

)

7
dabhaid
def to_time(seconds):
  delta = datetime.timedelta(seconds=seconds)
  return str(delta.days) + 'd ' + (datetime.datetime.utcfromtimestamp(0) + delta).strftime('%H:%M')
3
Daniel F

Les réponses ci-dessus ne semblent pas gérer les valeurs de timedelta négatives (telles que produites par exemple par pytz pour les fuseaux horaires "gauche" de UTC ). Comme selon la documentation , les objets timedelta sont normalisés et un timedelta négatif est représenté par un attribut d'instance day négatif. De la documentation:

Notez que la normalisation des valeurs négatives peut être surprenante au premier abord.

et cela

Les représentations de chaînes d'objets timedelta sont normalisées de la même manière que leur représentation interne. Cela conduit à des résultats quelque peu inhabituels pour les timedeltas négatifs.

Par exemple:

>>> td = timedelta(seconds=-30)
>>> str(td)
'-1 day, 23:59:30'
>>> repr(td)
'datetime.timedelta(-1, 86370)'

Dans cet exemple, timedelta, à la fois réponse acceptée par Shawn Chin et réponse de gumption return '23:59:30' et réponse de mpounsett'-1:59:30'.

Je pense que pour imprimer des timedeltas négatifs et positifs d'une manière plus lisible, nous devons gérer explicitement le signe et la valeur absolue d'un objet de fuseau horaire:

def strfdelta(td, fmt):

    # Get the timedelta’s sign and absolute number of seconds.
    sign = "-" if td.days < 0 else "+"
    secs = abs(td).total_seconds()

    # Break the seconds into more readable quantities.
    days, rem = divmod(secs, 86400)  # seconds per day: 24 * 60 * 60
    hours, rem = divmod(rem, 3600)  # seconds per hour: 60 * 60
    mins, secs = divmod(rem, 60)

    # Format (as per above answers) and return the result string.
    t = DeltaTemplate(fmt)
    return t.substitute(
        s=sign,
        D="{:d}".format(int(days)),
        H="{:02d}".format(int(hours)),
        M="{:02d}".format(int(mins)),
        S="{:02d}".format(int(secs)),
        )

Cette fonction renvoie une représentation sous forme de chaîne plus lisible:

>>> strfdelta(td, "%s%H:%M:%S")  # Note that %s refers to the timedelta’s sign.
'-00:00:30'
>>> strfdelta(timedelta(days=-1), "%s%D %H:%M:%S")
'-1 00:00:00'
>>> strfdelta(timedelta(days=-1, minutes=5), "%s%D %H:%M:%S")
'-0 23:55:00'
>>> strfdelta(timedelta(days=-1, minutes=-5), "%s%D %H:%M:%S")
'-1 00:05:00'

… Ou dans le contexte plus pratique des fuseaux horaires:

>>> import pytz
>>> import datetime
>>> td = pytz.timezone("Canada/Newfoundland").utcoffset(datetime.datetime.now())
>>> td
datetime.timedelta(-1, 77400)
>>> strfdelta(td, fmt="%s%H:%M")
'-02:30'
>>> td = pytz.timezone("Australia/Eucla").utcoffset(datetime.datetime.now())
>>> td
datetime.timedelta(0, 31500)
>>> strfdelta(td, fmt="%s%H:%M")
'+08:45'
2
Jens