Удобный формат времени в Python? - PullRequest
57 голосов
/ 11 октября 2009

Python: мне нужно показать время изменения файла в формате «1 день назад», «два часа назад».

Есть ли что-нибудь готовое для этого? Должно быть на английском.

Ответы [ 12 ]

108 голосов
/ 11 октября 2009

Код был первоначально опубликован в сообщении блога «Функция Python Pretty Date» (http://evaisse.com/post/93417709/python-pretty-date-function)

Воспроизводится здесь, поскольку учетная запись блога была приостановлена, а страница больше не доступна.

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"
29 голосов
/ 02 апреля 2012

Если вы используете Django , то в версии 1.4 появился новый шаблонный фильтр naturaltime.

Чтобы использовать его, сначала добавьте 'django.contrib.humanize' к настройке INSTALLED_APPS в settings.py и {% load humanize %} в шаблон, в котором вы используете фильтр.

Затем в вашем шаблоне, если у вас есть переменная datetime my_date, вы можете распечатать расстояние до настоящего времени, используя {{ my_date|naturaltime }}, который будет отображаться как 4 minutes ago.

Другие новые вещи в Django 1.4.

Документация для naturaltime и других фильтров в наборе django.contrib.humanize.

14 голосов
/ 10 апреля 2010

Ища то же самое с дополнительным требованием, чтобы оно обрабатывало будущие даты, я нашел это: http://pypi.python.org/pypi/py-pretty/1

Пример кода (с сайта):

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'
6 голосов
/ 28 сентября 2016

Вы также можете сделать это с стрелка пакет

со страницы github :

>>> import arrow
>>> utc = arrow.utcnow()
>>> utc = utc.replace(hours=-1)
>>> local.humanize()
'an hour ago'
5 голосов
/ 24 августа 2013

Ответ, на который ссылался Джед Смит, хорош, и я использовал его около года, но я думаю, что его можно улучшить несколькими способами:

  • Приятно иметь возможность определять каждую единицу времени в терминах предыдущей единицы, вместо того, чтобы разбрасывать «волшебные» константы, такие как 3600, 86400 и т. Д., Разбросанные по всему коду.
  • После долгого использования я обнаружил, что не хочу так охотно переходить к следующему устройству. Пример: и 7 дней, и 13 дней будут отображаться как «1 неделя»; Вместо этого я предпочел бы увидеть «7 дней» или «13 дней».

Вот что я придумал:

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

Обратите внимание, что каждый кортеж в intervals легко интерпретируется и проверяется: a 'minute' равен 60 секундам; 'hour' составляет 60 минут; и т. д. Единственная выдумка - установить weeks_per_month на среднее значение; учитывая заявление, это должно быть хорошо. (И обратите внимание, что сразу видно, что последние три константы умножаются на 365,242, количество дней в году.)

Одним из недостатков моей функции является то, что она ничего не делает вне шаблона "## units": "Вчера", "только сейчас" и т. Д. Опять же, оригинальный постер не просил этих причудливых терминов, поэтому я предпочитаю свою функцию за ее краткость и удобочитаемость ее числовых констант. :)

4 голосов
/ 29 июля 2012

Пакет назад обеспечивает это. Вызовите human объекта datetime, чтобы получить понятное для человека описание разницы.

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
3 голосов
/ 28 декабря 2014

Существует humanize пакет :

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

Поддерживается локализация , интернационализация :

>>> _ = humanize.i18n.activate('ru_RU')
>>> print humanize.naturaltime(datetime.now() - timedelta(days=1))
день назад
>>> print humanize.naturaltime(datetime.now() - timedelta(hours=2))
2 часа назад
2 голосов
/ 25 ноября 2015

Использование объектов datetime с 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 голос
/ 27 апреля 2010

Я написал подробный пост в блоге для решения на http://sunilarora.org/17329071 Я также публикую здесь быстрый фрагмент.

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
0 голосов
/ 24 января 2019

Вот обновленный ответ, основанный на реализации Джеда Смита, который должным образом передает даты и время смещения как наивного, так и с учетом смещения. Вы также можете указать часовые пояса по умолчанию. 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"
...