Python中用户友好的时间格式?


71

Python:我需要以“ 1天前”,“两个小时前”的格式显示文件修改时间。

准备好了吗?应该是英文。

Answers:


122

该代码最初发布在博客文章“ Python Pretty Date function”上(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"

3
根据我的确切需求量身定制。谢谢!
flybywire

2
该链接不再存在,并且被禁止。在这里需要一个永久链接,或者需要将内容移动到这篇文章中。
克里斯·戴尔

@Chris:感谢大家的注意,它仍然在Google Cache中,所以我抓到了它。
杰德·史密斯,

2
这对我不起作用。我收到此错误:赋值之前引用了局部变量'diff'为什么?
温伯特


30

如果您恰巧使用Django,那么1.4版中的新功能就是naturaltime模板过滤器。

要使用它,请先在settings.py中添加'django.contrib.humanize'INSTALLED_APPS的设置,然后{% load humanize %}在其中使用过滤器的模板中添加它。

然后,在模板中,如果您有一个datetime变量my_date,则可以使用来打印它与当前的距离{{ my_date|naturaltime }},它将呈现为4 minutes ago

Django 1.4中的其他新功能。

naturaltimedjango.contrib.humanize集中的文档和其他过滤器。


确实需要将其添加到INSTALLED_APPS吗?尽管我在python而不是模板中使用了过滤器,但对我来说却没有效果
2014年

我可以使用此内部视图
Dheeraj M Pai

14

在寻找具有处理将来日期的附加要求的相同东西时,我发现了这一点: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'

1
不幸的是,py-pretty似乎不允许使用i18n。
Karl Bartel 2012年


5

杰德·史密斯(Jed Smith)链接到的答案很好,我用了大约一年的时间,但我认为可以通过以下几种方法加以改进:

  • 能够根据前一个单位定义每个时间单位,而不是在整个代码中散布诸如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。)

我的功能的一个缺点是,它在“ ##单位”模式之外不执行任何操作:“昨天”,“现在”等都可以使用。再说一次,原始的发布者并不需要这些奇特的术语,所以我更喜欢我的函数,因为它的简洁性和数值常数的可读性。:)


不错的解决方案。保持简单允许更多的重用。例如,可以将后缀用于相对时间value + " ago"或持续时间value + " left"
swdev

4

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 часа назад

3
请注意,humanize它不支持时区感知日期时间。您必须使用将它们转换为天真的(在当地时区)dt.astimezone().replace(tzinfo=None)
卢卡斯·韦尔克梅斯特

4

包提供了这一点。调用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

2

在tzinfo中使用datetime对象:

def time_elapsed(etime):
    # need to add tzinfo to datetime.utcnow
    now = 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

我已经在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

这是@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

这是基于杰德·史密斯(Jed Smith)的实现方式的最新答案,该实现方式可以正确处理天真偏移时间和可感知偏移的日期时间。您还可以提供默认时区。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

很长时间以来,我一直在将这些代码从编程语言拖放到编程语言中,我不记得我最初从哪里得到的代码。它在PHP,Java和TypeScript中非常有用,现在是使用Python的时候了。

它处理过去和将来的日期,以及极端情况。

def unix_time() -> int:
    return int(time.time())


def pretty_time(t: int, absolute=False) -> str:
    if not type(t) is int:
        return "N/A"
    if t == 0:
        return "Never"

    now = unix_time()
    if t == now:
        return "Now"

    periods = ["second", "minute", "hour", "day", "week", "month", "year", "decade"]
    lengths = [60, 60, 24, 7, 4.35, 12, 10]

    diff = now - t

    if absolute:
        suffix = ""
    else:
        if diff >= 0:
            suffix = "ago"
        else:
            diff *= -1
            suffix = "remaining"

    i = 0
    while diff >= lengths[i] and i < len(lengths) - 1:
        diff /= lengths[i]
        i += 1

    diff = round(diff)
    if diff > 1:
        periods[i] += "s"

    return "{0} {1} {2}".format(diff, periods[i], suffix)

0
DAY_INCREMENTS = [
    [365, "year"],
    [30, "month"],
    [7, "week"],
    [1, "day"],
]

SECOND_INCREMENTS = [
    [3600, "hour"],
    [60, "minute"],
    [1, "second"],
]


def time_ago(dt):
    diff = datetime.now() - dt  # use timezone.now() or equivalent if `dt` is timezone aware
    if diff.days < 0:
        return "in the future?!?"
    for increment, label in DAY_INCREMENTS:
        if diff.days >= increment:
            increment_diff = int(diff.days / increment)
            return str(increment_diff) + " " + label + plural(increment_diff) + " ago"
    for increment, label in SECOND_INCREMENTS:
        if diff.seconds >= increment:
            increment_diff = int(diff.seconds / increment)
            return str(increment_diff) + " " + label + plural(increment_diff) + " ago"
    return "just now"


def plural(num):
    if num != 1:
        return "s"
    return ""
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.