【问题标题】:How can I produce a human readable difference when subtracting two UNIX timestamps using Python?使用 Python 减去两个 UNIX 时间戳时,如何产生人类可读的差异?
【发布时间】:2011-09-28 07:04:46
【问题描述】:

这个问题类似于to this question about subtracting dates with Python,但不完全相同。我不是在处理字符串,我必须找出两个纪元时间戳之间的差异,并以人类可读的格式产生差异。

例如:

32 Seconds
17 Minutes
22.3 Hours
1.25 Days
3.5 Weeks
2 Months
4.25 Years

或者,我想这样表达差异:

4 years, 6 months, 3 weeks, 4 days, 6 hours 21 minutes and 15 seconds

我认为我不能使用strptime,因为我正在处理两个纪元日期的差异。我可以写一些东西来做到这一点,但我很确定已经写了一些我可以使用的东西。

什么模块合适?我只是在time 中遗漏了什么吗?我的 Python 之旅才刚刚开始,如果这确实是重复的,那是因为我没有弄清楚要搜索什么。

附录

为了准确,我最关心的是当年的日历。

【问题讨论】:

  • 通过 UNIX 纪元日期,您是指自 X 以来通常的秒数?
  • 您希望月/年计算的准确度如何?由于每月和每年的天数可能会有所不同,因此情况可能会变得复杂。

标签: python time formatting


【解决方案1】:

您可以使用精彩的dateutil 模块及其relativedelta 类:

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

这不算数周,但这应该不会太难添加。

【讨论】:

  • 谢谢!这完美地工作。将周表示为天不是问题。
  • 有没有一种简单的方法可以只显示非 0 单位并自动复数?比如“1年2分1秒”
  • @PierredeLESPINAY 您可以将rd.second 三元化作为最终的printf 参数,并将seconds 中的s 替换为%s,它可以是's' 或'',具体取决于复数。
【解决方案2】:

我今天早些时候遇到了同样的问题,我在标准库中找不到任何可以使用的东西,所以我写了这个:

humanize_time.py

    #!/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 __name__ == "__main__":
        import doctest
        doctest.testmod()

您可以使用dateutil.relativedelta() 计算准确的时间增量,并使用此脚本将其人性化。

【讨论】:

  • 我很好奇您从哪里获得数周、月和年的秒数。
  • 我把它写成[1, 60, 60*60, 24*60*60, 7*24*60*60 ... ] 本来是为了更明显,但我觉得它看起来很长而且很烦人,所以我把它改成了这个。当前代码只是乘法的结果。
  • 我们如何处理amount 有小数位的值?例如:humanize_time(60.5, 'seconds') 应该给1 minute, 0.5 seconds
【解决方案3】:

与@Schnouki 的解决方案相比,使用单行列表理解略有改进。如果是多个实体(如小时),也会显示复数

导入相对增量

>>> from dateutil.relativedelta import relativedelta

一个 lambda 函数

>>> attrs = ['years', 'months', 'days', 'hours', 'minutes', 'seconds']
>>> human_readable = lambda delta: ['%d %s' % (getattr(delta, attr), attr if getattr(delta, attr) > 1 else attr[:-1]) 
...     for attr in attrs if getattr(delta, attr)]

示例用法:

>>> human_readable(relativedelta(minutes=125))
['2 hours', '5 minutes']
>>> human_readable(relativedelta(hours=(24 * 365) + 1))
['365 days', '1 hour']

【讨论】:

  • 必须在很短的时间内将“微秒”添加到 attrs 以避免错误,但方法很好。
  • 当您可以直接存储纪元时间时,这也有助于避免从时间戳创建日期时间对象。那么你可以说relativedelta(seconds=(t1 - t0))
【解决方案4】:
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

使用示例:

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

优势(您不需要第三方!)。

从“Liudmil Mitev”算法改进而来。 (谢谢!)

【讨论】:

  • 我喜欢不需要安装新模块。
  • 直到一周还不错,但是从它开始计算月份和年份的那一刻起,它变得非常不准确,根据间隔逻辑,您的月份将是 28 天,而年份将是 336 天.
【解决方案5】:

老问题,但我个人最喜欢这种方法:

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"

如果你想用“and”分隔秒部分:

"%s and %s" % tuple(human_time(seconds=3721).rsplit(", ", 1))
# -> "1 hour, 2 minutes and 1 second"

【讨论】:

    【解决方案6】:

    这是一个较短的间隔,单位为秒和一天内 (t

    t = 45678
    print('%d hours, %d minutes, %d seconds' % (t//3600, t%3600//60, t%60))
    

    可能会进一步扩展(t//86400,...)。

    【讨论】:

      【解决方案7】:

      查看人性化包

      https://github.com/jmoiron/humanize

      import datetime
      
      humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=1))
      
      'a second ago'
      
      humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=3600))
      
      'an hour ago'
      

      【讨论】:

      • 这应该是2020年的答案
      【解决方案8】:

      一个非常古老的问题,但我发现这个解决方案在 Python3 中似乎非常简单:

      print(datetime.timedelta(seconds=3600))
      # output: 1:00:00
      print(datetime.timedelta(hours=360.1245))
      # output: 15 days, 0:07:28.200000
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-06-19
        • 1970-01-01
        • 2013-03-30
        • 2014-05-04
        • 1970-01-01
        • 1970-01-01
        • 2011-10-28
        相关资源
        最近更新 更多