User-friendly time format in Python?

后端 未结 14 1459
[愿得一人]
[愿得一人] 2020-12-12 10:06

Python: I need to show file modification times in the \"1 day ago\", \"two hours ago\", format.

Is there something ready to do that? It should be in English.

相关标签:
14条回答
  • 2020-12-12 10:51

    This is the gist of @sunil 's post

    >>> 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 讨论(0)
  • 2020-12-12 10:54
    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 ""
    
    0 讨论(0)
  • 2020-12-12 10:55

    I have written a detailed blog post for the solution on http://sunilarora.org/17329071 I am posting a quick snippet here as well.

    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 讨论(0)
  • 2020-12-12 10:57

    The answer Jed Smith linked to is good, and I used it for a year or so, but I think it could be improved in a few ways:

    • It's nice to be able to define each time unit in terms of the preceding unit, instead of having "magic" constants like 3600, 86400, etc. sprinkled throughout the code.
    • After much use, I find I don't want to go to the next unit quite so eagerly. Example: both 7 days and 13 days will show as "1 week"; I'd rather see "7 days" or "13 days" instead.

    Here's what I came up with:

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

    Notice how every tuple in intervals is easy to interpret and check: a 'minute' is 60 seconds; an 'hour' is 60 minutes; etc. The only fudge is setting weeks_per_month to its average value; given the application, that should be fine. (And note that it's clear at a glance that the last three constants multiply out to 365.242, the number of days per year.)

    One downside to my function is that it doesn't do anything outside the "## units" pattern: "Yesterday", "just now", etc. are right out. Then again, the original poster didn't ask for these fancy terms, so I prefer my function for its succinctness and the readability of its numerical constants. :)

    0 讨论(0)
  • 2020-12-12 10:58

    If you happen to be using Django, then new in version 1.4 is the naturaltime template filter.

    To use it, first add 'django.contrib.humanize' to your INSTALLED_APPS setting in settings.py, and {% load humanize %} into the template you're using the filter in.

    Then, in your template, if you have a datetime variable my_date, you can print its distance from the present by using {{ my_date|naturaltime }}, which will be rendered as something like 4 minutes ago.

    Other new things in Django 1.4.

    Documentation for naturaltime and other filters in the django.contrib.humanize set.

    0 讨论(0)
  • 2020-12-12 11:02

    In looking for the same thing with the additional requirement that it handle future dates, I found this: http://pypi.python.org/pypi/py-pretty/1

    Example code (from 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'
    
    0 讨论(0)
提交回复
热议问题