| 1 | """Date/Time Helpers""" |
|---|
| 2 | # Last synced with Rails copy at Revision 4674 on Aug 19th, 2006. |
|---|
| 3 | # Note that the select_ tags are purposely not ported as they're very totally useless |
|---|
| 4 | # and inefficient beyond comprehension. |
|---|
| 5 | |
|---|
| 6 | from datetime import datetime |
|---|
| 7 | import time |
|---|
| 8 | |
|---|
| 9 | DEFAULT_PREFIX = 'date' |
|---|
| 10 | |
|---|
| 11 | def distance_of_time_in_words(from_time, to_time=0, include_seconds=False): |
|---|
| 12 | """ |
|---|
| 13 | Reports the approximate distance in time between two datetime objects or integers. |
|---|
| 14 | |
|---|
| 15 | For example, if the distance is 47 minutes, it'll return |
|---|
| 16 | "about 1 hour". See the source for the complete wording list. |
|---|
| 17 | |
|---|
| 18 | Integers are interpreted as seconds from now. So, |
|---|
| 19 | ``distance_of_time_in_words(50)`` returns "less than a minute". |
|---|
| 20 | |
|---|
| 21 | Set ``include_seconds`` to True if you want more detailed approximations if distance < 1 minute |
|---|
| 22 | """ |
|---|
| 23 | if isinstance(from_time, int): |
|---|
| 24 | from_time = time.time()+from_time |
|---|
| 25 | else: |
|---|
| 26 | from_time = time.mktime(from_time.timetuple()) |
|---|
| 27 | if isinstance(to_time, int): |
|---|
| 28 | to_time = time.time()+to_time |
|---|
| 29 | else: |
|---|
| 30 | to_time = time.mktime(to_time.timetuple()) |
|---|
| 31 | |
|---|
| 32 | distance_in_minutes = int(round(abs(to_time-from_time)/60)) |
|---|
| 33 | distance_in_seconds = int(round(abs(to_time-from_time))) |
|---|
| 34 | |
|---|
| 35 | if distance_in_minutes <= 1: |
|---|
| 36 | if include_seconds: |
|---|
| 37 | for remainder in [5, 10, 20]: |
|---|
| 38 | if distance_in_seconds < remainder: |
|---|
| 39 | return "less than %s seconds" % remainder |
|---|
| 40 | if distance_in_seconds < 40: |
|---|
| 41 | return "half a minute" |
|---|
| 42 | elif distance_in_seconds < 60: |
|---|
| 43 | return "less than a minute" |
|---|
| 44 | else: |
|---|
| 45 | return "1 minute" |
|---|
| 46 | else: |
|---|
| 47 | if distance_in_minutes == 0: |
|---|
| 48 | return "less than a minute" |
|---|
| 49 | else: |
|---|
| 50 | return "1 minute" |
|---|
| 51 | elif distance_in_minutes <= 45: |
|---|
| 52 | return "%s minutes" % distance_in_minutes |
|---|
| 53 | elif distance_in_minutes <= 90: |
|---|
| 54 | return "about 1 hour" |
|---|
| 55 | elif distance_in_minutes <= 1440: |
|---|
| 56 | return "about %d hours" % (round(distance_in_minutes / 60.0)) |
|---|
| 57 | elif distance_in_minutes <= 2880: |
|---|
| 58 | return "1 day" |
|---|
| 59 | elif distance_in_minutes <= 43220: |
|---|
| 60 | return "%d days" % (round(distance_in_minutes / 1440)) |
|---|
| 61 | elif distance_in_minutes <= 86400: |
|---|
| 62 | return "about 1 month" |
|---|
| 63 | elif distance_in_minutes <= 525960: |
|---|
| 64 | return "%d months" % (round(distance_in_minutes / 43200)) |
|---|
| 65 | elif distance_in_minutes <= 1051920: |
|---|
| 66 | return "about 1 year" |
|---|
| 67 | else: |
|---|
| 68 | return "over %d years" % (round(distance_in_minutes / 525600)) |
|---|
| 69 | |
|---|
| 70 | def time_ago_in_words(from_time, include_seconds=False): |
|---|
| 71 | """ |
|---|
| 72 | Like distance_of_time_in_words, but where ``to_time`` is fixed to ``datetime.now()``. |
|---|
| 73 | """ |
|---|
| 74 | return distance_of_time_in_words(from_time, datetime.now(), include_seconds) |
|---|
| 75 | |
|---|
| 76 | __all__ = ['distance_of_time_in_words', 'time_ago_in_words'] |
|---|