web-dev-qa-db-fra.com

Format de l'heure convivial en Python?

Python: Je dois afficher les temps de modification des fichiers au format "il y a 1 jour", "il y a deux heures".

Y a-t-il quelque chose de prêt à le faire? Ce devrait être en anglais.

54
flybywire

Le code a été initialement publié sur un article de blog "Python Pretty Date function" ( http://evaisse.com/post/93417709/python-pretty-date-function )

Il est reproduit ici car le compte du blog a été suspendu et la page n'est plus disponible.

def pretty_date(time=False):
    """
    Get a datetime object or a int() Epoch timestamp and return a
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
    'just now', etc
    """
    from datetime import datetime
    now = datetime.now()
    if type(time) is int:
        diff = now - datetime.fromtimestamp(time)
    Elif isinstance(time,datetime):
        diff = now - time
    Elif not time:
        diff = now - now
    second_diff = diff.seconds
    day_diff = diff.days

    if day_diff < 0:
        return ''

    if day_diff == 0:
        if second_diff < 10:
            return "just now"
        if second_diff < 60:
            return str(second_diff) + " seconds ago"
        if second_diff < 120:
            return "a minute ago"
        if second_diff < 3600:
            return str(second_diff / 60) + " minutes ago"
        if second_diff < 7200:
            return "an hour ago"
        if second_diff < 86400:
            return str(second_diff / 3600) + " hours ago"
    if day_diff == 1:
        return "Yesterday"
    if day_diff < 7:
        return str(day_diff) + " days ago"
    if day_diff < 31:
        return str(day_diff / 7) + " weeks ago"
    if day_diff < 365:
        return str(day_diff / 30) + " months ago"
    return str(day_diff / 365) + " years ago"
105
Jed Smith

Si vous utilisez Django , la nouveauté de la version 1.4 est le filtre de modèle naturaltime.

Pour l'utiliser, commencez par ajouter 'Django.contrib.humanize' à votre paramètre INSTALLED_APPS dans settings.py et {% load humanize %} au modèle dans lequel vous utilisez le filtre.

Ensuite, dans votre modèle, si vous avez une variable datetime my_date, vous pouvez imprimer sa distance par rapport au présent à l’aide de {{ my_date|naturaltime }}, qui sera rendu sous la forme 4 minutes ago.

Autres nouveautés dans Django 1.4.

Documentation sur naturaltime et autres filtres de l'ensemble Django.contrib.humanize.

29
plowman

En cherchant la même chose avec l'exigence supplémentaire de gérer les dates futures, j'ai trouvé ceci: http://pypi.python.org/pypi/py-pretty/1

Exemple de code (du site):

from datetime import datetime, timedelta
now = datetime.now()
hrago = now - timedelta(hours=1)
yesterday = now - timedelta(days=1)
tomorrow = now + timedelta(days=1)
dayafter = now + timedelta(days=2)

import pretty
print pretty.date(now)                      # 'now'
print pretty.date(hrago)                    # 'an hour ago'
print pretty.date(hrago, short=True)        # '1h ago'
print pretty.date(hrago, asdays=True)       # 'today'
print pretty.date(yesterday, short=True)    # 'yest'
print pretty.date(tomorrow)                 # 'tomorrow'
14
Karim

La réponse associée à Jed Smith est bonne et je l’utilise depuis environ un an, mais je pense qu’elle pourrait être améliorée de plusieurs façons:

  • Il est agréable de pouvoir définir chaque unité de temps en fonction de l'unité précédente, au lieu d'avoir des constantes "magiques" telles que 3600, 86400, etc., dispersées dans le code.
  • Après une longue utilisation, je me rends compte que je ne veux pas aller aussi passionnément à l'unité suivante. Exemple: 7 jours et 13 jours seront affichés comme "1 semaine"; Je préfère voir "7 jours" ou "13 jours" à la place.

Voici ce que je suis venu avec:

def PrettyRelativeTime(time_diff_secs):
    # Each Tuple in the sequence gives the name of a unit, and the number of
    # previous units which go into it.
    weeks_per_month = 365.242 / 12 / 7
    intervals = [('minute', 60), ('hour', 60), ('day', 24), ('week', 7),
                 ('month', weeks_per_month), ('year', 12)]

    unit, number = 'second', abs(time_diff_secs)
    for new_unit, ratio in intervals:
        new_number = float(number) / ratio
        # If the new number is too small, don't go to the next unit.
        if new_number < 2:
            break
        unit, number = new_unit, new_number
    shown_num = int(number)
    return '{} {}'.format(shown_num, unit + ('' if shown_num == 1 else 's'))

Remarquez comment chaque tuple dans intervals est facile à interpréter et à vérifier: un 'minute' est 60 secondes; un 'hour' est 60 minutes; etc. Le seul fudge est de mettre weeks_per_month à sa valeur moyenne; compte tenu de la demande, ça devrait aller. (Et notez qu’il est clair en un coup d’œil que les trois dernières constantes se multiplient par 365.242, le nombre de jours par an.)

L'un des inconvénients de ma fonction est qu'elle ne fait rien en dehors du motif "## unités": "Hier", "tout à l'heure", etc. sont tout de suite en sortie. Là encore, l'affiche originale ne demandait pas ces termes sophistiqués, je préfère donc ma fonction pour sa brièveté et la lisibilité de ses constantes numériques. :)

5
Chip Hogg

Vous pouvez aussi le faire avec arrow package

De page de github :

>>> import arrow
>>> utc = arrow.utcnow()
>>> utc = utc.replace(hours=-1)
>>> local.humanize()
'an hour ago'
5
vishes_shell

Le paquet ago le fournit. Appelez human sur un objet datetime pour obtenir une description lisible par l'homme de la différence. 

from ago import human
from datetime import datetime
from datetime import timedelta

ts = datetime.now() - timedelta(days=1, hours=5)

print(human(ts))
# 1 day, 5 hours ago

print(human(ts, precision=1))
# 1 day ago
4
russellballestrini

Il y a humanize package :

>>> from datetime import datetime, timedelta
>>> import humanize # $ pip install humanize
>>> humanize.naturaltime(datetime.now() - timedelta(days=1))
'a day ago'
>>> humanize.naturaltime(datetime.now() - timedelta(hours=2))
'2 hours ago'

Il prend en charge la localisation l10n , l'internationalisation i18n :

>>> _ = humanize.i18n.activate('ru_RU')
>>> print humanize.naturaltime(datetime.now() - timedelta(days=1))
день назад
>>> print humanize.naturaltime(datetime.now() - timedelta(hours=2))
2 часа назад
2
jfs

J'ai écrit un article de blog détaillé pour la solution sur http://sunilarora.org/17329071 Je publie également un extrait rapide ici.

from datetime import datetime
from dateutil.relativedelta import relativedelta

def get_fancy_time(d, display_full_version = False):
    """Returns a user friendly date format
    d: some datetime instace in the past
    display_second_unit: True/False
    """
    #some helpers lambda's
    plural = lambda x: 's' if x > 1 else ''
    singular = lambda x: x[:-1]
    #convert pluran (years) --> to singular (year)
    display_unit = lambda unit, name: '%s %s%s'%(unit, name, plural(unit)) if unit > 0 else ''

    #time units we are interested in descending order of significance
    tm_units = ['years', 'months', 'days', 'hours', 'minutes', 'seconds']

    rdelta = relativedelta(datetime.utcnow(), d) #capture the date difference
    for idx, tm_unit in enumerate(tm_units):
        first_unit_val = getattr(rdelta, tm_unit)
        if first_unit_val > 0:
            primary_unit = display_unit(first_unit_val, singular(tm_unit))
            if display_full_version and idx < len(tm_units)-1:
                next_unit = tm_units[idx + 1]
                second_unit_val = getattr(rdelta, next_unit)
                if second_unit_val > 0:
                    secondary_unit = display_unit(second_unit_val, singular(next_unit))
                    return primary_unit + ', '  + secondary_unit
            return primary_unit
    return None
1
sunil

Utilisation d'objets datetime avec tzinfo:

def time_elapsed(etime):
    # need to add tzinfo to datetime.utcnow
    now = datetime.datetime.utcnow().replace(tzinfo=etime.tzinfo)
    opened_for = (now - etime).total_seconds()
    names = ["seconds","minutes","hours","days","weeks","months"]
    modulos = [ 1,60,3600,3600*24,3600*24*7,3660*24*30]
    values = []
    for m in modulos[::-1]:
      values.append(int(opened_for / m))
      opened_for -= values[-1]*m
pretty = [] 
for i,nm in enumerate(names[::-1]):
    if values[i]!=0:
        pretty.append("%i %s" % (values[i],nm))
return " ".join(pretty)
1
Charles Doutriaux

Voici une réponse mise à jour basée sur l'implémentation de Jed Smith, qui transmet correctement les dates/heures naïves et sensibles à l'offset. Vous pouvez également donner un fuseau horaire par défaut. Python 3.5+.

import datetime

def pretty_date(time=None, default_timezone=datetime.timezone.utc):
    """
    Get a datetime object or a int() Epoch timestamp and return a
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
    'just now', etc
    """

    # Assumes all timezone naive dates are UTC
    if time.tzinfo is None or time.tzinfo.utcoffset(time) is None:
        if default_timezone:
            time = time.replace(tzinfo=default_timezone)

    now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)

    if type(time) is int:
        diff = now - datetime.fromtimestamp(time)
    Elif isinstance(time, datetime.datetime):
        diff = now - time
    Elif not time:
        diff = now - now
    second_diff = diff.seconds
    day_diff = diff.days

    if day_diff < 0:
        return ''

    if day_diff == 0:
        if second_diff < 10:
            return "just now"
        if second_diff < 60:
            return str(second_diff) + " seconds ago"
        if second_diff < 120:
            return "a minute ago"
        if second_diff < 3600:
            return str(second_diff / 60) + " minutes ago"
        if second_diff < 7200:
            return "an hour ago"
        if second_diff < 86400:
            return str(second_diff / 3600) + " hours ago"
    if day_diff == 1:
        return "Yesterday"
    if day_diff < 7:
        return str(day_diff) + " days ago"
    if day_diff < 31:
        return str(day_diff / 7) + " weeks ago"
    if day_diff < 365:
        return str(day_diff / 30) + " months ago"
    return str(day_diff / 365) + " years ago"
0
Mikko Ohtamaa

Vous pouvez télécharger et installer le lien ci-dessous. Cela devrait être plus utile pour vous. Il fournit un message convivial de seconde en année.

C'est bien testé.

https://github.com/nareshchaudhary37/timestamp_content

Les étapes ci-dessous à installer dans votre env virtuel.

git clone https://github.com/nareshchaudhary37/timestamp_content
cd timestamp-content
python setup.py
0
Naresh Chaudhary

Ceci est le message de @sunil

>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> then = datetime(2003, 9, 17, 20, 54, 47, 282310)
>>> relativedelta(then, datetime.now())
relativedelta(years=-11, months=-3, days=-9, hours=-18, minutes=-17, seconds=-8, microseconds=+912664)
0
Steven Almeroth