Cette question est similaire à cette question sur la soustraction de dates avec Python , mais pas identique. Je ne traite pas de chaînes, je dois comprendre la différence entre deux horodatages Epoch et produire la différence dans un format lisible par l'homme.
Par exemple:
32 Seconds
17 Minutes
22.3 Hours
1.25 Days
3.5 Weeks
2 Months
4.25 Years
Alternativement, je voudrais exprimer la différence comme ceci:
4 years, 6 months, 3 weeks, 4 days, 6 hours 21 minutes and 15 seconds
Je ne pense pas pouvoir utiliser strptime
, car je travaille avec la différence de deux dates. Je pourrais écrire quelque chose pour faire cela, mais je suis à peu près sûr qu'il y a déjà quelque chose d'écrit que je pourrais utiliser.
Quel module serait approprié? Est-ce que je manque juste quelque chose dans time
? Mon voyage en Python ne fait que commencer, s'il s'agit bien d'un doublon, c'est que je n'ai pas trouvé ce que je devais rechercher.
Pour des raisons de précision, le calendrier de l’année en cours m’intéresse beaucoup.
Vous pouvez utiliser le merveilleux dateutil module et son relativedelta class:
import datetime
import dateutil.relativedelta
dt1 = datetime.datetime.fromtimestamp(123456789) # 1973-11-29 22:33:09
dt2 = datetime.datetime.fromtimestamp(234567890) # 1977-06-07 23:44:50
rd = dateutil.relativedelta.relativedelta (dt2, dt1)
print "%d years, %d months, %d days, %d hours, %d minutes and %d seconds" % (rd.years, rd.months, rd.days, rd.hours, rd.minutes, rd.seconds)
# 3 years, 6 months, 9 days, 1 hours, 11 minutes and 41 seconds
Cela ne compte pas les semaines, mais cela ne devrait pas être trop difficile à ajouter.
Une petite amélioration par rapport à la solution de @ Schnouki avec une compréhension en une seule ligne. Affiche également le pluriel en cas d'entités plurielles (comme heures)
Import relativedelta
>>> from dateutil.relativedelta import relativedelta
Une fonction lambda
>>> attrs = ['years', 'months', 'days', 'hours', 'minutes', 'seconds']
>>> human_readable = lambda delta: ['%d %s' % (getattr(delta, attr), getattr(delta, attr) > 1 and attr or attr[:-1])
... for attr in attrs if getattr(delta, attr)]
Exemple d'utilisation:
>>> human_readable(relativedelta(minutes=125))
['2 hours', '5 minutes']
>>> human_readable(relativedelta(hours=(24 * 365) + 1))
['365 days', '1 hour']
J'ai eu exactement le même problème un peu plus tôt aujourd'hui et je ne pouvais rien trouver dans les bibliothèques standard que je pouvais utiliser. C'est ce que j'ai écrit:
#!/usr/bin/env python
INTERVALS = [1, 60, 3600, 86400, 604800, 2419200, 29030400]
NAMES = [('second', 'seconds'),
('minute', 'minutes'),
('hour', 'hours'),
('day', 'days'),
('week', 'weeks'),
('month', 'months'),
('year', 'years')]
def humanize_time(amount, units):
"""
Divide `amount` in time periods.
Useful for making time intervals more human readable.
>>> humanize_time(173, 'hours')
[(1, 'week'), (5, 'hours')]
>>> humanize_time(17313, 'seconds')
[(4, 'hours'), (48, 'minutes'), (33, 'seconds')]
>>> humanize_time(90, 'weeks')
[(1, 'year'), (10, 'months'), (2, 'weeks')]
>>> humanize_time(42, 'months')
[(3, 'years'), (6, 'months')]
>>> humanize_time(500, 'days')
[(1, 'year'), (5, 'months'), (3, 'weeks'), (3, 'days')]
"""
result = []
unit = map(lambda a: a[1], NAMES).index(units)
# Convert to seconds
amount = amount * INTERVALS[unit]
for i in range(len(NAMES)-1, -1, -1):
a = amount / INTERVALS[i]
if a > 0:
result.append( (a, NAMES[i][1 % a]) )
amount -= a * INTERVALS[i]
return result
if __== "__main__":
import doctest
doctest.testmod()
Vous pouvez utiliser dateutil.relativedelta()
pour calculer le delta temporel précis et l’humaniser avec ce script.
def humanize_time(amount, units = 'seconds'):
def process_time(amount, units):
INTERVALS = [ 1, 60,
60*60,
60*60*24,
60*60*24*7,
60*60*24*7*4,
60*60*24*7*4*12,
60*60*24*7*4*12*100,
60*60*24*7*4*12*100*10]
NAMES = [('second', 'seconds'),
('minute', 'minutes'),
('hour', 'hours'),
('day', 'days'),
('week', 'weeks'),
('month', 'months'),
('year', 'years'),
('century', 'centuries'),
('millennium', 'millennia')]
result = []
unit = map(lambda a: a[1], NAMES).index(units)
# Convert to seconds
amount = amount * INTERVALS[unit]
for i in range(len(NAMES)-1, -1, -1):
a = amount // INTERVALS[i]
if a > 0:
result.append( (a, NAMES[i][1 % a]) )
amount -= a * INTERVALS[i]
return result
rd = process_time(int(amount), units)
cont = 0
for u in rd:
if u[0] > 0:
cont += 1
buf = ''
i = 0
for u in rd:
if u[0] > 0:
buf += "%d %s" % (u[0], u[1])
cont -= 1
if i < (len(rd)-1):
if cont > 1:
buf += ", "
else:
buf += " and "
i += 1
return buf
Exemple d'utilisation:
>>> print humanize_time(234567890 - 123456789)
3 years, 9 months, 3 weeks, 5 days, 11 minutes and 41 seconds
>>> humanize_time(9, 'weeks')
2 months and 1 week
Avantage (vous n'avez pas besoin de tiers!).
Amélioration de l'algorithme "Liudmil Mitev". (Merci!)
Vieille question, mais personnellement, j'aime beaucoup cette approche:
import datetime
import math
def human_time(*args, **kwargs):
secs = float(datetime.timedelta(*args, **kwargs).total_seconds())
units = [("day", 86400), ("hour", 3600), ("minute", 60), ("second", 1)]
parts = []
for unit, mul in units:
if secs / mul >= 1 or mul == 1:
if mul > 1:
n = int(math.floor(secs / mul))
secs -= n * mul
else:
n = secs if secs != int(secs) else int(secs)
parts.append("%s %s%s" % (n, unit, "" if n == 1 else "s"))
return ", ".join(parts)
human_time(seconds=3721)
# -> "1 hour, 2 minutes, 1 second"
Si vous voulez séparer la partie secondes avec un "et" faire:
"%s and %s" % Tuple(human_time(seconds=3721).rsplit(", ", 1))
# -> "1 hour, 2 minutes and 1 second"
En voici un plus court pour l'intervalle en secondes et dans la journée (t <86400). Utile si vous travaillez avec des horodatages Unix (secondes depuis Epoch, UTC).
t = 45678
print('%d hours, %d minutes, %d seconds' % (t//3600, t%3600//60, t%60))
Peut être étendu davantage (t // 86400, ...).