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,
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
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
>>> 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.
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)
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.
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.
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.
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'
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)
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:
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
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!
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.
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/
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
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'
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)
Cela produira:
>>> from datetime import timedelta
>>> localize_timedelta(timedelta(days=3660, minutes=500))
'10 years 10 days 8 hours 20 minutes'
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)